Node.js · Virtual try-on API
Virtual try-on API in Node.js
In één zin
Stel PHOTTA_API_KEY in env in, schrijf een kleine fetch-helper voor https://api.photta.app/api/v1 met een Bearer-sleutel, POST naar /api/v1/tryon/apparel en poll vervolgens elke 3 seconden /api/v1/tryon/apparel/{id} totdat de status completed is.
- Typische duur: 1.5–4 min
- Credits: 5 credits bij 2K · 6 credits bij 4K
- Verwerking: async, polling
- Limieten: 600 requests/min
- Authenticatie: Bearer key
Elk getal op deze pagina is afkomstig uit de Photta-API-documentatie · gecontroleerd
RESPONSE=$(curl -s -X POST https://api.photta.app/api/v1/tryon/apparel \
-H "Authorization: Bearer $PHOTTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"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."
}')
ID=$(echo "$RESPONSE" | jq -r '.data.id')
# $ID is data.id from the POST response. Poll every 3 seconds until status is "completed" or "failed".
for i in $(seq 1 120); do
RESULT=$(curl -s "https://api.photta.app/api/v1/tryon/apparel/$ID" -H "Authorization: Bearer $PHOTTA_API_KEY")
STATUS=$(echo "$RESULT" | jq -r '.data.status')
[ "$STATUS" = "completed" ] && break
[ "$STATUS" = "failed" ] && echo "generation failed" && exit 1
sleep 3
done
echo "$RESULT" | jq -r '.data.output_url'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");
}import os, requests
res = requests.post(
"https://api.photta.app/api/v1/tryon/apparel",
headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
json={
"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."
},
)
data = res.json()["data"] # data["id"], data["status"]
import time
def wait_for(job_id):
for _ in range(120):
res = requests.get(f"https://api.photta.app/api/v1/tryon/apparel/{job_id}", headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"})
data = res.json()["data"]
if data["status"] == "completed":
return data["output_url"]
if data["status"] == "failed":
raise RuntimeError(data.get("error_message", "generation failed"))
time.sleep(3)
raise TimeoutError("timed out after 6 minutes")In één oogopslag
In één oogopslag
- Endpoint
- POST /api/v1/tryon/apparel
- Authenticatie
- Bearer API key
- Verwerking
- Asynchroon — poll het status-endpoint
- Typische duur
- 1.5–4 min
- Credits
- 5 credits bij 2K · 6 credits bij 4K
- Beeldverhoudingen
- 3:4 · 1:1 · 4:3 · 9:16 · 16:9
- Invoerlimieten
- Tot 10 MB per afbeelding; JPEG, PNG, WebP
- Idempotent
- Nee
Hoe het werkt
Vijf stappen, ongeveer vijf minuten van registratie tot eerste afbeelding.
Stap 1
Meld je aan en genereer een sleutel
Maak een account aan op ai.photta.app. Open het Developers-gedeelte van het dashboard, klik op Generate API key en kopieer de live-sleutel. Deze begint met `photta_live_` gevolgd door 32 hex-tekens.
Stap 2
Sla de sleutel op in env, niet in broncode
Voeg `PHOTTA_API_KEY=...` toe aan `.env` (of de secret store van je platform) en laad deze via `process.env`. Commit de sleutel nooit; importeer hem nooit in een bestand gemarkeerd met `'use client'` — de bundler trekt hem anders naar de browser.
node --version # 18 or later ships fetch echo "PHOTTA_API_KEY=photta_live_xxxxx" >> .envStap 3
Schrijf een kleine fetch-helper
Eén wrapper van 20 regels rond `fetch()` regelt de basis-URL, Authorization-header, JSON-body en foutnormalisatie. Dit is de enige abstractie die je nodig hebt totdat er een officiële SDK verschijnt.
// 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; }Stap 4
Verstuur een try-on en voer polling uit
Voer een POST uit naar `/api/v1/tryon/apparel` met de gedocumenteerde voorbeeld-body — de onderstaande code bevat elk veld. De API retourneert direct een generatie-ID. Voer elke 3 seconden polling uit op `/api/v1/tryon/apparel/{id}` totdat `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"); }Stap 5
Sla het resultaat op
De voltooide payload bevat `output_url` en `thumbnail_url`. Haal de bytes één keer op en sla ze op in je eigen objectopslag — de URL's zijn stabiel, maar je product mag voor de weergave niet afhankelijk zijn van het CDN van Photta.
Errors
- 400
invalid_request_error - The request is malformed or contains invalid parameters.
- 401
authentication_error - The API key is missing, invalid, or expired.
- 402
insufficient_credits_error - Your account does not have enough credits.
- 404
not_found_error - The requested resource does not exist.
- 409
conflict_error - The request conflicts with current state — e.g. your concurrent-batch cap is reached.
- 429
rate_limit_error - Too many requests. Slow down and retry.Retry-After: 30
- 500
api_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;
}Limieten
- 600 verzoeken per minuut per API key (Vast venster; de limiet is identiek op elk abonnement)
- Een 429 bevat Retry-After: 30 seconden
- Tot 10 MB per afbeelding
- Formaten: JPEG, PNG, WebP
- Gelijktijdige batches per abonnement: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
- Uitsluitend polling — de API stuurt geen webhooks.
Waarom een helper beter is dan een clientbibliotheek — voorlopig
- Node 18+ levert standaard fetch mee — geen runtime-afhankelijkheden
- Dezelfde helper werkt binnen Next.js route handlers, server actions en elke worker
- Houd de API-sleutel in env; importeer de helper nooit vanuit een 'use client'-module, anders lekt de bundler deze
- Werkt met elke wachtrij of cron: BullMQ, Inngest, Trigger.dev, Vercel Cron, Cloudflare Queues
Wat het niet doet
- Nog geen officieel @photta/node-package — REST is momenteel het enige pad
- Polling is het enige signaal voor voltooiing; de API verstuurt geen webhooks
- Alleen een Bearer-sleutel; er is geen OAuth client-credentials-flow
Veelgestelde vragen van developers
Is er een officiële Photta Node.js-SDK?
Nog niet. Het gedocumenteerde pad is rechtstreekse REST via de native fetch van Node — de helper op deze pagina is het volledige integratie-oppervlak, en hij werkt hetzelfde voor elke functionaliteit.
Welke Node.js-versies worden ondersteund?
Alles wat native `fetch` ondersteunt — wat neerkomt op Node 18 en nieuwer. Op Node 16 gebruik je een polyfill met `node-fetch` of voer je een upgrade uit. De rest van de code is standaard ECMAScript 2020 (async/await, template literals, dynamische imports), dus er zijn geen verdere runtime-vereisten.
Hoe authenticeer ik vanuit Node?
Stuur `Authorization: Bearer photta_live_xxx` mee bij elk verzoek. Plaats de sleutel in een omgevingsvariabele en laad deze via `process.env`; importeer deze nooit vanuit een bestand dat in de browser draait.
Hoe ga ik om met de generatietijd?
Het generatie-endpoint is volledig asynchroon. Een POST retourneert direct een generatie-ID met `status: 'processing'`. Voer elke 3–5 seconden polling uit met `GET /api/v1/tryon/apparel/{id}` totdat `status` verandert in `completed` of `failed`, en begrens het aantal pogingen zodat een vastgelopen taak een verzoek niet laat hangen.
Kan ik de API aanroepen binnen Next.js?
Ja — vanuit een route handler (`app/**/route.ts`), een server action (`'use server'`) of middleware. Houd de API-sleutel op de server; importeer de helper nooit vanuit een `'use client'`-bestand. Voor langdurige taken die de functietime-out van Vercel overschrijden, delegeer je de polling naar een wachtrij (Inngest, Trigger.dev, BullMQ) en stuur je de generatie-ID terug naar de browser, zodat deze je eigen status-endpoint kan pollen.
Hoe signaleert de API fouten?
Niet-2xx-antwoorden bevatten een JSON-body met een `error`-object: `type`, `code`, `message` en een request-ID voor ondersteuning. Veelvoorkomende situaties: 400 invalid_request_error waarbij `param` naar het ongeldige veld verwijst; 402 insufficient_credits met aantallen voor `required` en `available`; 429 rate_limit_exceeded met `retry_after` in seconden en een bijbehorende Retry-After-header. 5xx-fouten kunnen veilig opnieuw worden geprobeerd met exponentiële back-off.
Gerelateerde pagina's
Python
Virtual try-on API in Python
Roep Photta's virtual try-on API aan in Python met de requests-bibliotheek. Een wrapper van dertig regels, async-vriendelijk via httpx, werkt in Django, FastAPI en Celery.
LezencURL
Virtual try-on API met cURL
Roep Photta's virtual try-on API direct aan vanuit de shell met cURL. Verzenden, pollen, downloaden — elk fragment werkt naadloos samen met jq voor gescripte pipelines.
LezenMogelijkheden
Virtual try-on API voor kleding
REST API die kleding op AI-mannequins plaatst. Asynchroon, generatie in 1.5–4 minuten, 5 credits bij 2K, 6 bij 4K. Voorbeelden in cURL, Node.js en Python.
LezenBouwen
Bouw een virtual try-on-feature voor je app
End-to-end recept voor het uitrollen van een virtual try-on-feature met de Photta API: uploaden, submitten, pollen vanuit een worker, resultaat renderen. TypeScript + server-only patronen.
LezenBouwen
Voeg virtual try-on toe aan een iOS- of Android-app
Hoe je met de Photta API virtuele AI-try-on toevoegt aan een native iOS- of Android-app: lever de key nooit mee naar het apparaat, proxy via je backend, spiegel de taakstatus.
LezenSnelstartgids
Eerste Photta-afbeelding in minder dan vijf minuten.
Registreer je, genereer een API-sleutel, voer je eerste cURL-call uit, poll totdat deze is voltooid, sla het resultaat op. Vijf stappen, end-to-end gedocumenteerd.
LezenReferentie
Photta API-prijzen
Wat elk Photta API-endpoint kost in credits op 2K en 4K, hoe facturering en restituties werken, en welke endpoints gratis zijn. Geen geldprijs per afbeelding.
Lezen
Elk getal op deze pagina is afkomstig uit de Photta-API-documentatie · gecontroleerd
Probeer de API in vijf minuten
Genereer een Photta API-sleutel, plak het bovenstaande cURL-fragment en je hebt je eerste afbeelding op een model voordat de koffie klaar is.
- 1.5–4 min
- 5 credits bij 2K · 6 credits bij 4K
- async, polling
- 600 requests/min
- Bearer key