Node.js · API de manequim invisível
API de manequim invisível em Node.js
Em uma frase
Defina PHOTTA_API_KEY no ambiente, escreva um helper simples com fetch para https://api.photta.app/api/v1 com uma chave Bearer, faça POST para /api/v1/ghost-mannequin e depois faça polling em /api/v1/ghost-mannequin/{id} a cada 3 segundos até que o status seja completed.
- Tempo típico: 1.5–4 min
- Credits: 4 credits em 2K · 5 credits em 4K
- Processamento: assíncrono, polling
- Limites: 600 requisições/min
- Autenticação: Chave Bearer
Todos os números nesta página vêm de a documentação da API da Photta · verificado
RESPONSE=$(curl -s -X POST https://api.photta.app/api/v1/ghost-mannequin \
-H "Authorization: Bearer $PHOTTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product_image": "https://cdn.yourstore.com/products/jacket-front.jpg",
"back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
"image_side": "front",
"angle": "side",
"style": "3d",
"background_color": "white",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "closed front, cream interior lining",
"brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
}')
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/ghost-mannequin/$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/ghost-mannequin", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"product_image": "https://cdn.yourstore.com/products/jacket-front.jpg",
"back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
"image_side": "front",
"angle": "side",
"style": "3d",
"background_color": "white",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "closed front, cream interior lining",
"brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
}),
});
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/ghost-mannequin/${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/ghost-mannequin",
headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
json={
"product_image": "https://cdn.yourstore.com/products/jacket-front.jpg",
"back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
"image_side": "front",
"angle": "side",
"style": "3d",
"background_color": "white",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "closed front, cream interior lining",
"brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
},
)
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/ghost-mannequin/{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")Visão geral
Visão geral
- Endpoint
- POST /api/v1/ghost-mannequin
- Autenticação
- API key Bearer
- Processamento
- Assíncrono — faça polling no endpoint de status
- Tempo típico
- 1.5–4 min
- Credits
- 4 credits em 2K · 5 credits em 4K
- Proporções de tela
- 3:4 · 1:1 · 4:3 · 9:16 · 16:9
- Limites de entrada
- Até 10 MB por imagem; JPEG, PNG, WebP
- Idempotente
- Não
Como funciona
Cinco etapas, cerca de cinco minutos do cadastro à primeira imagem.
Passo 1
Cadastre-se e gere uma chave
Crie uma conta em ai.photta.app. Abra a seção Desenvolvedores do painel, clique em Generate API key e copie a chave de produção. Ela começa com `photta_live_` seguido por 32 caracteres hexadecimais.
Passo 2
Armazene a chave no ambiente, não no código-fonte
Adicione `PHOTTA_API_KEY=...` ao `.env` (ou ao gerenciador de segredos da sua plataforma) e carregue-a via `process.env`. Nunca comite a chave; nunca a importe em um arquivo marcado com `'use client'` — o empacotador irá incluí-la no navegador.
node --version # 18 or later ships fetch echo "PHOTTA_API_KEY=photta_live_xxxxx" >> .envPasso 3
Escreva um helper de fetch enxuto
Um wrapper de 20 linhas em torno de `fetch()` lida com a URL base, o header Authorization, o body JSON e a normalização de erros. Esta é a única abstração de que você precisa até o lançamento de um SDK oficial.
// 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; }Passo 4
Envie um try-on e faça polling
Envie um POST para `/api/v1/ghost-mannequin` com o body de exemplo documentado — o código abaixo inclui todos os campos. A API retorna um generation ID imediatamente. Faça polling em `/api/v1/ghost-mannequin/{id}` a cada 3 segundos até que `status === 'completed'`.
const res = await fetch("https://api.photta.app/api/v1/ghost-mannequin", { method: "POST", headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ "product_image": "https://cdn.yourstore.com/products/jacket-front.jpg", "back_image": "https://cdn.yourstore.com/products/jacket-back.jpg", "image_side": "front", "angle": "side", "style": "3d", "background_color": "white", "resolution": "2K", "aspect_ratio": "3:4", "user_instruction": "closed front, cream interior lining", "brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67" }), }); 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/ghost-mannequin/${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"); }Passo 5
Persista o resultado
O payload concluído inclui `output_url` e `thumbnail_url`. Faça o fetch dos bytes uma vez e armazene-os no seu próprio object storage — as URLs são estáveis, mas o seu produto não deve depender da CDN do Photta para renderização.
Erros
- 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("/ghost-mannequin", { method: "POST", body: JSON.stringify({
"product_image": "https://cdn.yourstore.com/products/jacket-front.jpg",
"back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
"image_side": "front",
"angle": "side",
"style": "3d",
"background_color": "white",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "closed front, cream interior lining",
"brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
}) });
} 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 requisições por minuto por API key (Janela fixa; o limite é o mesmo em todos os planos)
- Um 429 retorna Retry-After: 30 segundos
- Até 10 MB por imagem
- Formatos: JPEG, PNG, WebP
- Batches concorrentes por plano: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
- Apenas polling — a API não envia webhooks.
Por que um helper é melhor que uma biblioteca cliente — por enquanto
- O Node 18+ traz fetch nativo — sem dependências em tempo de execução
- O mesmo helper funciona em route handlers do Next.js, server actions e em qualquer worker
- Mantenha a chave de API em variáveis de ambiente; nunca importe o helper a partir de um módulo 'use client', caso contrário o bundler irá expô-la
- Funciona com qualquer fila ou cron: BullMQ, Inngest, Trigger.dev, Vercel Cron, Cloudflare Queues
O que ela não faz
- Ainda não há um pacote oficial @photta/node — REST é o único caminho atualmente
- Polling é o único sinal de conclusão; a API não envia webhooks
- Apenas chave Bearer; não há fluxo OAuth client-credentials
Dúvidas frequentes de desenvolvedores
Existe um SDK oficial do Photta para Node.js?
Ainda não. O caminho documentado é REST puro via fetch nativo do Node — o helper nesta página cobre toda a superfície de integração e funciona da mesma forma para todas as capabilities.
Quais versões do Node.js são suportadas?
Qualquer uma que suporte `fetch` nativo — ou seja, Node 18 e versões posteriores. No Node 16, você usa um polyfill com `node-fetch` ou atualiza o runtime. O restante do código é ECMAScript 2020 puro (async/await, template literals, dynamic imports), portanto não há outros requisitos de runtime.
Como faço a autenticação no Node?
Envie `Authorization: Bearer photta_live_xxx` em todas as requisições. Defina a chave em uma variável de ambiente e carregue-a via `process.env`; nunca a importe em um arquivo executado no navegador.
Como gerencio a janela de geração?
O endpoint de geração é totalmente assíncrono. O POST retorna um generation ID imediatamente com `status: 'processing'`. Faça polling com `GET /api/v1/ghost-mannequin/{id}` a cada 3–5 segundos até que o `status` mude para `completed` ou `failed`, e limite o número de tentativas para que um job travado não prenda a requisição.
Posso chamar a API dentro do Next.js?
Sim — a partir de um route handler (`app/**/route.ts`), uma server action (`'use server'`) ou middleware. Mantenha a chave de API no servidor; nunca importe o helper de um arquivo `'use client'`. Para jobs longos que excedam o timeout de função da Vercel, delegue o polling para uma fila (Inngest, Trigger.dev, BullMQ) e retorne o generation ID para o navegador fazer polling no seu próprio status endpoint.
Como a API sinaliza erros?
Respostas não-2xx contêm um body JSON com um objeto `error`: `type`, `code`, `message` e um ID de requisição para suporte. Casos comuns: 400 invalid_request_error com `param` apontando para o campo inválido; 402 insufficient_credits com contagens de `required` e `available`; 429 rate_limit_exceeded com `retry_after` em segundos e o header Retry-After correspondente. Erros 5xx podem ser repetidos com segurança usando backoff exponencial.
Páginas relacionadas
Python
API de manequim invisível em Python
Chame aPI de manequim invisível do Photta em Python com a biblioteca requests. Wrapper de trinta linhas, compatível com async via httpx, funciona em Django, FastAPI e Celery.
LercURL
API de manequim invisível com cURL
Chame a aPI de manequim invisível da Photta diretamente do shell com cURL. Envie, faça polling, baixe — todos os snippets funcionam com o jq para pipelines em script.
LerRecursos
API de manequim invisível para fotos de produtos em manequim invisível
Endpoint de IA para manequim invisível. Remova o manequim de uma foto de roupa e preserve os detalhes internos. 3D ou flat-lay, processamento em lote, 4 credits em 2K.
LerConstruir
Automatize a extração de ghost mannequin para o seu catálogo
Receita de automação noturna de catálogo: busque as atualizações de produtos de ontem, envie em lote para a API de ghost mannequin da Photta, faça o polling do lote pai e persista no seu banco de dados.
LerInício rápido
Primeira imagem no Photta em menos de cinco minutos.
Cadastre-se, gere uma chave de API, execute sua primeira chamada cURL, faça polling até a conclusão, persista o resultado. Cinco etapas, documentadas de ponta a ponta.
LerReferência
Preços da API Photta
Quanto custa em credits cada endpoint da Photta API em 2K e 4K, como funcionam as cobranças e reembolsos, e quais endpoints são gratuitos. Sem preço monetário por imagem.
Ler
Todos os números nesta página vêm de a documentação da API da Photta · verificado
Experimente a API em cinco minutos
Gere uma chave de API do Photta, cole o snippet de cURL acima e você terá sua primeira imagem com modelo antes que o café termine de passar.
- 1.5–4 min
- 4 credits em 2K · 5 credits em 4K
- assíncrono, polling
- 600 requisições/min
- Chave Bearer