Node.js · API de try-on virtuel

API de try-on virtuel en Node.js

En une phrase

Définissez PHOTTA_API_KEY dans l'environnement, écrivez un léger helper fetch pour https://api.photta.app/api/v1 avec une clé Bearer, envoyez un POST à /api/v1/tryon/apparel, puis interrogez /api/v1/tryon/apparel/{id} toutes les 3 secondes jusqu'à ce que le statut soit completed.

  • Délai typique: 1.5–4 min
  • Crédits: 5 crédits en 2K · 6 crédits en 4K
  • Traitement: asynchrone, polling
  • Limites: 600 requêtes/min
  • Authentification: Clé Bearer

Chaque chiffre sur cette page provient de la documentation de l'API Photta · vérifié

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");
}

En un coup d'œil

En un coup d'œil

Endpoint
POST /api/v1/tryon/apparel
Authentification
Clé API Bearer
Traitement
Asynchrone — interrogez l'endpoint de statut par polling
Délai typique
1.5–4 min
Crédits
5 crédits en 2K · 6 crédits en 4K
Rapports d'aspect
3:4 · 1:1 · 4:3 · 9:16 · 16:9
Limites d'entrée
Jusqu'à 10 Mo par image ; JPEG, PNG, WebP
Idempotent
Non

Comment ça fonctionne

Cinq étapes, environ cinq minutes de l'inscription à la première image.

  1. Étape 1

    S'inscrire et générer une clé

    Créez un compte sur ai.photta.app. Ouvrez la section Développeurs du dashboard, cliquez sur Générer une clé d'API et copiez la clé live. Elle commence par `photta_live_` suivi de 32 caractères hexadécimaux.

  2. Étape 2

    Stocker la clé dans l'environnement, pas dans le code source

    Ajoutez `PHOTTA_API_KEY=...` dans `.env` (ou dans le gestionnaire de secrets de votre plateforme) et chargez-la via `process.env`. Ne committez jamais la clé ; ne l'importez jamais dans un fichier annoté avec `'use client'` — le bundler l'exposerait côté navigateur.

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

    Écrire un helper fetch minimal

    Un wrapper de 20 lignes autour de `fetch()` gère l'URL de base, l'en-tête Authorization, le corps JSON et la normalisation des erreurs. C'est la seule abstraction dont vous avez besoin en attendant la sortie d'un SDK officiel.

    // 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. Étape 4

    Soumettre un try-on et faire du polling

    Envoyez un POST vers `/api/v1/tryon/apparel` avec le corps d'exemple documenté — le code ci-dessous inclut chaque champ. L'API renvoie immédiatement un identifiant de génération. Interrogez `/api/v1/tryon/apparel/{id}` par polling toutes les 3 secondes jusqu'à ce que `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. Étape 5

    Persister le résultat

    Le payload final inclut `output_url` et `thumbnail_url`. Récupérez les octets une seule fois via fetch et stockez-les dans votre propre stockage objet — les URL sont stables mais votre produit ne devrait pas dépendre du CDN de Photta pour le rendu.

Erreurs

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;
}

Limites

  • 600 requêtes par minute par clé API (Fenêtre fixe ; la limite est identique sur tous les forfaits)
  • Une 429 renvoie Retry-After: 30 secondes
  • Jusqu'à 10 Mo par image
  • Formats: JPEG, PNG, WebP
  • Lots simultanés par forfait: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
  • Polling uniquement — l'API n'envoie aucun webhook.

Pourquoi un helper surpasse une bibliothèque cliente — pour l'instant

  • Node 18+ intègre fetch en natif — aucune dépendance d'exécution
  • Le même helper fonctionne dans les route handlers Next.js, les server actions et n'importe quel worker
  • Conservez la clé d'API dans l'environnement ; n'importez jamais le helper depuis un module 'use client' sous peine de fuite via le bundler
  • Fonctionne avec n'importe quelle file d'attente ou cron : BullMQ, Inngest, Trigger.dev, Vercel Cron, Cloudflare Queues

Ce qu'il ne fait pas

  • Aucun package officiel @photta/node pour l'instant — REST est l'unique voie aujourd'hui
  • Le polling est le seul signal d'achèvement ; l'API n'envoie aucun webhook
  • Clé Bearer uniquement ; il n'y a pas de flux client-credentials OAuth

Questions fréquentes des développeurs

Existe-t-il un SDK officiel Photta pour Node.js ?

Pas encore. La méthode documentée repose sur du pur REST via le fetch natif de Node — le helper sur cette page représente l'intégralité de la surface d'intégration, et fonctionne de manière identique pour chaque fonctionnalité.

Quelles versions de Node.js sont prises en charge ?

Toutes celles supportant `fetch` nativement — c'est-à-dire Node 18 et versions ultérieures. Sur Node 16, utilisez un polyfill avec `node-fetch` ou mettez à niveau votre version. Le reste du code est du pur ECMAScript 2020 (async/await, template literals, imports dynamiques), sans autre exigence d'exécution.

Comment s'authentifier depuis Node ?

Envoyez `Authorization: Bearer photta_live_xxx` à chaque requête. Placez la clé dans une variable d'environnement et chargez-la via `process.env` ; ne l'importez jamais depuis un fichier exécuté dans le navigateur.

Comment gérer la fenêtre de génération ?

L'endpoint de génération est entièrement asynchrone. Le POST renvoie immédiatement un identifiant de génération avec `status: 'processing'`. Interrogez `GET /api/v1/tryon/apparel/{id}` par polling toutes les 3 à 5 secondes jusqu'à ce que `status` passe à `completed` ou `failed`, et limitez le nombre de tentatives afin qu'un job bloqué ne suspende pas indéfiniment une requête.

Puis-je appeler l'API au sein de Next.js ?

Oui — depuis un route handler (`app/**/route.ts`), une server action (`'use server'`) ou un middleware. Conservez la clé d'API côté serveur ; n'importez jamais le helper depuis un fichier `'use client'`. Pour les jobs longs qui dépassent le timeout des fonctions Vercel, déléguez le polling à une file d'attente (Inngest, Trigger.dev, BullMQ) et renvoyez l'identifiant de génération au navigateur pour qu'il interroge votre propre endpoint d'état.

Comment l'API signale-t-elle les erreurs ?

Les réponses autres que 2xx contiennent un corps JSON avec un objet `error` : `type`, `code`, `message` et un identifiant de requête pour le support. Cas fréquents : 400 invalid_request_error avec `param` pointant vers le champ invalide ; 402 insufficient_credits avec les soldes `required` et `available` ; 429 rate_limit_exceeded avec `retry_after` en secondes et un en-tête Retry-After correspondant. Les erreurs 5xx peuvent être réessayées sans risque avec un backoff exponentiel.

Pages associées

Chaque chiffre sur cette page provient de la documentation de l'API Photta · vérifié

Testez l'API en cinq minutes

Générez une clé d'API Photta, collez le snippet cURL ci-dessus, et vous obtiendrez votre première photo portée sur modèle avant même que le café ait fini de couler.

  • 1.5–4 min
  • 5 crédits en 2K · 6 crédits en 4K
  • asynchrone, polling
  • 600 requêtes/min
  • Clé Bearer