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 · проверено
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")Коротко о главном
Коротко о главном
- Эндпоинт
- 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
Зарегистрируйтесь и сгенерируйте ключ
Тот же путь, что и для любого другого языка: ai.photta.app → Developers → Generate API key. Ключи начинаются с `photta_live_` и подходят для любой capability в Photta.
Шаг 2
Загрузите ключ из окружения
Задайте `PHOTTA_API_KEY` в окружении и считывайте его через `os.environ`. В FastAPI или Django загрузите его один раз при старте в модуль настроек. Не вшивайте ключ в исходный код или публичную историю Git.
python3 -m pip install requests export PHOTTA_API_KEY="photta_live_xxxxx"Шаг 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
Отправка и поллинг
Выполните 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
Сохранение результата
Payload завершенной задачи содержит `output_url` и `thumbnail_url`. Загрузите байты в собственное объектное хранилище — S3, GCS, Cloudflare R2, — чтобы отображение в вашем продукте не зависело в долгосрочной перспективе от CDN Photta.
Ошибки
- 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:
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` со всеми этими полями, так что вызывающий код может ветвиться по статус-коду без ручного парсинга тела.
Связанные страницы
Node.js
API виртуального try-on на Node.js
Вызывайте aPI виртуального try-on от Photta из бэкенда на Node.js с помощью нативного fetch. SDK не требуется: сохраните ключ, отправьте задачу, получите результат через polling.
ЧитатьcURL
API виртуального try-on с помощью cURL
Вызывайте aPI виртуального try-on от Photta напрямую из терминала с помощью cURL. Отправка, поллинг, загрузка — каждый фрагмент готов к использованию с jq для построения скриптовых пайплайнов.
ЧитатьВозможности
API виртуального try-on для одежды
REST API, примеряющий одежду на AI-манекены. Асинхронно, генерация за 1.5–4 минуты, 5 credits для 2K, 6 для 4K. Примеры для cURL, Node.js и Python.
ЧитатьРазработка
Внедрите виртуальную примерку (virtual try-on) в свое приложение
Полное пошаговое руководство по запуску виртуальной примерки с помощью Photta API: загрузка, отправка задачи, polling из воркера и отображение результата. Паттерны для TypeScript и бэкенда.
ЧитатьРазработка
Добавьте виртуальный try-on в приложение для iOS или Android
Как добавить виртуальный AI-try-on в нативное приложение для iOS или Android с помощью Photta API: никогда не зашивайте ключ в устройство, проксируйте через бэкенд, дублируйте статус задачи.
ЧитатьБыстрый старт
Первое изображение в Photta менее чем за 5 минут.
Зарегистрируйтесь, сгенерируйте API-ключ, выполните первый вызов cURL, опрашивайте статус до завершения, сохраните результат. 5 шагов с полным описанием.
ЧитатьСправочник
Тарифы Photta API
Сколько стоит каждый endpoint Photta API в credits при 2K и 4K, как устроены списания и возвраты, и какие эндпоинты бесплатны. Никаких цен в деньгах за изображение.
Читать
Все показатели на этой странице взяты из документации API Photta · проверено
Протестируйте API за пять минут
Сгенерируйте API-ключ Photta, вставьте пример cURL выше, и вы получите первое изображение на модели быстрее, чем сварится кофе.
- 1.5–4 min
- 5 credits при 2K · 6 credits при 4K
- async, polling
- 600 запросов/мин
- Ключ Bearer