Node.js · API för smyckes-try-on

API för smyckes-try-on i Node.js

I en mening

Sätt PHOTTA_API_KEY i env, skriv en liten fetch-hjälpreda för https://api.photta.app/api/v1 med en Bearer-nyckel, POST:a till /api/v1/tryon/jewelry, polla sedan /api/v1/tryon/jewelry/{id} var 3:e sekund tills status är completed.

  • Typisk tid: 1.5–4 min
  • Credits: 5 credits vid 2K · 6 credits vid 4K
  • Bearbetning: asynkron, polling
  • Gränser: 600 requests/min
  • Autentisering: Bearer-nyckel

Varje siffra på den här sidan kommer från Photter API-dokumentationen · kontrollerad

POST/api/v1/tryon/jewelry
const res = await fetch("https://api.photta.app/api/v1/tryon/jewelry", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "jewelry_image": "https://cdn.yourstore.com/products/gold-necklace.jpg",
    "jewelry_type": "necklace",
    "mannequin_id": "mnk_j9k1l2",
    "background": "soft window light on dark velvet",
    "resolution": "2K",
    "aspect_ratio": "3:4",
    "user_instruction": "Make the pendant 10% smaller."
  }),
});
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/jewelry/${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");
}

I korthet

I korthet

Endpoint
POST /api/v1/tryon/jewelry
Autentisering
Bearer API-nyckel
Bearbetning
Asynkron — polla status-endpointen
Typisk tid
1.5–4 min
Credits
5 credits vid 2K · 6 credits vid 4K
Bildförhållanden
3:4 · 1:1 · 4:3 · 9:16 · 16:9
Indatagränser
Upp till 10 MB per bild; JPEG, PNG, WebP
Idempotent
Nej

Hur det fungerar

Fem steg, cirka fem minuter från registrering till första bild.

  1. Steg 1

    Registrera dig och generera en nyckel

    Skapa ett konto på ai.photta.app. Öppna sektionen Developers i dashboarden, klicka på Generate API key och ta live-nyckeln. Den börjar med `photta_live_` följt av 32 hex-tecken.

  2. Steg 2

    Lagra nyckeln i env, inte i källkoden

    Lägg till `PHOTTA_API_KEY=...` i `.env` (eller din plattforms hemlighetslager) och läs in den via `process.env`. Checka aldrig in nyckeln; importera den aldrig till en fil markerad med `'use client'` — bundlaren drar då in den i webbläsaren.

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

    Skriv en enkel fetch-helper

    En wrapper på 20 rader runt `fetch()` hanterar bas-URL, Authorization-header, JSON-body och felnormalisering. Detta är den enda abstraktion du behöver tills ett officiellt SDK släpps.

    // 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. Steg 4

    Skicka en try-on och polla

    Gör en POST till `/api/v1/tryon/jewelry` med den dokumenterade exempel-bodyn — koden nedan innehåller alla fält. API:et returnerar ett generation-ID direkt. Polla `/api/v1/tryon/jewelry/{id}` var 3:e sekund tills `status === 'completed'`.

    const res = await fetch("https://api.photta.app/api/v1/tryon/jewelry", {
      method: "POST",
      headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        "jewelry_image": "https://cdn.yourstore.com/products/gold-necklace.jpg",
        "jewelry_type": "necklace",
        "mannequin_id": "mnk_j9k1l2",
        "background": "soft window light on dark velvet",
        "resolution": "2K",
        "aspect_ratio": "3:4",
        "user_instruction": "Make the pendant 10% smaller."
      }),
    });
    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/jewelry/${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. Steg 5

    Spara resultatet persistent

    Den slutförda payloaden innehåller `output_url` och `thumbnail_url`. Hämta byten en gång och spara dem i din egen objektlagring — URL:erna är stabila, men din produkt bör inte vara beroende av Phottas CDN för rendering.

Fel

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/jewelry", { method: "POST", body: JSON.stringify({
    "jewelry_image": "https://cdn.yourstore.com/products/gold-necklace.jpg",
    "jewelry_type": "necklace",
    "mannequin_id": "mnk_j9k1l2",
    "background": "soft window light on dark velvet",
    "resolution": "2K",
    "aspect_ratio": "3:4",
    "user_instruction": "Make the pendant 10% smaller."
  }) });
} 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;
}

Gränser

  • 600 anrop per minut per API-nyckel (Fast fönster; gränsen är densamma på alla planer)
  • En 429 innehåller Retry-After: 30 sekunder
  • Upp till 10 MB per bild
  • Format: JPEG, PNG, WebP
  • Samtidiga batcher per plan: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
  • Endast polling — API:et skickar inga webhooks.

Varför en helper slår ett klientbibliotek — tills vidare

  • Node 18+ har inbyggd fetch — inga runtime-beroenden
  • Samma helper fungerar i Next.js route handlers, server actions och alla workers
  • Håll API-nyckeln i env; importera aldrig helpern från en 'use client'-modul, annars läcker bundlern den
  • Fungerar med alla köer och cron-lösningar: BullMQ, Inngest, Trigger.dev, Vercel Cron, Cloudflare Queues

Vad det inte gör

  • Inget officiellt @photta/node-paket än — REST är den enda vägen idag
  • Polling är den enda signalen på slutförande; API:et skickar inga webhooks
  • Endast Bearer-nyckel; det finns inget OAuth client-credentials-flöde

Vanliga utvecklarfrågor

Finns det ett officiellt Photta Node.js-SDK?

Inte än. Den dokumenterade vägen är rå REST via Nodes inbyggda fetch — helpern på den här sidan utgör hela integrationsytan, och den fungerar likadant för varje capability.

Vilka Node.js-versioner stöds?

Alla som stöder inbyggd `fetch` — vilket innebär Node 18 och senare. På Node 16 får du antingen köra polyfill med `node-fetch` eller uppgradera. Resten av koden är ren ECMAScript 2020 (async/await, template literals, dynamiska importer) så det finns inga andra runtime-krav.

Hur autentiserar jag från Node?

Skicka `Authorization: Bearer photta_live_xxx` i varje anrop. Lägg nyckeln i en miljövariabel och läs in den via `process.env`; importera den aldrig från en fil som körs i webbläsaren.

Hur hanterar jag generationsfönstret?

Generations-endpointen är helt asynkron. POST returnerar omedelbart ett generation-ID med `status: 'processing'`. Polla `GET /api/v1/tryon/jewelry/{id}` var 3–5:e sekund tills `status` slår om till `completed` eller `failed`, och sätt ett tak för antalet försök så att ett fastnat jobb inte låser en förfrågan.

Kan jag anropa API:et inuti Next.js?

Ja — från en route handler (`app/**/route.ts`), en server action (`'use server'`) eller middleware. Håll API-nyckeln på servern; importera aldrig helpern från en `'use client'`-fil. För långkörande jobb som överskrider Vercels funktionstimeout, lägg ut pollingen på en kö (Inngest, Trigger.dev, BullMQ) och returnera generation-ID:t till webbläsaren så att den kan polla din egen status-endpoint.

Hur signalerar API:et fel?

Svar som inte är 2xx innehåller en JSON-body med ett `error`-objekt: `type`, `code`, `message` och ett request-ID för support. Vanliga fall: 400 invalid_request_error med `param` som pekar ut det felaktiga fältet; 402 insufficient_credits med antal för `required` och `available`; 429 rate_limit_exceeded med `retry_after` i sekunder och en matchande Retry-After-header. 5xx-fel är säkra att försöka igen med exponentiell backoff.

Testa API:et på fem minuter

Generera en Photta API-nyckel, klistra in cURL-kodfragmentet ovan, och du har din första bild på en modell innan kaffet har bryggts klart.

  • 1.5–4 min
  • 5 credits vid 2K · 6 credits vid 4K
  • asynkron, polling
  • 600 requests/min
  • Bearer-nyckel