Python · API de try-on de joyas

API de try-on de joyas en Python

En una frase

Instala requests, carga PHOTTA_API_KEY desde las variables de entorno, escribe un wrapper photta() que envíe la clave Bearer y lance un PhottaError tipado en respuestas no 2xx, luego haz POST a /api/v1/tryon/jewelry y polling a /api/v1/tryon/jewelry/{id} cada 3 segundos hasta que status sea completed.

  • Tiempo típico: 1.5–4 min
  • Credits: 5 credits a 2K · 6 credits a 4K
  • Procesamiento: asíncrono, polling
  • Límites: 600 requests/min
  • Autenticación: Bearer key

Cada cifra de esta página proviene de la documentación de la API de Photta · verificado

POST/api/v1/tryon/jewelry
import os, requests

res = requests.post(
    "https://api.photta.app/api/v1/tryon/jewelry",
    headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
    json={
      "jewelry_image": "https://cdn.yourstore.com/products/gold-necklace.jpg",
      "jewelry_type": "necklace",
      "mannequin_id": "mnk_j9k1l2",
      "background": "soft window light on dark velvet",
      "resolution": "2K",
      "aspect_ratio": "3:4",
      "user_instruction": "Make the pendant 10% smaller."
    },
)
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/jewelry/{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")

De un vistazo

De un vistazo

Endpoint
POST /api/v1/tryon/jewelry
Autenticación
Bearer API key
Procesamiento
Asíncrono — realiza polling al endpoint de estado
Tiempo típico
1.5–4 min
Credits
5 credits a 2K · 6 credits a 4K
Relaciones de aspecto
3:4 · 1:1 · 4:3 · 9:16 · 16:9
Límites de entrada
Hasta 10 MB por imagen; JPEG, PNG, WebP
Idempotente
No

Cómo funciona

Cinco pasos, cero dependencias específicas de Photta — requests cubre todo.

  1. Paso 1

    Regístrate y genera una clave

    El mismo proceso que en cualquier otro lenguaje: ai.photta.app → Developers → Generate API key. Las claves empiezan por `photta_live_` y funcionan en todas las capabilities de Photta.

  2. Paso 2

    Carga la clave desde el entorno

    Configura `PHOTTA_API_KEY` en el entorno y léela mediante `os.environ`. En FastAPI o Django, cárgala una vez al arrancar en un módulo de configuración. No insertes la clave en el código fuente ni en el historial público de Git.

    python3 -m pip install requests
    export PHOTTA_API_KEY="photta_live_xxxxx"
  3. Paso 3

    Escribe un wrapper con requests

    Una función `photta_request()` de treinta líneas gestiona la URL base, el encabezado Authorization, la serialización JSON y la traducción de errores. Es la única abstracción que necesitas hasta que llegue el SDK de 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. Paso 4

    Enviar y hacer polling

    Haz un POST a `/api/v1/tryon/jewelry` para obtener un ID de generación, luego haz polling a `/api/v1/tryon/jewelry/{id}` en un intervalo de 3 segundos. En Celery o RQ, divide el bucle de polling entre tareas para que un trabajo de varios minutos no bloquee a un worker.

    import os, requests
    
    res = requests.post(
        "https://api.photta.app/api/v1/tryon/jewelry",
        headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
        json={
          "jewelry_image": "https://cdn.yourstore.com/products/gold-necklace.jpg",
          "jewelry_type": "necklace",
          "mannequin_id": "mnk_j9k1l2",
          "background": "soft window light on dark velvet",
          "resolution": "2K",
          "aspect_ratio": "3:4",
          "user_instruction": "Make the pendant 10% smaller."
        },
    )
    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/jewelry/{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. Paso 5

    Persistir el resultado

    El payload completado incluye `output_url` y `thumbnail_url`. Descarga los bytes en tu propio almacenamiento de objetos — S3, GCS, Cloudflare R2 — para que tu producto no dependa de la CDN de Photta para el renderizado a largo plazo.

Errores

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/jewelry", json={
      "jewelry_image": "https://cdn.yourstore.com/products/gold-necklace.jpg",
      "jewelry_type": "necklace",
      "mannequin_id": "mnk_j9k1l2",
      "background": "soft window light on dark velvet",
      "resolution": "2K",
      "aspect_ratio": "3:4",
      "user_instruction": "Make the pendant 10% smaller."
    })
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

Límites

  • 600 solicitudes por minuto por API key (Ventana fija; el límite es el mismo en todos los planes)
  • Un 429 incluye Retry-After: 30 segundos
  • Hasta 10 MB por imagen
  • Formatos: JPEG, PNG, WebP
  • Lotes simultáneos por plan: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
  • Solo polling — la API no envía webhooks.

Por qué requests es la opción correcta hoy

  • Sin dependencias específicas de Photta: requests ya está en la mayoría de los proyectos de Python
  • Funciona donde sea que se ejecute Python: Django, FastAPI, Flask, workers de Celery, Lambda, scripts
  • El wrapper del cliente tiene unas treinta líneas: pégalo en photta.py y continúa
  • Se combina con asyncio mediante httpx cuando necesitas envíos paralelos

Lo que no hace

  • Todavía no hay un paquete oficial de Python para Photta: REST con requests es el patrón documentado
  • El polling es la única señal de finalización; la API no envía webhooks
  • Solo clave Bearer; no hay flujo de credenciales de cliente OAuth

Preguntas frecuentes de desarrolladores

¿Existe un SDK oficial de Python para Photta?

Aún no. La vía de integración admitida es la biblioteca requests (síncrona) o httpx (asíncrona); ambas están a solo un delgado wrapper de distancia, y el wrapper de esta página funciona para cada capacidad.

¿Qué versiones de Python son compatibles?

Python 3.9 y posteriores. El wrapper usa type hints, f-strings y la sintaxis `str | None`, lo cual funciona bien en 3.10+. En 3.9 cambia `str | None` por `Optional[str]`. requests admite oficialmente 3.8+, pero 3.9+ es el mínimo práctico para proyectos modernos de Photta.

¿Debería usar requests o httpx?

requests si estás realizando un puñado de llamadas secuenciales: es más simple y ya se encuentra en la mayoría de las bases de código. httpx si necesitas concurrencia asíncrona (envíos en paralelo, backfills por lotes) o HTTP/2. El wrapper es mínimo en ambos casos; reemplaza `requests.request` por `httpx.Client().request` y el resto del código se mantendrá idéntico.

¿Puedo llamarlo desde Django?

Sí. Coloca el wrapper en `services/photta.py`, impórtalo desde una vista o una tarea de Celery. No llames a la API dentro de un manejador de solicitudes síncrono si el trabajo toma tiempo: haz el submit desde la vista, devuelve el ID de generación y haz polling desde una tarea en segundo plano o un consumidor de Channels.

¿Cómo hago polling desde un worker de Celery?

Envía el trabajo desde una tarea y luego programa una tarea de seguimiento (mediante `apply_async(countdown=3)`) que haga polling una vez y se vuelva a programar a sí misma hasta que el trabajo se complete o se alcance un límite de reintentos máximos. Esto mantiene libres a los workers y evita tareas de larga duración que alteren los autoscalers.

¿Cómo señala los errores la API?

Las respuestas que no son 2xx incluyen un cuerpo JSON con un objeto `error`: `type`, `code`, `message`, además de `param` en respuestas 400 y `retry_after` en respuestas 429. El wrapper de esta página lanza un `PhottaError` que incluye todos estos datos para que tus llamadas puedan bifurcar por código de estado sin tener que parsear el cuerpo directamente.

Páginas relacionadas

Cada cifra de esta página proviene de la documentación de la API de Photta · verificado

Prueba la API en cinco minutos

Genera una API key de Photta, pega el snippet de cURL de arriba y tendrás tu primera imagen con modelo antes de que termine de prepararse el café.

  • 1.5–4 min
  • 5 credits a 2K · 6 credits a 4K
  • asíncrono, polling
  • 600 requests/min
  • Bearer key