Python · バーチャルtry-on API

バーチャルtry-on API Pythonでの実装

要約

requests をインストールし、環境変数から PHOTTA_API_KEY をロードし、Bearer キーを送信して 2xx 以外のレスポンスで型付きの PhottaError を発生させる photta() ラッパーを作成します。その後 /api/v1/tryon/apparel に POST し、status が completed になるまで 3秒ごとに /api/v1/tryon/apparel/{id} をポーリングします。

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

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

POST/api/v1/tryon/apparel
import os, requests

res = requests.post(
    "https://api.photta.app/api/v1/tryon/apparel",
    headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
    json={
      "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."
    },
)
data = res.json()["data"]  # data["id"], data["status"]

import time

def wait_for(job_id):
    for _ in range(120):
        res = requests.get(f"https://api.photta.app/api/v1/tryon/apparel/{job_id}", headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"})
        data = res.json()["data"]
        if data["status"] == "completed":
            return data["output_url"]
        if data["status"] == "failed":
            raise RuntimeError(data.get("error_message", "generation failed"))
        time.sleep(3)
    raise TimeoutError("timed out after 6 minutes")

概要

概要

エンドポイント
POST /api/v1/tryon/apparel
認証
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つのステップ、Photta 固有の依存関係はゼロ — requests ですべてカバーできます。

  1. ステップ 1

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

    他のすべての言語と同じ手順です: ai.photta.app → Developers → Generate API key。キーは `photta_live_` で始まり、すべての Photta 機能で使用できます。

  2. ステップ 2

    環境変数からキーをロードする

    環境変数に `PHOTTA_API_KEY` を設定し、`os.environ` 経由で読み込みます。FastAPI や Django では、起動時に設定モジュールへ1度だけロードしてください。ソースコードや公開 Git 履歴にキーを埋め込まないでください。

    python3 -m pip install requests
    export PHOTTA_API_KEY="photta_live_xxxxx"
  3. ステップ 3

    requests ラッパーを作成する

    30行の `photta_request()` 関数が、ベースURL、Authorizationヘッダー、JSONシリアライズ、エラー変換を処理します。Python SDKが登場するまでは、この抽象化だけで十分です。

    # photta.py — one helper for every endpoint
    import os, requests
    BASE = "https://api.photta.app/api/v1"
    
    class PhottaError(Exception):
        def __init__(self, status, error, retry_after=None):
            super().__init__(error.get("message", "request failed"))
            self.status, self.type, self.retry_after = status, error.get("type"), retry_after
    
    def photta(method, path, **kwargs):
        res = requests.request(method, BASE + path, headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"}, timeout=30, **kwargs)
        body = res.json()
        if not res.ok:
            raise PhottaError(res.status_code, body.get("error", {}), res.headers.get("Retry-After"))
        return body
  4. ステップ 4

    送信とポーリング

    `/api/v1/tryon/apparel` にPOSTして生成IDを取得し、3秒間隔で `/api/v1/tryon/apparel/{id}` をポーリングします。CeleryやRQでは、数分かかるジョブでワーカーを占有しないよう、ポーリングループをタスクに分割してください。

    import os, requests
    
    res = requests.post(
        "https://api.photta.app/api/v1/tryon/apparel",
        headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
        json={
          "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."
        },
    )
    data = res.json()["data"]  # data["id"], data["status"]
    
    import time
    
    def wait_for(job_id):
        for _ in range(120):
            res = requests.get(f"https://api.photta.app/api/v1/tryon/apparel/{job_id}", headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"})
            data = res.json()["data"]
            if data["status"] == "completed":
                return data["output_url"]
            if data["status"] == "failed":
                raise RuntimeError(data.get("error_message", "generation failed"))
            time.sleep(3)
        raise TimeoutError("timed out after 6 minutes")
  5. ステップ 5

    結果の永続化

    完了時のペイロードには `output_url` と `thumbnail_url` が含まれます。長期的なレンダリングでプロダクトがPhottaのCDNに依存しないよう、バイトデータを自身のオブジェクトストレージ(S3、GCS、Cloudflare R2など)にダウンロードしてください。

エラー

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:
    photta("POST", "/tryon/apparel", json={
      "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."
    })
except PhottaError as e:
    if e.status == 402:
        print("insufficient_credits_error: top up before retrying")
    elif e.status == 429:
        time.sleep(int(e.retry_after or 30))
    else:
        raise

制限

  • 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を送信しません。

現時点でrequestsが最適な選択肢である理由

  • Photta固有の依存関係は不要 — ほとんどのPythonプロジェクトにrequestsはすでに導入されています
  • Pythonが動作する環境ならどこでも機能: Django、FastAPI、Flask、Celeryワーカー、Lambda、スクリプト
  • クライアントラッパーは約30行 — photta.py に貼り付けるだけですぐに使えます
  • 並行送信が必要な場合はhttpx経由でasyncioと連携可能

対応していない機能

  • 公式のPhotta Pythonパッケージは未提供 — requestsを使用したRESTがドキュメント記載のパターンです
  • 完了シグナルはポーリングのみで、APIからWebhookは送信されません
  • Bearerキーのみ対応。OAuthのクライアント認証情報フローはありません

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

公式のPhotta Python SDKはありますか?

現時点ではありません。サポートされている連携方法はrequestsライブラリ(同期)またはhttpx(非同期)です。どちらも薄いラッパーを介して利用でき、本ページのラッパーはすべての機能に対応しています。

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

Python 3.9以降です。このラッパーは型ヒント、f-string、`str | None` 構文を使用しており、3.10以降で問題なく動作します。3.9の場合は `str | None` を `Optional[str]` に置き換えてください。requests自体は公式に3.8以降をサポートしていますが、近年のPhottaプロジェクトでは3.9以降が実質的な最小要件です。

requestsとhttpxのどちらを使うべきですか?

少数の呼び出しを順次実行する場合はrequestsが適しています。シンプルであり、ほとんどのコードベースにすでに含まれています。非同期の並行処理(並列送信、一括バックフィルなど)やHTTP/2が必要な場合はhttpxを使用してください。どちらの場合もラッパーは軽量で、`requests.request` を `httpx.Client().request` に置き換えるだけで、残りのコードは同一のまま動作します。

Djangoから呼び出せますか?

はい。ラッパーを `services/photta.py` に配置し、ビューまたはCeleryタスクからインポートしてください。ジョブに時間がかかる場合、同期リクエストハンドラー内でAPIを呼び出すのは避けてください。ビューから送信して生成IDを返し、バックグラウンドタスクまたはChannelsのコンシューマーからポーリングします。

Celeryワーカーからポーリングするにはどうすればよいですか?

1つのタスクからジョブを送信し、`apply_async(countdown=3)` を使用して1回ポーリングする後続タスクをスケジュールします。ジョブが完了するか最大再試行回数に達するまで自身を再スケジュールします。これによりワーカーが解放され、オートスケーラーに悪影響を与える長時間タスクを回避できます。

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

2xx以外のレスポンスには、`error` オブジェクト(`type`、`code`、`message`、さらに400番台では `param`、429番台では `retry_after`)を含むJSONボディが返されます。本ページのラッパーはこれらすべてを保持する `PhottaError` を送出するため、呼び出し元はボディ自体をパースすることなくステータスコードで分岐できます。

関連ページ

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

5分でAPIを試す

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

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