Node.js · API try-on perhiasan

API try-on perhiasan di Node.js

Ringkasan singkat

Set PHOTTA_API_KEY di env, tulis helper fetch kecil untuk https://api.photta.app/api/v1 dengan kunci Bearer, POST ke /api/v1/tryon/jewelry, lalu lakukan polling ke /api/v1/tryon/jewelry/{id} setiap 3 detik hingga status completed.

  • Waktu umum: 1.5–4 min
  • Credits: 5 credits pada 2K · 6 credits pada 4K
  • Pemrosesan: async, polling
  • Batasan: 600 requests/min
  • Autentikasi: Bearer key

Setiap angka di halaman ini bersumber dari dokumentasi API Photta · diperiksa

POST/api/v1/tryon/jewelry
const res = await fetch("https://api.photta.app/api/v1/tryon/jewelry", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "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."
  }),
});
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/jewelry/${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");
}

Sekilas

Sekilas

Endpoint
POST /api/v1/tryon/jewelry
Autentikasi
API key Bearer
Pemrosesan
Asinkron — lakukan polling ke endpoint status
Waktu umum
1.5–4 min
Credits
5 credits pada 2K · 6 credits pada 4K
Rasio aspek
3:4 · 1:1 · 4:3 · 9:16 · 16:9
Batas input
Hingga 10 MB per gambar; JPEG, PNG, WebP
Idempoten
Tidak

Cara kerja

Lima langkah, sekitar lima menit dari pendaftaran hingga gambar pertama.

  1. Langkah 1

    Daftar dan buat kunci

    Buat akun di ai.photta.app. Buka bagian Developers di dashboard, klik Generate API key, dan ambil live key tersebut. Kunci ini dimulai dengan `photta_live_` diikuti oleh 32 karakter heksadesimal.

  2. Langkah 2

    Simpan kunci di env, bukan di source code

    Tambahkan `PHOTTA_API_KEY=...` ke `.env` (atau secret store platform Anda) dan muat melalui `process.env`. Jangan pernah commit kunci tersebut; jangan pernah mengimpornya ke dalam file yang ditandai `'use client'` — bundler akan memasukkannya ke browser.

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

    Tulis helper fetch ringkas

    Satu wrapper 20 baris di sekitar `fetch()` menangani base URL, header Authorization, body JSON, dan normalisasi error. Ini adalah satu-satunya abstraksi yang Anda butuhkan hingga SDK resmi dirilis.

    // 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. Langkah 4

    Kirim try-on dan lakukan polling

    Kirim POST ke `/api/v1/tryon/jewelry` dengan contoh body yang terdokumentasi — kode di bawah mencakup setiap field. API langsung mengembalikan ID generasi. Lakukan polling ke `/api/v1/tryon/jewelry/{id}` setiap 3 detik hingga `status === 'completed'`.

    const res = await fetch("https://api.photta.app/api/v1/tryon/jewelry", {
      method: "POST",
      headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        "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."
      }),
    });
    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/jewelry/${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. Langkah 5

    Simpan hasilnya

    Payload yang selesai mencakup `output_url` dan `thumbnail_url`. Ambil byte-nya sekali dan simpan di object storage Anda sendiri — URL-nya stabil, tetapi produk Anda sebaiknya tidak bergantung pada CDN Photta untuk rendering.

Error

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("/tryon/jewelry", { method: "POST", body: JSON.stringify({
    "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."
  }) });
} 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;
}

Batasan

  • 600 request per menit per API key (Fixed window; batasannya sama di setiap paket)
  • Respons 429 menyertakan Retry-After: 30 detik
  • Hingga 10 MB per gambar
  • Format: JPEG, PNG, WebP
  • Batch konkuren per paket: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
  • Hanya polling — API tidak mengirim webhook.

Mengapa helper lebih baik daripada client library — untuk saat ini

  • Node 18+ menyediakan native fetch — tanpa dependensi runtime
  • Helper yang sama bekerja di dalam route handler Next.js, server action, dan worker apa pun
  • Simpan API key di env; jangan pernah mengimpor helper dari modul 'use client' atau bundler akan membocorkannya
  • Bekerja dengan antrean atau cron apa pun: BullMQ, Inngest, Trigger.dev, Vercel Cron, Cloudflare Queues

Yang tidak dilakukannya

  • Belum ada paket resmi @photta/node — REST adalah satu-satunya jalur saat ini
  • Polling adalah satu-satunya sinyal penyelesaian; API tidak mengirimkan webhook
  • Hanya Bearer key; tidak ada alur client-credentials OAuth

Pertanyaan umum developer

Apakah ada SDK Node.js resmi dari Photta?

Belum ada. Jalur yang terdokumentasi adalah REST mentah via native fetch bawaan Node — helper di halaman ini mencakup seluruh integrasi, dan bekerja sama persis untuk setiap kapabilitas.

Versi Node.js apa saja yang didukung?

Versi apa pun yang mendukung native `fetch` — artinya Node 18 dan yang lebih baru. Pada Node 16, Anda dapat melakukan polyfill dengan `node-fetch` atau melakukan upgrade. Kode lainnya adalah ECMAScript 2020 standar (async/await, template literals, dynamic imports) sehingga tidak ada persyaratan runtime lainnya.

Bagaimana cara mengautentikasi dari Node?

Kirimkan `Authorization: Bearer photta_live_xxx` pada setiap request. Simpan key di environment variable dan muat via `process.env`; jangan pernah mengimpornya dari file yang berjalan di browser.

Bagaimana cara menangani jendela generasi?

Endpoint generasi sepenuhnya asinkron. POST langsung mengembalikan ID generasi dengan `status: 'processing'`. Lakukan polling `GET /api/v1/tryon/jewelry/{id}` setiap 3–5 detik hingga `status` berubah menjadi `completed` atau `failed`, dan batasi jumlah percobaan agar job yang macet tidak menggantung request.

Bisakah saya memanggil API di dalam Next.js?

Bisa — dari route handler (`app/**/route.ts`), server action (`'use server'`), atau middleware. Simpan API key di sisi server; jangan pernah mengimpor helper dari file `'use client'`. Untuk job berdurasi panjang yang melebihi batas function timeout Vercel, alihkan polling ke antrean (Inngest, Trigger.dev, BullMQ) dan kembalikan ID generasi ke browser agar browser dapat melakukan polling ke endpoint status Anda sendiri.

Bagaimana cara API menandai error?

Respons non-2xx memuat body JSON dengan objek `error`: `type`, `code`, `message`, dan request ID untuk bantuan teknis. Kasus umum: 400 invalid_request_error dengan `param` mengarah ke field yang bermasalah; 402 insufficient_credits dengan jumlah `required` dan `available`; 429 rate_limit_exceeded dengan `retry_after` dalam detik beserta header Retry-After yang sesuai. Error 5xx aman untuk di-retry dengan exponential backoff.

Coba API dalam lima menit

Buat Photta API key, tempel cuplikan cURL di atas, dan Anda akan mendapatkan gambar on-model pertama sebelum kopi selesai diseduh.

  • 1.5–4 min
  • 5 credits pada 2K · 6 credits pada 4K
  • async, polling
  • 600 requests/min
  • Bearer key