Node.js · 幽灵模特 API

幽灵模特 API Node.js 环境

一句话总结

在环境变量中设置 PHOTTA_API_KEY,为 https://api.photta.app/api/v1 编写一个带有 Bearer 认证的简易 fetch 辅助函数,POST 到 /api/v1/ghost-mannequin,随后每 3 秒轮询一次 /api/v1/ghost-mannequin/{id} 直到状态变为 completed。

  • 常规耗时: 1.5–4 min
  • Credits: 2K 分辨率 4 credits · 4K 分辨率 5 credits
  • 处理方式: 异步,轮询
  • 限制: 600 次请求/分钟
  • 身份验证: Bearer key

本页所有数据均来自 Photta API 文档 · 已验证

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");
}

概览

概览

端点
POST /api/v1/ghost-mannequin
身份验证
Bearer API 密钥
处理方式
异步 — 轮询状态端点
常规耗时
1.5–4 min
Credits
2K 分辨率 4 credits · 4K 分辨率 5 credits
宽高比
3:4 · 1:1 · 4:3 · 9:16 · 16:9
输入限制
单张图片最大 10 MB;支持 JPEG, PNG, WebP
幂等性
否

工作原理

从注册到输出第一张图片,只需五步,大约五分钟。

  1. 步骤 1

    注册并生成 key

    在 ai.photta.app 创建账号。打开控制台的 Developers 区域,点击 Generate API key,获取正式环境 key。它以 `photta_live_` 开头,后接 32 位十六进制字符。

  2. 步骤 2

    将 key 存入环境变量,切勿写入源码

    将 `PHOTTA_API_KEY=...` 添加到 `.env`(或你平台的密钥管理服务中),并通过 `process.env` 读取。切勿将 key 提交到版本库;切勿将其导入带有 `'use client'` 标记的文件——打包工具会将其暴露到浏览器端。

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

    编写一个微型 fetch 辅助函数

    一个 20 行左右的 `fetch()` 包装函数即可处理 Base URL、Authorization 请求头、JSON 请求体和错误规范化。在官方 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. 步骤 4

    提交 try-on 任务并轮询

    使用文档示例中的请求体向 `/api/v1/ghost-mannequin` 发送 POST 请求——下方的代码包含了所有字段。API 会立即返回一个生成 ID。每隔 3 秒轮询一次 `/api/v1/ghost-mannequin/{id}`,直到 `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. 步骤 5

    持久化存储结果

    完成状态的 payload 包含 `output_url` 和 `thumbnail_url`。拉取一次字节流并存入你自己的对象存储中——这些 URL 虽是长期有效的,但你的产品不应依赖 Photta 的 CDN 来进行渲染展示。

错误

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;
}

限制

  • 每个 API 密钥每分钟 600 次请求 (固定窗口;各套餐限制相同)
  • 429 响应包含 Retry-After: 30 秒
  • 单张图片最大 10 MB
  • 格式:: JPEG, PNG, WebP
  • 各套餐并发批处理数:: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
  • 仅支持轮询 — API 不发送 Webhook。

为什么辅助函数目前优于客户端 SDK

  • Node 18+ 内置原生 fetch——零运行时依赖
  • 同一个辅助函数可在 Next.js 路由处理程序、Server Actions 以及任何 Worker 中直接运行
  • 将 API Key 保留在环境变量中;切勿在 'use client' 模块中导入该辅助函数,否则打包器会将其泄露
  • 兼容任何队列系统或定时任务:BullMQ、Inngest、Trigger.dev、Vercel Cron、Cloudflare Queues

暂不支持的功能

  • 尚无官方 @photta/node 包——目前 REST 是唯一的接入方式
  • 轮询是唯一的完成信号;API 不发送 Webhook
  • 仅支持 Bearer Key;没有 OAuth 客户端凭据流程

开发者常见问题

是否有官方的 Photta Node.js SDK?

暂无。文档推荐的做法是通过 Node 原生 fetch 调用底层 REST API——本页提供的辅助函数就是完整的集成实现,且适用于所有功能特性。

支持哪些 Node.js 版本?

任何支持原生 `fetch` 的版本——即 Node 18 及更高版本。在 Node 16 上,你需要使用 `node-fetch` 打 polyfill 或直接升级环境。其余代码均为纯 ECMAScript 2020(async/await、模板字符串、动态导入),无其他运行时要求。

如何从 Node 进行身份验证?

在每个请求中携带 `Authorization: Bearer photta_live_xxx`。将密钥存入环境变量并通过 `process.env` 读取;切勿在浏览器端运行的文件中导入它。

如何处理生成等待窗口?

生成 endpoint 完全采用异步设计。POST 请求会立即返回一个带有 `status: 'processing'` 的生成 ID。每隔 3–5 秒轮询一次 `GET /api/v1/ghost-mannequin/{id}`,直到 `status` 变为 `completed` 或 `failed`,并设置最大重试次数以防卡住的任务导致请求无限挂起。

我可以在 Next.js 内部调用此 API 吗?

可以——在路由处理程序(`app/**/route.ts`)、Server Action(`'use server'`)或中间件中调用。务必将 API Key 保留在服务端;切勿在 `'use client'` 文件中导入该辅助函数。对于执行时间超过 Vercel 函数超时的长任务,可将轮询交由队列系统(Inngest、Trigger.dev、BullMQ)处理,并将生成 ID 返回给浏览器,让前端轮询你自己的状态 endpoint。

API 如何返回错误信号?

非 2xx 响应会返回一个包含 `error` 对象的 JSON 请求体:`type`、`code`、`message` 以及用于支持排查的请求 ID。常见情况:400 invalid_request_error,附带指向错误字段的 `param`;402 insufficient_credits,附带 `required` 和 `available` 计数;429 rate_limit_exceeded,附带以秒为单位的 `retry_after` 及对应的 Retry-After 标头。5xx 错误可安全使用指数退避策略重试。

五分钟内上手试用 API

生成 Photta API key,粘贴上方的 cURL 代码片段,咖啡还没煮好,你的第一张模特实穿图就已经生成完毕。

  • 1.5–4 min
  • 2K 分辨率 4 credits · 4K 分辨率 5 credits
  • 异步,轮询
  • 600 次请求/分钟
  • Bearer key