Python · API de try-on virtual
API de try-on virtual 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/tryon/apparel e polling em /api/v1/tryon/apparel/{id} a cada 3 segundos até que o status seja completed.
- Tempo típico: 1.5–4 min
- Credits: 5 credits em 2K · 6 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/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")Visão geral
Visão geral
- Endpoint
- POST /api/v1/tryon/apparel
- Autenticação
- API key Bearer
- Processamento
- Assíncrono — faça polling no endpoint de status
- Tempo típico
- 1.5–4 min
- Credits
- 5 credits em 2K · 6 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/tryon/apparel` para obter um ID de geração e, em seguida, faça polling em `/api/v1/tryon/apparel/{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/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")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", "/tryon/apparel", 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."
})
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 try-on virtual em Node.js
Chame aPI de try-on virtual 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 try-on virtual com cURL
Chame a aPI de try-on virtual 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 try-on virtual para roupas
API REST que veste roupas em manequins de IA. Assíncrona, geração em 1.5–4 minutos, 5 credits em 2K, 6 em 4K. Exemplos em cURL, Node.js e Python.
LerConstruir
Crie um recurso de virtual try-on para sua aplicação
Receita ponta a ponta para implementar um recurso de virtual try-on com a Photta API: upload, submissão, polling a partir de um worker e renderização do resultado. Padrões com TypeScript + server-only.
LerConstruir
Adicione virtual try-on a um app iOS ou Android
Como adicionar virtual try-on com IA a um app nativo iOS ou Android usando a API da Photta: nunca envie a chave para o dispositivo, faça proxy pelo seu backend, espelhe o status do job.
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
- 5 credits em 2K · 6 credits em 4K
- assíncrono, polling
- 600 requisições/min
- Chave Bearer