非同期画像生成APIを使用すると、生成リクエストを送信して即座にジョブ識別子を受け取り、HTTP接続を開いたまま待機することなく、後から完成した画像を取得できます。本チュートリアルでは、ジョブのライフサイクル、ポーリングとWebhookの使い分け、そして低速なジョブや失敗したジョブが製品体験を損なわないようにするためのリトライ設計について解説します。
重要なポイント
- 画像生成はリクエスト・レスポンス型ではなくジョブベースで行われます。これは、生成レイテンシ(数秒から数十秒)が長く、同期接続を維持するには信頼性が低いためです。
- ポーリングは構築やデバッグが容易です。Webhookはレイテンシとリクエスト数を削減できますが、公開エンドポイント、署名検証、および重複配信に対するべき等(idempotent)な処理の実装が必要です。
- リトライロジックは、送信失敗、ジョブのスタック、Webhook配信の失敗を区別する必要があります。それぞれ異なる復旧パスが必要です。
- 正確なエンドポイント名、フィールド名、Webhookペイロードの形式は、プロバイダーやTokenLab独自のAPIサーフェスによって異なります。実装前に必ず docs.tokenlab.sh で最新の仕様を確認してください。
なぜ画像生成APIは非同期なのか
テキスト補完APIは、トークン生成が高速でストリーミング可能なため、同じ接続でレスポンスを返すことが可能です。一方、画像生成モデルは、拡散ベースか自己回帰ベースかを問わず、通常は処理時間が長く、解像度、モデルの選択、キューの深さによってレイテンシが大きく変動します。同期HTTPリクエストを数十秒間維持することは非常に不安定です。クライアントのタイムアウト、ロードバランサーのアイドル制限、モバイルネットワークの切断などにより、生成コストを支払ったにもかかわらず完成した結果を失う可能性が高まります。
画像生成プロバイダー全体で採用されている標準的なパターンはジョブモデルです。リクエストを送信してジョブ識別子と初期ステータス(通常は queued や processing など)を受け取ります。その後、ステータスエンドポイントをポーリングするか、ジョブが終了状態に達したときにWebhook通知を受け取り、別の呼び出しで最終的な画像のURLやバイナリデータを取得します。
TokenLabは、Nano Banana 2、Nano Banana Pro、および Nano Banana 2 Lite ファミリー、GPT Image 2、Reve 2.0、MAI-Image-2.5を含む複数の画像モデルへのアクセスを単一のAPIサーフェスを通じて提供しています。現在のリストについては画像モデルディレクトリを、TokenLab固有のジョブエンドポイントの動作については非同期画像生成タスクガイドを参照してください。以下の一般的なパターンは、呼び出す基盤モデルに関係なく適用されますが、正確なフィールド名やステータスの値は docs.tokenlab.sh に記載されているため、この記事の内容を前提とせず、必ずそちらで確認してください。
ジョブのライフサイクル:送信、ポーリング、取得
概念レベルでは、非同期画像ジョブには3つの段階があります:
- 送信 (Submit): プロンプトとパラメータをPOSTし、ジョブIDと初期ステータスを受け取る。
- ステータス確認 (Check status): ジョブIDを使用してGETエンドポイントをポーリングするか、Webhookイベントを待機する。
- 出力取得 (Retrieve output): ステータスが終了(成功または失敗)したら、画像URLまたはエラー詳細を取得する。
以下は、Pythonによるポーリングパターンの例です。エンドポイントのパスとフィールド名はプレースホルダーとして扱ってください。本番環境で使用する前に、APIドキュメントで現在のTokenLabジョブエンドポイントの形式を確認してください。
import time
import requests
API_BASE = "https://api.tokenlab.sh/v1" # docs.tokenlab.sh で現在のベースURLを確認してください
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def submit_image_job(prompt, model="nano-banana-2"):
resp = requests.post(
f"{API_BASE}/images/jobs",
headers=HEADERS,
json={"prompt": prompt, "model": model, "idempotency_key": generate_key()},
)
resp.raise_for_status()
return resp.json()["job_id"]
def poll_job(job_id, max_wait_seconds=120, interval=2, backoff=1.5):
waited = 0
while waited < max_wait_seconds:
resp = requests.get(f"{API_BASE}/images/jobs/{job_id}", headers=HEADERS)
resp.raise_for_status()
data = resp.json()
if data["status"] in ("succeeded", "failed"):
return data
time.sleep(interval)
waited += interval
interval = min(interval * backoff, 15)
raise TimeoutError(f"Job {job_id} did not complete within {max_wait_seconds}s")
job_id = submit_image_job("a studio product shot on white background")
result = poll_job(job_id)
if result["status"] == "succeeded":
image_url = result["output"]["url"]
else:
print("job failed:", result.get("error"))
送信呼び出しにおける idempotency_key は重要です。ジョブ作成後にネットワークエラーが発生し、クライアントがジョブIDを受け取る前に失敗した場合、同じキーで送信呼び出しをリトライすることで、重複した生成を行わずに既存のジョブを取得できるはずです。これがプロバイダー間で一般的ですが普遍的ではないパターンであるため、TokenLabのジョブエンドポイントがべき等キーをサポートしているかどうか、現在のドキュメントを確認してください。
ポーリング vs. Webhook:トレードオフ
どちらのアプローチも有効であり、適切な選択はトラフィックパターンとインフラストラクチャに依存します。
ポーリングは実装とローカルでのテストが容易で、公開エンドポイントを必要とせず、数秒のレイテンシが許容される低ボリュームやバッチ処理に適しています。欠点は、ポーリング間隔に依存する最小レイテンシが発生することと、長時間実行されるジョブに対して過剰にポーリングを行うと不要なリクエスト数が増加することです。
Webhookはジョブの状態が変化したときにサーバーへ通知をプッシュするため、レイテンシを低減し、無駄なステータス確認呼び出しを削減できます。コストは運用面にあります。公開可能なHTTPSエンドポイント、ペイロードがプロバイダーから送信されたことを確認するための署名検証、および重複または順序不同の配信に対する処理が必要になります。
OpenAIのWebhookイベントリファレンスは、非同期操作におけるこのパターンの一般的な形式を示しています。エンドポイントはタイプとオブジェクト識別子を含むイベントを受け取ります。推奨されるプラクティスは、Webhookのボディを最終的な真実のソースとして信頼するのではなく、APIを通じてリソースの現在の状態を取得するための通知として扱うことです。この「プッシュ後のプル」パターンは、どの画像プロバイダーを統合する場合でも採用する価値があります。Webhookペイロードが切り捨てられたり、遅延したり、複数回配信されたりした場合でも保護されるためです。
Webhookを安全に実装する
画像ジョブの完了にWebhookを選択する場合、以下のプラクティスによりサイレントエラーの可能性を低減できます:
- 署名を検証する:すべての着信Webhookリクエストを処理する前に署名を検証してください。一致しないものは拒否し、拒否ログを通常のトラフィックとは別に記録することで、設定ミスを迅速に特定できるようにします。
- 高速に応答し、後で処理する:検証が完了したらすぐに200ステータスでWebhookに応答し、実際の作業(画像の取得、ストレージへの書き込み、ユーザーへの通知)はバックグラウンドジョブやキューに委ねてください。プロバイダーは通常、タイムリーな2xx応答がない場合にWebhook配信をリトライするため、ハンドラーが低速で同期的な場合、重複処理が発生する可能性があります。
- ジョブIDで重複排除する:処理済みのジョブID(またはイベントのハッシュ)を保存し、リトライされた配信によって通知が再生成されたり、ファイル書き込みが再実行されたりしないようにします。
- リソースを再取得する:前述の「プッシュ後のプル」パターンに従い、Webhookペイロードに含まれる出力URLを必ずしも最終的なものと信頼せず、ジョブIDを使用してリソースを再取得してください。
最小限のハンドラーの例:
from flask import Flask, request, abort
app = Flask(__name__)
processed_job_ids = set() # 本番環境では永続的なストアを使用してください
@app.route("/webhooks/image-jobs", methods=["POST"])
def handle_webhook():
if not verify_signature(request):
abort(401)
event = request.get_json()
job_id = event.get("job_id") or event.get("data", {}).get("id")
if job_id in processed_job_ids:
return "", 200 # 処理済みのため、スキップして応答
enqueue_background_task("fetch_and_store_image", job_id)
processed_job_ids.add(job_id)
return "", 200
画像ジョブ完了に使用される正確なWebhookイベント名、ペイロード構造、署名ヘッダーについては、現在のプロバイダーのドキュメント、および docs.tokenlab.sh に記載されているTokenLab独自のWebhookサポートを確認してください。これらの詳細はプロバイダー固有であり、変更される可能性があるためです。
リトライ設計:3つの失敗クラス
非同期画像ジョブの失敗には3つの明確な種類があり、それぞれに独自の対応が必要です:
- 送信失敗:ジョブ作成のためのPOSTが4xxまたは5xxを返す。5xxやネットワークエラーの場合は、指数バックオフとジッターを組み合わせてリトライし、同じべき等キーを再利用して重複ジョブを作成しないようにします。4xxエラー(不正なプロンプト、無効なモデル、クォータ超過)の場合は、リクエストを変更せずにリトライしても失敗するため、呼び出し元にエラーを伝達してください。
- スタックしたジョブ:ジョブが予想される生成時間を過ぎても終了状態にならない場合。モデルごとに最大待機しきい値を設定し(生成時間はモデルや解像度によって異なります)、プロバイダーが正式に失敗とマークしていなくても、アプリケーションの目的上は失敗として扱います。スタックしたジョブの増加はプロバイダー側のインシデントを示すことが多いため、これらは個別にログを記録してください。
- Webhook配信の失敗:エンドポイントがダウンしていたか、配信がドロップされ、イベントが届かない場合。これが、Webhookファーストの設計であってもポーリングによるフォールバックを維持する理由です。終了状態にならず数分以上経過したジョブのステータスをチェックする定期的なスイープにより、Webhookが届かなかったジョブを捕捉できます。
決定チェックリスト
画像生成機能のジョブ完了処理を設計する際は、このチェックリストを使用してください。
| シナリオ | 推奨アプローチ | 理由 |
|---|---|---|
| 低ボリューム、社内ツール、またはバッチスクリプト | ポーリング | 構築が最も簡単。公開エンドポイント不要 |
| レイテンシが重要なユーザー向け機能 | Webhook + ポーリングによるフォールバック | 低レイテンシ。フォールバックで配信漏れを捕捉 |
| 高ジョブボリューム(1日数千件) | Webhook | 過剰なステータス確認リクエストを回避 |
| 公開HTTPSエンドポイントを公開できない | ポーリング | Webhookには到達可能な受信側が必要 |
| 厳密な重複防止が必要 | 送信時にべき等キーを使用、受信時にジョブIDで重複排除 | リトライされた送信や重複Webhook配信を保護 |
| 1つのパイプラインで複数の画像モデルを使用 | 独自のレイヤーでジョブステータスとエラー処理を正規化 | 基盤プロバイダー(画像モデル比較を参照)はステータスの分類を共有していない |
制限事項
本記事は非同期画像ジョブAPIの一般的なパターンを説明するものであり、TokenLabや特定の基盤モデルプロバイダーについて、上記で引用された以外の正確なエンドポイントパス、フィールド名、タイムアウト値、Webhookイベント名を保証するものではありません。ジョブステータスの語彙、Retry-Afterヘッダー、Webhook署名スキームはプロバイダー間で異なり、時間の経過とともに変更される可能性があります。本記事のコードはコピー&ペースト可能な本番コードではなく、例示として扱い、実装前に docs.tokenlab.sh で現在のリクエストおよびレスポンスの形式を確認してください。本記事では、特定のモデルの価格、レート制限、スループット保証については扱っていません。
FAQ
常にポーリングではなくWebhookを使用すべきですか? いいえ。Webhookはレイテンシとリクエスト数を削減しますが、運用コストが高くなります。低ボリュームや社内ユースケースでは、ポーリングの方がシンプルで信頼性が高い場合も多いです。多くの本番システムでは、Webhookをメインパスとし、定期的なポーリングスイープをフォールバックとして使用しています。
リトライ時に画像の重複生成を避けるにはどうすればよいですか? ジョブ送信リクエストにべき等キーを使用し、ネットワーク障害後のリトライPOSTが新しいジョブを作成するのではなく、既存のジョブを返すようにします。これに依存する前に、プロバイダーのジョブ作成エンドポイントがこれをサポートしているか確認してください。
ジョブ完了時にWebhookエンドポイントがダウンしていたらどうなりますか? 動作はプロバイダーに依存します。一定期間配信をリトライするものもあれば、再配信を保証しないものもあります。プロバイダーのリトライポリシーに関係なく、終了ステータスがない数分以上前のジョブを定期的にポーリングで確認することは実用的な保護策です。
画像生成機能を構築しており、複数のモデルにわたるジョブベースのアクセスを1つのAPIで比較したい場合は、画像モデルディレクトリと非同期画像生成タスクガイドを確認し、Get StartedからTokenLabのAPIドキュメントを参照して、構築に必要なエンドポイントとWebhookの詳細を確認してください。
出典
価格確認日 2026-07-14
- OpenAI webhook events2026-07-14 時点で確認
- TokenLab API documentation2026-07-14 時点で確認
- TokenLab model directory2026-07-14 時点で確認



