Node.js · ジュエリー try-on API

ジュエリー try-on API Node.js

要約

環境変数にPHOTTA_API_KEYを設定し、https://api.photta.app/api/v1向けにBearerキーを含むシンプルなfetchヘルパーを作成して/api/v1/tryon/jewelryにPOST後、ステータスがcompletedになるまで3秒ごとに/api/v1/tryon/jewelry/{id}をポーリングします。

  • 所要時間の目安: 1.5–4 min
  • Credits: 2Kで5 credits · 4Kで6 credits
  • 処理方式: 非同期、ポーリング
  • 制限: 600 リクエスト/分
  • 認証: Bearerキー

このページに記載されている数値はすべて以下に基づきます: Photta APIドキュメント · 確認済み

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

概要

概要

エンドポイント
POST /api/v1/tryon/jewelry
認証
Bearer APIキー
処理方式
非同期 — ステータスエンドポイントをポーリング
所要時間の目安
1.5–4 min
Credits
2Kで5 credits · 4Kで6 credits
アスペクト比
3:4 · 1:1 · 4:3 · 9:16 · 16:9
入力制限
1画像あたり最大10 MB、JPEG, PNG, WebP
冪等性
なし

仕組み

サインアップから最初の画像生成まで、5ステップで約5分。

  1. ステップ 1

    サインアップしてキーを生成

    ai.photta.appでアカウントを作成します。ダッシュボードのDevelopersセクションを開き、APIキーの生成をクリックして本番キーを取得します。キーは`photta_live_`で始まり、その後に32文字の16進数が続きます。

  2. ステップ 2

    キーはソースコードではなく環境変数に保存

    `.env`(またはプラットフォームのシークレット管理)に`PHOTTA_API_KEY=...`を追加し、`process.env`経由で読み込みます。キーをコミットしたり、`'use client'`とマークされたファイルにインポートしたりしないでください — バンドラによってブラウザ側に露出してしまいます。

    node --version   # 18 or later ships fetch
    echo "PHOTTA_API_KEY=photta_live_xxxxx" >> .env
  3. ステップ 3

    軽量な fetch ヘルパーを作成する

    `fetch()` をラップする20行のコード1つで、ベース 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/tryon/jewelry` に POST します(以下のコードには全フィールドが含まれています)。API は即座に生成 ID を返します。`status === 'completed'` になるまで、3秒ごとに `/api/v1/tryon/jewelry/{id}` をポーリングしてください。

    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. ステップ 5

    結果を永続化する

    完了時のペイロードには `output_url` と `thumbnail_url` が含まれます。バイトデータを一度 fetch して自前のオブジェクトストレージに保存してください。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("/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;
}

制限

  • APIキーごとに毎分600リクエスト (固定ウィンドウ方式。制限値はすべてのプランで共通です)
  • 429にはRetry-After: 30 secondsが含まれます
  • 1画像あたり最大10 MB
  • フォーマット: JPEG, PNG, WebP
  • プランごとの同時実行バッチ数: Free 1 · Hobby 2 · Seller 3 · Brand 4 · Studio 6
  • ポーリングのみ — APIはWebhookを送信しません。

現時点でヘルパーがクライアントライブラリより優れている理由

  • Node 18+ はネイティブ fetch を標準搭載 — ランタイム依存関係なし
  • 同じヘルパーが Next.js の Route Handlers、Server Actions、およびあらゆるワーカー内で動作
  • API キーは環境変数に保持。バンドラーによる漏洩を防ぐため、'use client' モジュールからは絶対にヘルパーをインポートしないこと
  • あらゆるキューや cron で動作: BullMQ、Inngest、Trigger.dev、Vercel Cron、Cloudflare Queues

対応していない事項

  • 公式 @photta/node パッケージは未提供 — 現時点では REST が唯一の手段
  • 完了通知はポーリングのみ — API は Webhook を送信しません
  • Bearer キーのみ — OAuth クライアント認証情報フローはありません

開発者からのよくある質問

公式の Photta Node.js SDK はありますか?

まだありません。ドキュメント化されている方法は Node のネイティブ fetch による直接の REST 呼び出しです。このページのヘルパーが連携インターフェースのすべてであり、すべての機能で同様に機能します。

サポートされている Node.js のバージョンは?

ネイティブの `fetch` をサポートする環境 — つまり Node 18 以降です。Node 16 では `node-fetch` でポリフィルするか、アップグレードする必要があります。残りのコードはプレーンな ECMAScript 2020(async/await、テンプレートリテラル、動的インポート)であるため、その他のランタイム要件はありません。

Node からの認証方法は?

すべてのリクエストで `Authorization: Bearer photta_live_xxx` を送信します。キーは環境変数に設定し、`process.env` 経由で読み込んでください。ブラウザで実行されるファイルからは絶対にインポートしないでください。

生成待機の処理方法は?

生成エンドポイントは完全な非同期です。POST すると `status: 'processing'` とともに即座に生成 ID が返されます。`status` が `completed` または `failed` に変わるまで、3〜5秒ごとに `GET /api/v1/tryon/jewelry/{id}` をポーリングしてください。ジョブの停止によってリクエストがハングしないよう、試行回数には上限を設けてください。

Next.js 内から API を呼び出せますか?

はい — Route Handler(`app/**/route.ts`)、Server Action(`'use server'`)、または Middleware から呼び出せます。API キーはサーバー側に保持し、`'use client'` ファイルからヘルパーをインポートしないでください。Vercel の関数タイムアウトを超える長時間ジョブの場合は、ポーリングをキュー(Inngest、Trigger.dev、BullMQ)に委譲し、生成 ID をブラウザに返して独自のエンドポイントをポーリングさせてください。

API はどのようにエラーを通知しますか?

2xx 以外のレスポンスには、`error` オブジェクトを含む JSON ボディが付随します: `type`、`code`、`message`、およびサポート用のリクエスト ID です。代表的なケース: 不正なフィールドを示す `param` を伴う 400 invalid_request_error、`required` および `available` のカウントを示す 402 insufficient_credits、秒単位の `retry_after` と一致する Retry-After ヘッダーを伴う 429 rate_limit_exceeded です。5xx エラーは指数バックオフによる再試行が安全です。

関連ページ

このページに記載されている数値はすべて以下に基づきます: Photta APIドキュメント · 確認済み

5分でAPIを試す

Photta APIキーを生成し、上記のcURLスニペットを貼り付ければ、コーヒーが淹れ終わる前に最初のモデル着用画像が手に入ります。

  • 1.5–4 min
  • 2Kで5 credits · 4Kで6 credits
  • 非同期、ポーリング
  • 600 リクエスト/分
  • Bearerキー