Node.js · Ghost mannequin API

Ghost mannequin API w Node.js

W jednym zdaniu

Ustaw PHOTTA_API_KEY w env, napisz prosty helper fetch dla https://api.photta.app/api/v1 z kluczem Bearer, wyślij żądanie POST do /api/v1/ghost-mannequin, a następnie odpytuj /api/v1/ghost-mannequin/{id} co 3 sekundy, aż status zmieni się na completed.

  • Typowy czas: 1.5–4 min
  • Credits: 4 credits w 2K · 5 credits w 4K
  • Przetwarzanie: asynchroniczne, polling
  • Limity: 600 requests/min
  • Uwierzytelnianie: Klucz Bearer

Każda liczba na tej stronie pochodzi z dokumentacji API Photta · sprawdzono

POST/api/v1/ghost-mannequin
const res = await fetch("https://api.photta.app/api/v1/ghost-mannequin", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "product_image": "https://cdn.yourstore.com/products/jacket-front.jpg",
    "back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
    "image_side": "front",
    "angle": "side",
    "style": "3d",
    "background_color": "white",
    "resolution": "2K",
    "aspect_ratio": "3:4",
    "user_instruction": "closed front, cream interior lining",
    "brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
  }),
});
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/ghost-mannequin/${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");
}

W skrócie

W skrócie

Endpoint
POST /api/v1/ghost-mannequin
Uwierzytelnianie
Klucz API Bearer
Przetwarzanie
Asynchroniczne — odpytuj endpoint statusu
Typowy czas
1.5–4 min
Credits
4 credits w 2K · 5 credits w 4K
Proporcje obrazu
3:4 · 1:1 · 4:3 · 9:16 · 16:9
Limity wejściowe
Do 10 MB na obraz; JPEG, PNG, WebP
Idempotentne
Nie

Jak to działa

Pięć kroków, około pięciu minut od rejestracji do pierwszego obrazu.

  1. Krok 1

    Zarejestruj się i wygeneruj klucz

    Utwórz konto na ai.photta.app. Otwórz sekcję Developers w panelu, kliknij Generate API key i pobierz klucz produkcyjny. Zaczyna się od `photta_live_`, po którym następuje 32 znaków szesnastkowych.

  2. Krok 2

    Przechowuj klucz w env, nie w kodzie źródłowym

    Dodaj `PHOTTA_API_KEY=...` do `.env` (lub magazynu sekretów swojej platformy) i ładuj go przez `process.env`. Nigdy nie commituj klucza; nigdy nie importuj go do pliku oznaczonego jako `'use client'` — bundler wyeksponuje go w przeglądarce.

    node --version   # 18 or later ships fetch
    echo "PHOTTA_API_KEY=photta_live_xxxxx" >> .env
  3. Krok 3

    Napisz prosty helper fetch

    Jeden 20-wierszowy wrapper wokół `fetch()` obsługuje bazowy URL, nagłówek Authorization, body JSON oraz normalizację błędów. To jedyna abstrakcja, jakiej potrzebujesz, dopóki nie pojawi się oficjalne SDK.

    // photta.js — one helper for every endpoint
    const BASE = "https://api.photta.app/api/v1";
    export async function photta(path, init = {}) {
      const res = await fetch(BASE + path, { ...init, headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json", ...(init.headers ?? {}) } });
      const body = await res.json();
      if (!res.ok) throw Object.assign(new Error(body.error?.message ?? res.statusText), { status: res.status, retryAfter: res.headers.get("Retry-After"), body });
      return body;
    }
  4. Krok 4

    Wyślij zadanie try-on i wykonaj polling

    Wyślij POST do `/api/v1/ghost-mannequin` z przykładowym body z dokumentacji — poniższy kod zawiera każde pole. API natychmiast zwraca ID generowania. Wykonuj polling do `/api/v1/ghost-mannequin/{id}` co 3 sekundy, aż `status === 'completed'`.

    const res = await fetch("https://api.photta.app/api/v1/ghost-mannequin", {
      method: "POST",
      headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        "product_image": "https://cdn.yourstore.com/products/jacket-front.jpg",
        "back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
        "image_side": "front",
        "angle": "side",
        "style": "3d",
        "background_color": "white",
        "resolution": "2K",
        "aspect_ratio": "3:4",
        "user_instruction": "closed front, cream interior lining",
        "brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
      }),
    });
    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/ghost-mannequin/${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");
    }
  5. Krok 5

    Zapisz wynik na stałe

    Ukończony payload zawiera `output_url` i `thumbnail_url`. Pobierz bajty raz i zapisz je we własnym object storage — adresy URL są stabilne, ale Twój produkt nie powinien zależeć od CDN-u Photta przy renderowaniu.

Błędy

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 {
  await photta("/ghost-mannequin", { method: "POST", body: JSON.stringify({
    "product_image": "https://cdn.yourstore.com/products/jacket-front.jpg",
    "back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
    "image_side": "front",
    "angle": "side",
    "style": "3d",
    "background_color": "white",
    "resolution": "2K",
    "aspect_ratio": "3:4",
    "user_instruction": "closed front, cream interior lining",
    "brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
  }) });
} catch (err) {
  if (err.status === 402) console.error("insufficient_credits_error: top up before retrying");
  else if (err.status === 429) await new Promise((r) => setTimeout(r, Number(err.retryAfter ?? 30) * 1000));
  else throw err;
}

Limity

  • 600 żądań na minutę na klucz API (Stałe okno czasowe; limit jest taki sam w każdym planie)
  • Odpowiedź 429 zawiera nagłówek Retry-After: 30 s
  • Do 10 MB na obraz
  • Formaty: JPEG, PNG, WebP
  • Jednoczesne zadania wsadowe w zależności od planu: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
  • Tylko polling — API nie wysyła webhooków.

Dlaczego helper wygrywa z biblioteką klienta — na ten moment

  • Node 18+ zawiera natywny fetch — zero zależności w runtime
  • Ten sam helper działa wewnątrz route handlerów Next.js, server actions i dowolnego workera
  • Trzymaj klucz API w env; nigdy nie importuj helpera z modułu 'use client', bo inaczej bundler go ujawni
  • Działa z każdą kolejką lub cronem: BullMQ, Inngest, Trigger.dev, Vercel Cron, Cloudflare Queues

Czego nie robi

  • Brak oficjalnego pakietu @photta/node — na dziś REST to jedyna ścieżka
  • Polling jest jedynym sygnałem ukończenia; API nie wysyła webhooków
  • Tylko klucz Bearer; brak przepływu OAuth client-credentials

Pytania zadawane przez programistów

Czy istnieje oficjalne Photta Node.js SDK?

Jeszcze nie. Udokumentowaną ścieżką jest czysty REST przez natywny fetch w Node — helper na tej stronie to cała powierzchnia integracji i działa tak samo dla każdej capability.

Jakie wersje Node.js są obsługiwane?

Wszystkie obsługujące natywny `fetch` — czyli Node 18 i nowsze. Na Node 16 musisz użyć polyfilla z `node-fetch` albo zrobić upgrade. Reszta kodu to zwykły ECMAScript 2020 (async/await, template literals, dynamic imports), więc brak innych wymagań runtime.

Jak uwierzytelniać się z poziomu Node?

Wysyłaj `Authorization: Bearer photta_live_xxx` przy każdym żądaniu. Umieść klucz w zmiennej środowiskowej i wczytuj go przez `process.env`; nigdy nie importuj go z pliku uruchamianego w przeglądarce.

Jak obsłużyć okno generowania?

Endpoint generowania jest w pełni asynchroniczny. POST natychmiast zwraca ID generowania ze statusem `status: 'processing'`. Wykonuj polling `GET /api/v1/ghost-mannequin/{id}` co 3–5 sekund, aż `status` zmieni się na `completed` lub `failed`, i ustaw limit prób, aby zablokowane zadanie nie zawiesiło żądania.

Czy mogę wywoływać API wewnątrz Next.js?

Tak — z poziomu route handlera (`app/**/route.ts`), server action (`'use server'`) lub middleware. Trzymaj klucz API na serwerze; nigdy nie importuj helpera z pliku `'use client'`. W przypadku długo trwających zadań przekraczających timeout funkcji w Vercel, oddeleguj polling do kolejki (Inngest, Trigger.dev, BullMQ) i zwróć ID generowania do przeglądarki, aby mogła odpytywać Twój własny endpoint statusu.

Jak API sygnalizuje błędy?

Odpowiedzi inne niż 2xx zawierają body JSON z obiektem `error`: `type`, `code`, `message` oraz ID żądania dla celów wsparcia. Typowe przypadki: 400 invalid_request_error z parametrem `param` wskazującym na błędne pole; 402 insufficient_credits z liczbami `required` i `available`; 429 rate_limit_exceeded z wartością `retry_after` w sekundach i pasującym nagłówkiem Retry-After. Błędy 5xx można bezpiecznie ponawiać z użyciem exponential backoff.

Powiązane strony

Każda liczba na tej stronie pochodzi z dokumentacji API Photta · sprawdzono

Wypróbuj API w 5 minut

Wygeneruj klucz Photta API, wklej powyższy snippet cURL, a otrzymasz swoje pierwsze zdjęcie na modelu, zanim zaparzy się kawa.

  • 1.5–4 min
  • 4 credits w 2K · 5 credits w 4K
  • asynchroniczne, polling
  • 600 requests/min
  • Klucz Bearer