Python · API виртуального try-on

API виртуального try-on на Python

В двух словах

Установите requests, загрузите PHOTTA_API_KEY из окружения, напишите обертку photta(), которая передает ключ Bearer и вызывает типизированное исключение PhottaError при ответах, отличных от 2xx, затем отправьте POST на /api/v1/tryon/apparel и опрашивайте /api/v1/tryon/apparel/{id} каждые 3 секунды, пока status не станет completed.

  • Типичное время: 1.5–4 min
  • Кредиты: 5 credits при 2K · 6 credits при 4K
  • Обработка: async, polling
  • Лимиты: 600 запросов/мин
  • Аутентификация: Ключ Bearer

Все показатели на этой странице взяты из документации API Photta · проверено

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

Коротко о главном

Коротко о главном

Эндпоинт
POST /api/v1/tryon/apparel
Аутентификация
API-ключ Bearer
Обработка
Асинхронно — опрашивайте эндпоинт статуса
Типичное время
1.5–4 min
Кредиты
5 credits при 2K · 6 credits при 4K
Соотношения сторон
3:4 · 1:1 · 4:3 · 9:16 · 16:9
Ограничения на входные данные
До 10 МБ на изображение; JPEG, PNG, WebP
Идемпотентность
Нет

Как это работает

Пять шагов, ноль специфичных для Photta зависимостей — requests берет все на себя.

  1. Шаг 1

    Зарегистрируйтесь и сгенерируйте ключ

    Тот же путь, что и для любого другого языка: ai.photta.app → Developers → Generate API key. Ключи начинаются с `photta_live_` и подходят для любой capability в Photta.

  2. Шаг 2

    Загрузите ключ из окружения

    Задайте `PHOTTA_API_KEY` в окружении и считывайте его через `os.environ`. В FastAPI или Django загрузите его один раз при старте в модуль настроек. Не вшивайте ключ в исходный код или публичную историю Git.

    python3 -m pip install requests
    export PHOTTA_API_KEY="photta_live_xxxxx"
  3. Шаг 3

    Напишите обертку для requests

    Функция `photta_request()` из тридцати строк берет на себя базовый URL, заголовок Authorization, сериализацию JSON и трансляцию ошибок. Это единственная абстракция, которая вам нужна, пока не выйдет Python SDK.

    # 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. Шаг 4

    Отправка и поллинг

    Выполните POST к `/api/v1/tryon/apparel`, чтобы получить ID генерации, затем опрашивайте `/api/v1/tryon/apparel/{id}` с интервалом в 3 секунды. В Celery или RQ разбивайте цикл поллинга на отдельные задачи, чтобы многоминутная работа не блокировала воркер.

    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. Шаг 5

    Сохранение результата

    Payload завершенной задачи содержит `output_url` и `thumbnail_url`. Загрузите байты в собственное объектное хранилище — S3, GCS, Cloudflare R2, — чтобы отображение в вашем продукте не зависело в долгосрочной перспективе от CDN Photta.

Ошибки

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

Лимиты

  • 600 запросов в минуту на один API-ключ (Фиксированное окно; лимит одинаков для всех планов)
  • Ответ 429 содержит заголовок Retry-After: 30 секунд
  • До 10 МБ на изображение
  • Форматы: JPEG, PNG, WebP
  • Параллельных пакетов на план: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
  • Только polling — API не отправляет вебхуки.

Почему requests — оптимальный выбор сегодня

  • Никаких специфичных зависимостей от Photta — requests уже есть в большинстве проектов на Python
  • Работает везде, где запускается Python: Django, FastAPI, Flask, воркеры Celery, Lambda, скрипты
  • Обертка клиента занимает около тридцати строк — скопируйте ее в photta.py и двигайтесь дальше
  • Легко объединяется с asyncio через httpx, когда требуется параллельная отправка

Чего API не делает

  • Официального пакета Photta для Python пока нет — документированным паттерном остается REST через requests
  • Поллинг — единственный способ узнать о завершении; API не отправляет вебхуки
  • Только Bearer-токен; поток авторизации OAuth client-credentials отсутствует

Частые вопросы разработчиков

Существует ли официальный Python SDK для Photta?

Пока нет. Поддерживаемый способ интеграции — библиотека requests (синхронно) или httpx (асинхронно). В обоих случаях достаточно тонкой обертки, и код со страницы подходит для любых возможностей API.

Какие версии Python поддерживаются?

Python 3.9 и новее. Обертка использует type hints, f-строки и синтаксис `str | None`, который работает в 3.10+. В 3.9 замените `str | None` на `Optional[str]`. Сама requests официально поддерживает 3.8+, но 3.9+ — это практический минимум для современных проектов на Photta.

Что использовать: requests или httpx?

requests, если вы делаете несколько последовательных вызовов — она проще и уже присутствует в большинстве кодовых баз. httpx — если вам нужна асинхронная многозадачность (параллельная отправка, массовые бекфилы) или HTTP/2. Обертка компактна в обоих случаях: замените `requests.request` на `httpx.Client().request`, а остальной код останется прежним.

Можно ли вызывать API из Django?

Да. Поместите обертку в `services/photta.py`, импортируйте ее во view или в задачу Celery. Не вызывайте API внутри синхронного обработчика запросов, если операция длительная: отправьте запрос из view, верните ID генерации и опрашивайте статус из фоновой задачи или консьюмера Channels.

Как организовать поллинг из воркера Celery?

Запустите задачу на отправку, затем запланируйте следующую задачу (через `apply_async(countdown=3)`), которая опрашивает статус один раз и перезапускает саму себя, пока задача не перейдет в completed или не будет превышен лимит попыток. Это освобождает воркеры и помогает избежать долгих задач, ломающих автомасштабирование.

Как API сообщает об ошибках?

Ответы с кодом, отличным от 2xx, возвращают JSON-тело с объектом `error`: `type`, `code`, `message`, а также `param` при ошибках 400 и `retry_after` при 429. Обертка на этой странице выбрасывает исключение `PhottaError` со всеми этими полями, так что вызывающий код может ветвиться по статус-коду без ручного парсинга тела.

Связанные страницы

Все показатели на этой странице взяты из документации API Photta · проверено

Протестируйте API за пять минут

Сгенерируйте API-ключ Photta, вставьте пример cURL выше, и вы получите первое изображение на модели быстрее, чем сварится кофе.

  • 1.5–4 min
  • 5 credits при 2K · 6 credits при 4K
  • async, polling
  • 600 запросов/мин
  • Ключ Bearer