Node.js · Try-on-API til smykker

Try-on-API til smykker i Node.js

I én sætning

Sæt PHOTTA_API_KEY i env, skriv en lille fetch-helper til https://api.photta.app/api/v1 med en Bearer-nøgle, POST til /api/v1/tryon/jewelry, og foretag derefter polling på /api/v1/tryon/jewelry/{id} hvert 3. sekund, indtil status er completed.

  • Typisk tid: 1.5–4 min
  • Credits: 5 credits ved 2K · 6 credits ved 4K
  • Behandling: async, polling
  • Grænser: 600 requests/min
  • Autentifikation: Bearer key

Hvert tal på denne side stammer fra Photta API-dokumentationen · tjekket

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

Kort fortalt

Kort fortalt

Endpoint
POST /api/v1/tryon/jewelry
Autentifikation
Bearer API-nøgle
Behandling
Asynkron — poll status-endpointet
Typisk tid
1.5–4 min
Credits
5 credits ved 2K · 6 credits ved 4K
Billedformater
3:4 · 1:1 · 4:3 · 9:16 · 16:9
Inputgrænser
Op til 10 MB pr. billede; JPEG, PNG, WebP
Idempotent
Nej

Sådan virker det

Fem trin, cirka fem minutter fra oprettelse til første billede.

  1. Trin 1

    Opret en konto og generér en nøgle

    Opret en konto på ai.photta.app. Åbn afsnittet Developers i dashboardet, klik på Generate API key, og hent din live-nøgle. Den starter med `photta_live_` efterfulgt af 32 hex-tegn.

  2. Trin 2

    Gem nøglen i env, ikke i kildekoden

    Tilføj `PHOTTA_API_KEY=...` til `.env` (eller din platforms hemmelighedslager) og indlæs den via `process.env`. Commit aldrig nøglen; importér den aldrig i en fil markeret med `'use client'` — bundleren vil trække den ind i browseren.

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

    Skriv en lille fetch-helper

    En 20-linjers wrapper omkring `fetch()` håndterer base-URL, Authorization-header, JSON-body og fejlnormalisering. Dette er den eneste abstraktion, du har brug for, indtil der udgives et officielt SDK.

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

    Indsend en try-on og poll

    Lav et POST-kald til `/api/v1/tryon/jewelry` med den dokumenterede eksempel-body — koden nedenfor indeholder alle felter. API'et returnerer et generations-ID med det samme. Poll `/api/v1/tryon/jewelry/{id}` hvert 3. sekund, indtil `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. Trin 5

    Gem resultatet

    Det fuldførte payload indeholder `output_url` og `thumbnail_url`. Hent bytes én gang og gem dem i din egen object storage — URL'erne er stabile, men dit produkt bør ikke være afhængigt af Phottas CDN til rendering.

Fejl

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 anmodninger i minuttet pr. API-nøgle (Fast vindue; grænsen er den samme på alle planer)
  • En 429 indeholder Retry-After: 30 sekunder
  • Op til 10 MB pr. billede
  • Formater: JPEG, PNG, WebP
  • Samtidige batches pr. plan: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
  • Kun polling — API'et sender ingen webhooks.

Hvorfor en helper slår et klientbibliotek — indtil videre

  • Node 18+ leveres med native fetch — ingen runtime-afhængigheder
  • Den samme helper fungerer i Next.js route handlers, server actions og enhver worker
  • Gem API-nøglen i env; importér aldrig helperen fra et 'use client'-modul, da bundleren ellers vil lække den
  • Fungerer med enhver kø eller cron: BullMQ, Inngest, Trigger.dev, Vercel Cron, Cloudflare Queues

Hvad det ikke gør

  • Ingen officiel @photta/node-pakke endnu — REST er den eneste vej i dag
  • Polling er det eneste signal om fuldførelse; API'et sender ingen webhooks
  • Kun Bearer-nøgle; der findes intet OAuth client-credentials-flow

Spørgsmål udviklere stiller

Findes der et officielt Photta Node.js-SDK?

Ikke endnu. Den dokumenterede vej er rå REST via Nodes native fetch — helperen på denne side udgør hele integrationsfladen, og den fungerer ens for alle capabilities.

Hvilke Node.js-versioner understøttes?

Alt, der understøtter native `fetch` — hvilket vil sige Node 18 og nyere. På Node 16 kan du enten polyfille med `node-fetch` eller opgradere. Resten af koden er ren ECMAScript 2020 (async/await, template literals, dynamiske imports), så der er ingen andre runtime-krav.

Hvordan autentificerer jeg fra Node?

Send `Authorization: Bearer photta_live_xxx` på hver anmodning. Placer nøglen i en miljøvariabel og indlæs den via `process.env`; importér den aldrig fra en fil, der kører i browseren.

Hvordan håndterer jeg generationsvinduet?

Generations-endpointet er fuldstændig asynkront. POST returnerer et generations-ID med det samme med `status: 'processing'`. Poll `GET /api/v1/tryon/jewelry/{id}` hvert 3.–5. sekund, indtil `status` skifter til `completed` eller `failed`, og sæt et loft over forsøgene, så et job, der sidder fast, ikke blokerer en anmodning.

Kan jeg kalde API'et inde i Next.js?

Ja — fra en route handler (`app/**/route.ts`), en server action (`'use server'`) eller middleware. Hold API-nøglen på serveren; importér aldrig helperen fra en `'use client'`-fil. Ved langvarige job, der overskrider Vercels funktions-timeout, udskydes polling til en kø (Inngest, Trigger.dev, BullMQ), og generations-ID'et returneres til browseren, så den kan polle dit eget status-endpoint.

Hvordan signalerer API'et fejl?

Ikke-2xx-svar indeholder en JSON-body med et `error`-objekt: `type`, `code`, `message` og et anmodnings-ID til support. Almindelige tilfælde: 400 invalid_request_error med `param`, der peger på det ugyldige felt; 402 insufficient_credits med antal for `required` og `available`; 429 rate_limit_exceeded med `retry_after` i sekunder og en matchende Retry-After-header. 5xx-fejl kan sikkert forsøges igen med eksponentiel backoff.

Prøv API'et på fem minutter

Generér en Photta API-nøgle, indsæt cURL-kodestykket ovenfor, og du har dit første billede på en model, før kaffen er færdigbrygget.

  • 1.5–4 min
  • 5 credits ved 2K · 6 credits ved 4K
  • async, polling
  • 600 requests/min
  • Bearer key