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

POST/api/v1/tryon/apparel
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.

  1. 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.

  2. 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"
  3. 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 body
  4. Passo 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")
  5. 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

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:
    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:
        raise

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 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

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