Node.js · Virtual Try-On API

Virtual Try-On API на Node.js

Пакета @photta/node пока нет — и он вам не нужен. В Node 18+ есть встроенный `fetch`, и хелпер на 20 строк позволит получить первое фото на модели примерно за пять минут.

Суть

Установите `PHOTTA_API_KEY`, напишите хелпер `phottaFetch()` для вызовов `https://ai.photta.app/api/v1` с `Authorization: Bearer photta_live_xxx`, отправьте POST на `/tryon/apparel` с URL товара и ID манекена, затем опрашивайте `/tryon/apparel/:id` каждые 3 секунды до `status === 'completed'`. Обычно это занимает 1.5–4 минуты.

Обновлено · 2026-04-19

Ваш первый запрос

Node.jsPythoncURLcURL
import { phottaFetch } from "./photta.js";

// 1. Submit the job — returns a generation ID immediately.
const created = await phottaFetch("/tryon/apparel", {
  method: "POST",
  body: JSON.stringify({
    product_type: "dress",
    product_images: ["https://example.com/dress.jpg"],
    mannequin_id: "mnq_athena_ts",
    pose_id: "pose_standing_front",
    resolution: "2K",
    aspect_ratio: "3:4",
  }),
});
const generationId = created.data.id;

// 2. Poll every 3 seconds until processing completes. Typical
// completion is 1.5–4 minutes; put an upper bound so a stuck
// job can't hang your request forever.
const pollInterval = 3000;
const maxAttempts = 120;     // 120 × 3s = 6 minutes
let result;

for (let i = 0; i < maxAttempts; i++) {
  result = await phottaFetch(`/tryon/apparel/${generationId}`);
  if (result.data.status === "completed") break;
  if (result.data.status === "failed") {
    throw new Error(result.data.error_message);
  }
  await new Promise((r) => setTimeout(r, pollInterval));
}

console.log("Result:", result.data.output_url);

Чего ожидать

Typical completion

1–3min

2K / 4K credits

4 / 6

Styles

2

Batch-ready

yes

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

Virtual Try-On API на Node.js

Пять шагов и пять минут от регистрации до первого фото.

  1. 01

    Шаг 1

    Зарегистрируйтесь и создайте ключ

    Создайте аккаунт на ai.photta.app. В разделе Developers сгенерируйте API-ключ. Он начинается с `photta_live_`, за которым идут 32 символа.

  2. 02

    Шаг 2

    Храните ключ в env, а не в коде

    Добавьте `PHOTTA_API_KEY=...` в `.env` и загружайте через `process.env`. Никогда не делайте коммит ключа и не импортируйте его в файлы с пометкой `'use client'` — сборщик вытянет его в браузер.

  3. 03

    Шаг 3

    Напишите небольшой fetch-хелпер

    Обертка на 20 строк вокруг `fetch()` берет на себя базовый URL, заголовок Authorization, JSON и нормализацию ошибок. Это единственная абстракция, которая вам нужна до выхода официального SDK.

  4. 04

    Шаг 4

    Отправьте запрос и опрашивайте

    POST на `/tryon/apparel` с параметрами `product_type`, `product_images`, `mannequin_id`, `pose_id`, `resolution` и `aspect_ratio`. API сразу вернет ID генерации. Опрашивайте `/tryon/apparel/:id` каждые 3 секунды до `status === 'completed'`.

  5. 05

    Шаг 5

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

    Объект ответа содержит `output_url` и `thumbnail_url`. Скачайте файл один раз и сохраните в своем хранилище — URL стабильны, но ваш продукт не должен зависеть от CDN Photta для отображения контента.

Код от и до

Copy, paste, done.

Four snippets — install prerequisites, wrap the REST call, submit + poll, then handle the errors that actually happen in production.

01No SDK required — use native fetch in Node 18+
bash
# Node 18+ ships fetch natively. No install step needed.
node --version   # ensure v18 or later

# Optional: keep your API key out of source control
echo "PHOTTA_API_KEY=photta_live_xxxxx" >> .env
02Wrap the REST call in a tiny client helper
javascript
// photta.js — a 20-line wrapper you can reuse across your app.
const PHOTTA_BASE_URL = "https://ai.photta.app/api/v1";

export async function phottaFetch(path, init = {}) {
  const res = await fetch(`${PHOTTA_BASE_URL}${path}`, {
    ...init,
    headers: {
      "Authorization": `Bearer ${process.env.PHOTTA_API_KEY}`,
      "Content-Type": "application/json",
      ...init.headers,
    },
  });
  const body = await res.json();
  if (!res.ok) {
    const err = new Error(body?.error?.message ?? res.statusText);
    err.status = res.status;
    err.code = body?.error?.code;
    throw err;
  }
  return body;
}
03Submit a try-on job and poll until it lands
javascript
import { phottaFetch } from "./photta.js";

// 1. Submit the job — returns a generation ID immediately.
const created = await phottaFetch("/tryon/apparel", {
  method: "POST",
  body: JSON.stringify({
    product_type: "dress",
    product_images: ["https://example.com/dress.jpg"],
    mannequin_id: "mnq_athena_ts",
    pose_id: "pose_standing_front",
    resolution: "2K",
    aspect_ratio: "3:4",
  }),
});
const generationId = created.data.id;

// 2. Poll every 3 seconds until processing completes. Typical
// completion is 1.5–4 minutes; put an upper bound so a stuck
// job can't hang your request forever.
const pollInterval = 3000;
const maxAttempts = 120;     // 120 × 3s = 6 minutes
let result;

for (let i = 0; i < maxAttempts; i++) {
  result = await phottaFetch(`/tryon/apparel/${generationId}`);
  if (result.data.status === "completed") break;
  if (result.data.status === "failed") {
    throw new Error(result.data.error_message);
  }
  await new Promise((r) => setTimeout(r, pollInterval));
}

console.log("Result:", result.data.output_url);
04Handle 402 credit exhaustion and 429 rate limits
javascript
try {
  await phottaFetch("/tryon/apparel", {
    method: "POST",
    body: JSON.stringify({ /* … */ }),
  });
} catch (err) {
  if (err.status === 402) {
    // Out of credits — surface a top-up CTA to the user.
    // err.code === "insufficient_credits"
  } else if (err.status === 429) {
    // Rate-limited. Honour the Retry-After header.
    const retryAfter = err.retryAfter ?? 30;
    await new Promise((r) => setTimeout(r, retryAfter * 1000));
    // Then retry…
  } else if (err.status >= 500) {
    // Server-side issue — backoff + retry.
  }
  throw err;
}

Почему такая структура

Почему хелпер лучше библиотеки — на данный момент

  • Node 18+ ships native fetch — no runtime deps required
  • Same helper works inside Next.js route handlers, API routes, server actions and edge functions
  • Keep API keys in env; never import the helper from a 'use client' module or the bundler will leak them
  • Works with any queue/cron: BullMQ, Inngest, Trigger.dev, Vercel Cron, Cloudflare Queues

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

Honest caveats

  • No official @photta/node SDK yet — REST is the only path today
  • No built-in webhook delivery; polling is the documented pattern (3–5s interval)
  • Bearer token lives in env; the API doesn't support OAuth client credentials yet

Вопросы других разработчиков

Questions other developers ask

Есть ли официальный Photta Node.js SDK?+

Пока нет. Мы планируем выпуск SDK при достижении порогов использования: Python после 10 активных клиентов, Node.js после 20. До этого момента рекомендуемый путь — прямой REST через cURL, fetch или requests. Хелпер на этой странице полностью заменяет клиент для основных задач.

Какие версии Node.js поддерживаются?+

Любые, где есть встроенный `fetch` (Node 18 и выше). В Node 16 используйте полифилл `node-fetch` или обновитесь. Код написан на чистом ECMAScript 2020 (async/await, шаблоны строк), других требований к среде нет.

Как проходит аутентификация в Node?+

Передавайте заголовок `Authorization: Bearer photta_live_xxx` в каждом запросе. В продакшне ключ начинается с `photta_live_`, в песочнице — с `photta_test_`. Используйте `process.env.PHOTTA_API_KEY`. API пока не поддерживает OAuth или сессионную аутентификацию.

Как обрабатывать ожидание в 1.5–4 минуты?+

Эндпоинт примерки полностью асинхронен. POST сразу возвращает ID с `status: 'processing'`. Опрашивайте `GET /tryon/apparel/:id` каждые 3–5 секунд. Установите лимит итераций, чтобы запрос не висел вечно — 120 попыток по 3 секунды достаточно.

Можно ли вызывать API внутри Next.js?+

Да — из route handler (`app/**/route.ts`), server action (`'use server'`) или middleware. Держите ключ на сервере. Если задача длится дольше таймаута функций Vercel, перенесите опрос в очередь (Inngest, Trigger.dev, BullMQ) и верните браузеру ID генерации для проверки статуса через ваш эндпоинт.

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

Ответы, отличные от 2xx, содержат JSON с объектом `error`: `type`, `code`, `message` и ID запроса для поддержки. Типичные случаи: 400 invalid_request_error с указанием поля в `param`; 402 insufficient_credits; 429 rate_limit_exceeded с заголовком Retry-After. Ошибки 5xx можно повторять с экспоненциальной задержкой.

Node.js · Virtual Try-On API

Создайте аккаунт и получите API-ключ

Установите `PHOTTA_API_KEY`, напишите хелпер `phottaFetch()` для вызовов `https://ai.photta.app/api/v1` с `Authorization: Bearer photta_live_xxx`, отправьте POST на `/tryon/apparel` с URL товара и ID манекена, затем опрашивайте `/tryon/apparel/:id` каждые 3 секунды до `status === 'completed'`. Обычно это занимает 1.5–4 минуты.

Virtual Try-On API для Node.js — Photta | Photta