Node.js · API di Virtual try-on

API di Virtual try-on in Node.js

In sintesi

Imposta PHOTTA_API_KEY nell'ambiente, scrivi un piccolo helper fetch per https://api.photta.app/api/v1 con chiave Bearer, invia una POST a /api/v1/tryon/apparel, quindi esegui il polling su /api/v1/tryon/apparel/{id} ogni 3 secondi fino a quando lo stato non è completed.

  • Tempo tipico: 1.5–4 min
  • Credits: 5 credits a 2K · 6 credits a 4K
  • Elaborazione: asincrono, polling
  • Limiti: 600 richieste/min
  • Autenticazione: Chiave Bearer

Ogni dato in questa pagina proviene da la documentazione dell'API di Photta · verificato

POST/api/v1/tryon/apparel
const res = await fetch("https://api.photta.app/api/v1/tryon/apparel", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "product_type": "top",
    "product_images": [
      "https://cdn.yourstore.com/products/shirt-front.jpg"
    ],
    "product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
    "product_category": "t_shirt",
    "mannequin_id": "mnk_f3a9b1",
    "pose_id": "pose_c2d4e5",
    "background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
    "lock_background": true,
    "resolution": "2K",
    "aspect_ratio": "3:4",
    "body_size": "M",
    "expression": "neutral",
    "user_instruction": "Keep the collar pattern crisp and centered."
  }),
});
const { data } = await res.json(); // data.id, data.status

async function waitFor(id) {
  for (let attempt = 0; attempt < 120; attempt++) {
    const res = await fetch(`https://api.photta.app/api/v1/tryon/apparel/${id}`, { headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}` } });
    const { data } = await res.json();
    if (data.status === "completed") return data.output_url;
    if (data.status === "failed") throw new Error(data.error_message ?? "generation failed");
    await new Promise((r) => setTimeout(r, 3000));
  }
  throw new Error("timed out after 6 minutes");
}

Panoramica

Panoramica

Endpoint
POST /api/v1/tryon/apparel
Autenticazione
API key Bearer
Elaborazione
Asincrona — effettua il polling dell'endpoint di stato
Tempo tipico
1.5–4 min
Credits
5 credits a 2K · 6 credits a 4K
Rapporti d'aspetto
3:4 · 1:1 · 4:3 · 9:16 · 16:9
Limiti di input
Fino a 10 MB per immagine; JPEG, PNG, WebP
Idempotente
No

Come funziona

Cinque passaggi, circa cinque minuti dalla registrazione alla prima immagine.

  1. Passaggio 1

    Registrati e genera una chiave

    Crea un account su ai.photta.app. Apri la sezione Developers della dashboard, fai clic su Generate API key e copia la chiave live. Inizia con `photta_live_` seguito da 32 caratteri esadecimali.

  2. Passaggio 2

    Conserva la chiave nelle variabili d'ambiente, non nel codice sorgente

    Aggiungi `PHOTTA_API_KEY=...` al file `.env` (o al secret store della tua piattaforma) e caricala tramite `process.env`. Non committare mai la chiave; non importarla mai in un file contrassegnato con `'use client'` — il bundler la includerebbe nel browser.

    node --version   # 18 or later ships fetch
    echo "PHOTTA_API_KEY=photta_live_xxxxx" >> .env
  3. Passaggio 3

    Scrivi un piccolo helper per fetch

    Un wrapper di 20 righe attorno a `fetch()` gestisce l'URL di base, l'header Authorization, il body JSON e la normalizzazione degli errori. Questa è l'unica astrazione necessaria finché non verrà rilasciato un SDK ufficiale.

    // photta.js — one helper for every endpoint
    const BASE = "https://api.photta.app/api/v1";
    export async function photta(path, init = {}) {
      const res = await fetch(BASE + path, { ...init, headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json", ...(init.headers ?? {}) } });
      const body = await res.json();
      if (!res.ok) throw Object.assign(new Error(body.error?.message ?? res.statusText), { status: res.status, retryAfter: res.headers.get("Retry-After"), body });
      return body;
    }
  4. Passaggio 4

    Invia un try-on ed esegui il polling

    Invia una richiesta POST a `/api/v1/tryon/apparel` con il body di esempio documentato — il codice sottostante include ogni campo. L'API restituisce immediatamente un ID di generazione. Esegui il polling su `/api/v1/tryon/apparel/{id}` ogni 3 secondi finché `status === 'completed'`.

    const res = await fetch("https://api.photta.app/api/v1/tryon/apparel", {
      method: "POST",
      headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        "product_type": "top",
        "product_images": [
          "https://cdn.yourstore.com/products/shirt-front.jpg"
        ],
        "product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
        "product_category": "t_shirt",
        "mannequin_id": "mnk_f3a9b1",
        "pose_id": "pose_c2d4e5",
        "background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
        "lock_background": true,
        "resolution": "2K",
        "aspect_ratio": "3:4",
        "body_size": "M",
        "expression": "neutral",
        "user_instruction": "Keep the collar pattern crisp and centered."
      }),
    });
    const { data } = await res.json(); // data.id, data.status
    
    async function waitFor(id) {
      for (let attempt = 0; attempt < 120; attempt++) {
        const res = await fetch(`https://api.photta.app/api/v1/tryon/apparel/${id}`, { headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}` } });
        const { data } = await res.json();
        if (data.status === "completed") return data.output_url;
        if (data.status === "failed") throw new Error(data.error_message ?? "generation failed");
        await new Promise((r) => setTimeout(r, 3000));
      }
      throw new Error("timed out after 6 minutes");
    }
  5. Passaggio 5

    Salva il risultato

    Il payload completato include `output_url` e `thumbnail_url`. Recupera i byte una sola volta e archiviali nel tuo object storage — gli URL sono stabili, ma il tuo prodotto non dovrebbe dipendere dalla CDN di Photta per il rendering.

Errori

400invalid_request_error
The request is malformed or contains invalid parameters.
401authentication_error
The API key is missing, invalid, or expired.
402insufficient_credits_error
Your account does not have enough credits.
404not_found_error
The requested resource does not exist.
409conflict_error
The request conflicts with current state — e.g. your concurrent-batch cap is reached.
429rate_limit_error
Too many requests. Slow down and retry.Retry-After: 30
500api_error
An internal server error occurred. Retry later.
try {
  await photta("/tryon/apparel", { method: "POST", body: JSON.stringify({
    "product_type": "top",
    "product_images": [
      "https://cdn.yourstore.com/products/shirt-front.jpg"
    ],
    "product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
    "product_category": "t_shirt",
    "mannequin_id": "mnk_f3a9b1",
    "pose_id": "pose_c2d4e5",
    "background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
    "lock_background": true,
    "resolution": "2K",
    "aspect_ratio": "3:4",
    "body_size": "M",
    "expression": "neutral",
    "user_instruction": "Keep the collar pattern crisp and centered."
  }) });
} catch (err) {
  if (err.status === 402) console.error("insufficient_credits_error: top up before retrying");
  else if (err.status === 429) await new Promise((r) => setTimeout(r, Number(err.retryAfter ?? 30) * 1000));
  else throw err;
}

Limiti

  • 600 richieste al minuto per API key (Finestra fissa; il limite è lo stesso per ogni piano)
  • Un 429 restituisce Retry-After: 30 secondi
  • Fino a 10 MB per immagine
  • Formati: JPEG, PNG, WebP
  • Batch simultanei per piano: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
  • Solo polling — l'API non invia webhook.

Perché un helper batte una libreria client — per ora

  • Node 18+ include fetch nativo — nessuna dipendenza di runtime
  • Lo stesso helper funziona all'interno di route handler di Next.js, server action e qualsiasi worker
  • Mantieni la chiave API nelle variabili d'ambiente; non importare mai l'helper da un modulo 'use client' altrimenti il bundler la esporrà
  • Funziona con qualsiasi coda o cron: BullMQ, Inngest, Trigger.dev, Vercel Cron, Cloudflare Queues

Cosa non fa

  • Nessun pacchetto ufficiale @photta/node al momento — REST è l'unica via oggi
  • Il polling è l'unico segnale di completamento; l'API non invia webhook
  • Solo chiave Bearer; non è previsto un flusso OAuth client-credentials

Domande frequenti degli sviluppatori

Esiste un SDK Node.js ufficiale di Photta?

Non ancora. Il percorso documentato è REST puro tramite la fetch nativa di Node — l'helper in questa pagina rappresenta l'intera superficie di integrazione e funziona allo stesso modo per ogni capability.

Quali versioni di Node.js sono supportate?

Qualsiasi versione che supporti `fetch` nativo — ovvero Node 18 e successive. Su Node 16 puoi usare un polyfill con `node-fetch` oppure aggiornare la versione. Il resto del codice è plain ECMAScript 2020 (async/await, template literals, import dinamici), quindi nessun altro requisito di runtime.

Come si effettua l'autenticazione da Node?

Invia `Authorization: Bearer photta_live_xxx` su ogni richiesta. Inserisci la chiave in una variabile d'ambiente e caricala tramite `process.env`; non importarla mai da un file eseguito nel browser.

Come si gestisce la finestra di generazione?

L'endpoint di generazione è completamente asincrono. La POST restituisce immediatamente un ID di generazione con `status: 'processing'`. Esegui il polling con `GET /api/v1/tryon/apparel/{id}` ogni 3–5 secondi finché lo `status` non passa a `completed` o `failed`, e limita i tentativi in modo che un job bloccato non mantenga la richiesta in sospeso.

Posso chiamare l'API all'interno di Next.js?

Sì — da un route handler (`app/**/route.ts`), una server action (`'use server'`) o un middleware. Mantieni la chiave API sul server; non importare mai l'helper da un file `'use client'`. Per job di lunga durata che superano il timeout delle funzioni di Vercel, delega il polling a una coda (Inngest, Trigger.dev, BullMQ) e restituisci l'ID di generazione al browser affinché possa effettuare il polling sul tuo endpoint di stato.

Come segnala gli errori l'API?

Le risposte non-2xx contengono un body JSON con un oggetto `error`: `type`, `code`, `message` e un ID di richiesta per il supporto. Casi comuni: 400 invalid_request_error con `param` che punta al campo errato; 402 insufficient_credits con il conteggio di `required` e `available`; 429 rate_limit_exceeded con `retry_after` in secondi e un header Retry-After corrispondente. Gli errori 5xx possono essere ritentati in sicurezza con backoff esponenziale.

Pagine correlate

Ogni dato in questa pagina proviene da la documentazione dell'API di Photta · verificato

Prova l'API in 5 minuti

Genera una chiave API di Photta, incolla lo snippet cURL qui sopra e otterrai la tua prima immagine con modello prima ancora che il caffè sia pronto.

  • 1.5–4 min
  • 5 credits a 2K · 6 credits a 4K
  • asincrono, polling
  • 600 richieste/min
  • Chiave Bearer