Node.js · Virtual try-on API

Virtual try-on API trong Node.js

Tóm tắt trong một câu

Thiết lập PHOTTA_API_KEY trong env, viết một helper fetch ngắn cho https://api.photta.app/api/v1 kèm key Bearer, POST tới /api/v1/tryon/apparel, sau đó poll /api/v1/tryon/apparel/{id} mỗi 3 giây cho đến khi status là completed.

  • Thời gian điển hình: 1.5–4 min
  • Credits: 5 credits ở 2K · 6 credits ở 4K
  • Xử lý: async, polling
  • Giới hạn: 600 requests/min
  • Xác thực: Bearer key

Mọi số liệu trên trang này đều lấy từ tài liệu Photta API · đã kiểm tra

POST/api/v1/tryon/apparel
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");
}

Tổng quan nhanh

Tổng quan nhanh

Endpoint
POST /api/v1/tryon/apparel
Xác thực
Bearer API key
Xử lý
Bất đồng bộ — polling status endpoint
Thời gian điển hình
1.5–4 min
Credits
5 credits ở 2K · 6 credits ở 4K
Tỷ lệ khung hình
3:4 · 1:1 · 4:3 · 9:16 · 16:9
Giới hạn đầu vào
Tối đa 10 MB mỗi ảnh; JPEG, PNG, WebP
Idempotent
Không

Cơ chế hoạt động

Năm bước, mất khoảng năm phút từ khi đăng ký đến bức ảnh đầu tiên.

  1. Bước 1

    Đăng ký và tạo key

    Tạo tài khoản tại ai.photta.app. Mở mục Developers trong dashboard, nhấp vào Generate API key và lấy live key. Key bắt đầu bằng `photta_live_` theo sau là 32 ký tự hex.

  2. Bước 2

    Lưu key trong env, không để trong source code

    Thêm `PHOTTA_API_KEY=...` vào file `.env` (hoặc kho lưu trữ secret trên nền tảng của bạn) và nạp qua `process.env`. Tuyệt đối không commit key; không import key vào file có đánh dấu `'use client'` — trình đóng gói sẽ kéo nó ra phía browser.

    node --version   # 18 or later ships fetch
    echo "PHOTTA_API_KEY=photta_live_xxxxx" >> .env
  3. Bước 3

    Viết một helper fetch nhỏ gọn

    Một wrapper 20 dòng bọc quanh `fetch()` xử lý URL gốc, header Authorization, body JSON và chuẩn hóa lỗi. Đây là lớp trừu tượng duy nhất bạn cần cho đến khi SDK chính thức được phát hành.

    // 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. Bước 4

    Gửi yêu cầu try-on và poll

    POST tới `/api/v1/tryon/apparel` với body mẫu theo tài liệu — mã nguồn bên dưới chứa đầy đủ mọi trường. API trả về ID tạo ảnh ngay lập tức. Poll `/api/v1/tryon/apparel/{id}` mỗi 3 giây cho đến khi `status === 'completed'`.

    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");
    }
  5. Bước 5

    Lưu trữ kết quả

    Payload hoàn thành bao gồm `output_url` và `thumbnail_url`. Fetch dữ liệu một lần và lưu vào object storage của riêng bạn — các URL này ổn định nhưng sản phẩm của bạn không nên phụ thuộc vào CDN của Photta để hiển thị.

Lỗi

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/apparel", { method: "POST", 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."
  }) });
} 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;
}

Giới hạn

  • 600 request mỗi phút trên mỗi API key (Fixed window; giới hạn như nhau trên mọi gói)
  • Mã 429 kèm header Retry-After: 30 giây
  • Tối đa 10 MB mỗi ảnh
  • Định dạng: JPEG, PNG, WebP
  • Batch đồng thời theo từng gói: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
  • Chỉ hỗ trợ polling — API không gửi webhook.

Vì sao dùng helper tốt hơn thư viện client — vào lúc này

  • Node 18+ tích hợp sẵn fetch gốc — không phụ thuộc runtime
  • Cùng một helper hoạt động tốt trong Next.js route handlers, server actions và bất kỳ worker nào
  • Lưu API key trong env; không bao giờ import helper từ module 'use client' nếu không bundler sẽ làm lộ key
  • Tương thích với mọi queue hoặc cron: BullMQ, Inngest, Trigger.dev, Vercel Cron, Cloudflare Queues

Những gì API không hỗ trợ

  • Chưa có package @photta/node chính thức — hiện tại REST là cách duy nhất
  • Polling là tín hiệu hoàn thành duy nhất; API không gửi webhook
  • Chỉ dùng khóa Bearer; không có luồng OAuth client-credentials

Câu hỏi thường gặp từ developer

Có SDK Photta chính thức cho Node.js không?

Chưa có. Cách thức theo tài liệu là REST thuần qua fetch gốc của Node — helper trên trang này là toàn bộ phạm vi tích hợp, và hoạt động tương tự cho mọi capability.

Những phiên bản Node.js nào được hỗ trợ?

Bất kỳ phiên bản nào hỗ trợ `fetch` gốc — nghĩa là Node 18 trở lên. Trên Node 16 bạn phải dùng polyfill với `node-fetch` hoặc nâng cấp. Phần mã còn lại là ECMAScript 2020 thuần (async/await, template literals, dynamic imports) nên không có yêu cầu runtime nào khác.

Xác thực từ Node như thế nào?

Gửi `Authorization: Bearer photta_live_xxx` trong mỗi request. Đặt key trong biến môi trường và nạp qua `process.env`; không bao giờ import key từ file chạy trên trình duyệt.

Xử lý khoảng thời gian tạo ảnh thế nào?

Endpoint tạo ảnh hoàn toàn bất đồng bộ. POST trả về ID tạo ảnh ngay lập tức với `status: 'processing'`. Poll `GET /api/v1/tryon/apparel/{id}` mỗi 3–5 giây cho đến khi `status` chuyển sang `completed` hoặc `failed`, và giới hạn số lần thử để tác vụ bị treo không làm nghẽn request.

Tôi có thể gọi API bên trong Next.js không?

Được — từ route handler (`app/**/route.ts`), server action (`'use server'`), hoặc middleware. Giữ API key trên server; không bao giờ import helper từ file `'use client'`. Đối với các tác vụ chạy lâu vượt quá timeout hàm của Vercel, hãy chuyển việc polling sang một queue (Inngest, Trigger.dev, BullMQ) và trả ID tạo ảnh về trình duyệt để client tự poll endpoint trạng thái của bạn.

API báo lỗi như thế nào?

Các phản hồi non-2xx trả về JSON body kèm object `error`: `type`, `code`, `message` và ID request để hỗ trợ. Các trường hợp thường gặp: 400 invalid_request_error với `param` chỉ ra trường không hợp lệ; 402 insufficient_credits kèm số lượng `required` và `available`; 429 rate_limit_exceeded kèm `retry_after` tính bằng giây và header Retry-After tương ứng. Các lỗi 5xx có thể thử lại an toàn với exponential backoff.

Các trang liên quan

Mọi số liệu trên trang này đều lấy từ tài liệu Photta API · đã kiểm tra

Thử nghiệm API trong năm phút

Tạo một Photta API key, dán đoạn mã cURL ở trên, và bạn sẽ nhận được bức ảnh mẫu mặc đồ đầu tiên trước khi pha xong ly cà phê.

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