非同期の画像と動画:respond-async、ポーリング、Webhook
動画の生成には時間がかかり、画像も同様のことがあります。Hyperouter には、長時間の接続を保持せずに結果を待つ方法が 3 つあります。画像には Prefer: respond-async を送信する、記録をポーリングする、ジョブの終了を Webhook で受け取る、の 3 つです。このガイドでは、それぞれを説明します。
モデルごとの応答方法
GET /v1/models の各モデルは、response_modes を列挙しています。
sync:1 回の保持された応答。async:Prefer: respond-asyncを送信して202を受け取り、ポーリングする。stream:"stream": true。
async のみを列挙するモデルには Prefer ヘッダーが必要です。ヘッダーがない場合は、コード async_required の 400 が返ります。動画は、ヘッダーの有無にかかわらず、Videos API では常に非同期です。
画像:Prefer: respond-async
async を列挙しているモデルでは、このヘッダーを追加します。以下のモデル ID は例です。お使いのモデルの response_modes を確認してください。これは RFC 7240 の仕様のため、繰り返すことも、Prefer: wait=10, respond-async のように他の指定と組み合わせることもできます。
curl -i https://api.hyperouter.app/v1/images/generations \
-H "Authorization: Bearer $HYPEROUTER_API_KEY" \
-H "Content-Type: application/json" \
-H "Prefer: respond-async" \
-d '{"model": "x-ai/grok-imagine-image", "prompt": "A lighthouse at dusk", "n": 1}'
すぐに 202 が返り、レスポンスヘッダー Preference-Applied: respond-async が付きます。
{ "id": "0194f2c4-7a1e-7b3c-9d02-5f6a1c0e8b21", "status": "queued" }
sync で応答するモデルにヘッダーなしで送信すると、画像を最大 90 秒待ちます。その時間内に画像が完成しない場合は、コード timeout、ヘッダー x-should-retry: false、そしてジョブの ID を error.generation_id に含む 503 が返ります。ジョブは継続し、成功した場合にのみ課金されます。その ID を、次のようにポーリングしてください。
画像をポーリングする
GET /v1/generations/{id} は記録を返します。status は queued、submitting、running、done、failed のいずれかです。画像では、outputs に [{"url", "expires_at"}]、つまり 24 時間有効な署名付きリンクが入ります。結果は 30 日間利用できます。
import os
import time
import requests
headers = {"Authorization": f"Bearer {os.environ['HYPEROUTER_API_KEY']}"}
def wait_for_image(generation_id: str, every: float = 3.0, limit: float = 600.0) -> dict:
deadline = time.monotonic() + limit
while time.monotonic() < deadline:
r = requests.get(
f"https://api.hyperouter.app/v1/generations/{generation_id}",
headers=headers,
timeout=30,
)
r.raise_for_status()
rec = r.json()["data"]
if rec["status"] in ("done", "failed"):
return rec
time.sleep(every)
raise TimeoutError(generation_id)
failed の場合、記録の error に code と message が入ります。失敗したジョブは無料です。結果の読み取りは、レート制限にカウントされません。
動画:常にジョブです
POST /v1/videos の呼び出しは、ステータス queued の Video オブジェクトとともに、すぐに 200 で応答します。ここでは Prefer は何も変えません。
curl https://api.hyperouter.app/v1/videos \
-H "Authorization: Bearer $HYPEROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "x-ai/grok-imagine-video", "prompt": "A lighthouse at dusk", "seconds": 8}'
status が completed になるまで GET /v1/videos/{id} をポーリングします(queued、in_progress、failed になることもあります)。progress は 0 から 100 まで進みます。その後、ファイルをダウンロードします。
async function waitForVideo(id: string): Promise<void> {
const headers = { Authorization: `Bearer ${process.env.HYPEROUTER_API_KEY}` };
for (;;) {
const res = await fetch(`https://api.hyperouter.app/v1/videos/${id}`, { headers });
const video = await res.json();
if (video.status === "completed") break;
if (video.status === "failed") throw new Error(video.error?.code ?? "failed");
await new Promise((r) => setTimeout(r, 5000));
}
const file = await fetch(`https://api.hyperouter.app/v1/videos/${id}/content`, { headers });
// file is video/mp4; ?variant=thumbnail returns the thumbnail
}
動画は、作成から 30 日後の expires_at まで保持されます。それを過ぎると 404 not_found が返るため、残したいものはダウンロードしてください。金額は事前に確保され、ジョブが失敗した場合は全額が解放されます。
ポーリングの代わりに Webhook を使う
ダッシュボードでエンドポイントを追加すると、ジョブが終了したときにそのエンドポイントへリクエストが届きます。
| Event | 送信されるタイミング |
|---|---|
video.completed、video.failed | 動画のジョブが終了したとき。 |
image.completed、image.failed | Prefer: respond-async を付けて送信した画像のジョブが終了したとき。 |
ペイロードには ID だけが含まれ、結果そのものは含まれません。オブジェクトは API キーで取得してください。data.id は、GET /v1/videos/{id} と GET /v1/generations/{id} が受け取る値です。各配信は Standard Webhooks 方式で署名されるため、OpenAI SDK で検証できます。解析する前に、生のボディを検証してください。
const event = client.webhooks.unwrap(rawBody, req.headers, process.env.HYPEROUTER_WEBHOOK_SECRET);
if (event.type === "video.completed") {
const video = await client.videos.retrieve(event.data.id);
}
10 秒以内に 2xx で応答してください。それ以外の応答は、最大でさらに 8 回再試行されます。webhook-id は再試行のたびに同じ値になるため、重複の排除に使えます。エンドポイントのルール、ヘッダー、再試行のスケジュールの全体は、Webhook リファレンスに掲載しています。
3 日間すべての配信が失敗したエンドポイントは無効になり、ダッシュボードにその理由が表示されます。再度有効にするまでは、ポーリングを使ってください。
次のステップ
- Idempotency-Key で安全にリトライするを使うと、作成の呼び出し自体を安全に繰り返せます。
- 画像と動画は、完全なリファレンスです。