Python · API de ghost mannequin em lote
API de ghost mannequin em lote em Python
Em uma frase
Instale requests, carregue PHOTTA_API_KEY do ambiente, escreva um wrapper photta() que envia a chave Bearer e lança um PhottaError tipado em respostas não-2xx, depois faça POST para /api/v1/ghost-mannequin/batch e polling em /api/v1/ghost-mannequin/batch/{id} a cada 3 segundos até que o status seja completed.
- Tempo típico: 1.5–4 min per image
- 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/batch \
-H "Authorization: Bearer $PHOTTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product_images": [
{
"image": "https://cdn.yourstore.com/products/jacket-front.jpg",
"back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
"image_side": "front"
},
{
"image": "https://cdn.yourstore.com/products/shirt-front.jpg",
"image_side": "front"
},
{
"image": "https://cdn.yourstore.com/products/trousers-front.jpg",
"image_side": "front"
}
],
"style": "3d",
"background_color": "white",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "closed front, buttoned up",
"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/batch/$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/batch", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"product_images": [
{
"image": "https://cdn.yourstore.com/products/jacket-front.jpg",
"back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
"image_side": "front"
},
{
"image": "https://cdn.yourstore.com/products/shirt-front.jpg",
"image_side": "front"
},
{
"image": "https://cdn.yourstore.com/products/trousers-front.jpg",
"image_side": "front"
}
],
"style": "3d",
"background_color": "white",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "closed front, buttoned up",
"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/batch/${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/batch",
headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
json={
"product_images": [
{
"image": "https://cdn.yourstore.com/products/jacket-front.jpg",
"back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
"image_side": "front"
},
{
"image": "https://cdn.yourstore.com/products/shirt-front.jpg",
"image_side": "front"
},
{
"image": "https://cdn.yourstore.com/products/trousers-front.jpg",
"image_side": "front"
}
],
"style": "3d",
"background_color": "white",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "closed front, buttoned up",
"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/batch/{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/batch
- Autenticação
- API key Bearer
- Processamento
- Assíncrono — faça polling no endpoint de status
- Tempo típico
- 1.5–4 min per image
- 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, zero dependências específicas do Photta — requests cobre tudo.
Passo 1
Cadastre-se e gere uma chave
O mesmo processo de qualquer outra linguagem: ai.photta.app → Developers → Generate API key. As chaves começam com `photta_live_` e funcionam em todas as capabilities do Photta.
Passo 2
Carregue a chave a partir do ambiente
Defina `PHOTTA_API_KEY` no ambiente e leia-a via `os.environ`. No FastAPI ou Django, carregue-a uma única vez na inicialização em um módulo de settings. Não embuta a chave no código-fonte nem no histórico público do Git.
python3 -m pip install requests export PHOTTA_API_KEY="photta_live_xxxxx"Passo 3
Escreva um wrapper para requests
Uma função `photta_request()` de trinta linhas lida com a URL base, o cabeçalho Authorization, a serialização de JSON e a conversão de erros. Essa é a única abstração necessária até a chegada do SDK para Python.
# photta.py — one helper for every endpoint import os, requests BASE = "https://api.photta.app/api/v1" class PhottaError(Exception): def __init__(self, status, error, retry_after=None): super().__init__(error.get("message", "request failed")) self.status, self.type, self.retry_after = status, error.get("type"), retry_after def photta(method, path, **kwargs): res = requests.request(method, BASE + path, headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"}, timeout=30, **kwargs) body = res.json() if not res.ok: raise PhottaError(res.status_code, body.get("error", {}), res.headers.get("Retry-After")) return bodyPasso 4
Envie e faça polling
Faça um POST para `/api/v1/ghost-mannequin/batch` para obter um ID de geração e, em seguida, faça polling em `/api/v1/ghost-mannequin/batch/{id}` a cada 3 segundos. No Celery ou RQ, divida o loop de polling entre tarefas para que um job de vários minutos não bloqueie um worker.
import os, requests res = requests.post( "https://api.photta.app/api/v1/ghost-mannequin/batch", headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"}, json={ "product_images": [ { "image": "https://cdn.yourstore.com/products/jacket-front.jpg", "back_image": "https://cdn.yourstore.com/products/jacket-back.jpg", "image_side": "front" }, { "image": "https://cdn.yourstore.com/products/shirt-front.jpg", "image_side": "front" }, { "image": "https://cdn.yourstore.com/products/trousers-front.jpg", "image_side": "front" } ], "style": "3d", "background_color": "white", "resolution": "2K", "aspect_ratio": "3:4", "user_instruction": "closed front, buttoned up", "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/batch/{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")Passo 5
Persista o resultado
O payload concluído inclui `output_url` e `thumbnail_url`. Baixe os bytes no seu próprio storage de objetos — S3, GCS, Cloudflare R2 — para que seu produto não fique atrelado ao CDN da Photta para renderização a longo prazo.
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:
photta("POST", "/ghost-mannequin/batch", json={
"product_images": [
{
"image": "https://cdn.yourstore.com/products/jacket-front.jpg",
"back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
"image_side": "front"
},
{
"image": "https://cdn.yourstore.com/products/shirt-front.jpg",
"image_side": "front"
},
{
"image": "https://cdn.yourstore.com/products/trousers-front.jpg",
"image_side": "front"
}
],
"style": "3d",
"background_color": "white",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "closed front, buttoned up",
"brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
})
except PhottaError as e:
if e.status == 402:
print("insufficient_credits_error: top up before retrying")
elif e.status == 429:
time.sleep(int(e.retry_after or 30))
else:
raiseLimites
- 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 o requests é a escolha certa hoje
- Sem dependências específicas da Photta — o requests já está na maioria dos projetos Python
- Funciona onde quer que o Python rode: Django, FastAPI, Flask, workers do Celery, Lambda, scripts
- O wrapper do cliente tem cerca de trinta linhas — cole-o em photta.py e siga em frente
- Combina com asyncio via httpx quando você precisa de envio em paralelo
O que ela não faz
- Ainda não há um pacote oficial da Photta para Python — REST com requests é o padrão documentado
- Polling é o único sinal de conclusão; a API não envia webhooks
- Apenas chave Bearer; não há fluxo de client-credentials via OAuth
Dúvidas frequentes de desenvolvedores
Existe um SDK oficial da Photta para Python?
Ainda não. O caminho de integração suportado é a biblioteca requests (síncrona) ou httpx (assíncrona) — ambas exigem apenas um wrapper simples, e o wrapper desta página funciona para todas as capacidades.
Quais versões do Python são suportadas?
Python 3.9 ou superior. O wrapper usa type hints, f-strings e a sintaxe `str | None`, que funciona no 3.10+. No 3.9, troque `str | None` por `Optional[str]`. O próprio requests suporta oficialmente 3.8+, mas 3.9+ é o mínimo prático para projetos modernos com a Photta.
Devo usar requests ou httpx?
requests se você estiver fazendo poucas chamadas sequenciais — é mais simples e já está na maioria das bases de código. httpx se você precisa de concorrência assíncrona (envios em paralelo, backfills em lote) ou HTTP/2. O wrapper é mínimo em ambos os casos; troque `requests.request` por `httpx.Client().request` e o resto do código permanece idêntico.
Posso chamá-lo a partir do Django?
Sim. Coloque o wrapper em `services/photta.py`, importe-o de uma view ou de uma tarefa do Celery. Não chame a API dentro de um handler de requisição síncrono se o job for demorado — envie a partir da view, retorne o ID de geração e faça o polling a partir de uma tarefa em segundo plano ou de um consumidor do Channels.
Como faço polling a partir de um worker do Celery?
Envie o job a partir de uma tarefa e, em seguida, agende uma tarefa de acompanhamento (via `apply_async(countdown=3)`) que faz o polling uma vez e se reagenda até que o job termine ou atinja o limite máximo de tentativas. Isso mantém os workers livres e evita tarefas de longa duração que atrapalham o autoscaling.
Como a API sinaliza erros?
Respostas que não sejam 2xx trazem um corpo JSON com um objeto `error`: `type`, `code`, `message`, além de `param` em erros 400 e `retry_after` em erros 429. O wrapper desta página lança um `PhottaError` contendo todos esses dados para que seus callers possam tratar por código de status sem precisar fazer o parse do corpo manualmente.
Páginas relacionadas
Node.js
API de ghost mannequin em lote em Node.js
Chame aPI de ghost mannequin em lote da Photta a partir de um backend em Node.js com fetch nativo. Nenhum SDK é necessário: armazene sua chave, envie o job e faça polling pelo resultado.
LercURL
API de ghost mannequin em lote com cURL
Chame a aPI de ghost mannequin em lote 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 ghost mannequin em lote para catálogos inteiros
Envie várias fotos de roupas em uma única requisição de ghost mannequin. IDs individuais por item, polling agregado e reserva antecipada de créditos. 4 créditos por imagem 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 per image
- 4 credits em 2K · 5 credits em 4K
- assíncrono, polling
- 600 requisições/min
- Chave Bearer