3行でいうと
- AIを非同期で動かす方法は大きく3つあります。結果を取りに行く方式(バッチ、バックグラウンド実行)と、完了を知らせてもらう方式(Webhook)です。3社とも、バッチは50%割引で、24時間前後の期限や目標を書いています。
- Webhookは「少なくとも1回」届く仕組みで、重複や遅れがありえます。OpenAIは再試行を最大72時間、Googleは24時間と書いており、配信の保証期間も違います。受け取る側は、重複を捨てる処理と、通知が来なくても状態を確認し直す処理が必要です。
- 結果の保管期間は短く、Anthropicは作成から29日、OpenAIの出力ファイルは完了から30日、Geminiは既定で6週間です。ジョブの状態と結果は、自分のデータベースにも写しておく設計が前提になります。
この記事は、実際に使って確かめた記録ではありません。この記事は、公式の文書を読んでまとめたもので、実際に使って確かめた記録ではありません。数字と仕様は2026年10月16日に確認し、10月22日に読み直しました。変わることがあります。
先に結論
- 時間のかかる処理を、1回の通信を開いたまま待つのは危うい、と公式文書が書いています。Anthropicのエラーの文書は、10分を超えるような長い要求にはストリーミングかバッチを使うこと、通信が途中で切れると応答なしで失敗しうることを挙げています。
- 非同期にすると、「依頼を出す」「状態を見る」「結果を受け取る」が別々の処理になります。公式のAPIが決めてくれるのは、そのうち状態の名前と、結果を取り出す場所までです。失敗した1件をどうするか、同じ依頼を二重に出さないか、結果をどこに保存するかは、アプリ側の設計です。
- 通知(Webhook)は便利ですが、それだけに頼る設計は避けたほうがよさそうです。これは僕らの見立てです。公式の文書自体が「重複がありうる」「再試行には期限がある」と書いているためです。
前提:同期と非同期、そしてこの記事の範囲
同期の呼び出しは、依頼を送り、そのままAIの答えが返るまで待ちます。非同期処理は、依頼を送るとすぐに受付の印(IDなど)が返り、結果は後から別の方法で受け取ります。後から受け取る方法には、こちらから聞きに行くポーリングと、相手から知らせてもらうWebhookがあります。
この記事が扱うのは、アプリの開発者向けの話です。AIの回答を画面にすぐ出す用途ではなく、大量の文書の要約や分類、夜間の一括処理、長い推論などを想定しています。バッチAPIの対象や割引そのものは別の記事で扱っているので、ここでは設計に関わる点に絞ります。
3社が用意している非同期の仕組み
公式文書を読んで、仕組みを3種類に分けて表にしました。分け方は僕らの整理です。
| 方式 | OpenAI | Anthropic | Google(Gemini API) |
|---|---|---|---|
| 大量・急がない処理(バッチ) | Batch API(JSONLのファイルを渡す) | Message Batches API | Batch API(インラインかファイル) |
| 1件の長い処理を後で受け取る | Responsesのbackground: true |
該当する機能は、読んだ範囲では見つかりませんでした(ストリーミングかバッチを案内) | 動画生成や長時間の操作は、Webhookの対象に含まれる(後述) |
| 完了の通知(Webhook) | あり(Standard Webhooks準拠) | 読んだ文書では確認できていません | あり(バッチなどのイベント) |
OpenAIのバックグラウンド実行は、1件の依頼を「受付だけ先に済ませる」形です。公式の説明では、queuedまたはin_progressの間は、GET /v1/responses/{id}で状態を取り直し、それ以外の状態になったら終わりです。取り消しはPOST /v1/responses/{id}/cancelで、何度呼んでも同じ結果になる(冪等)と書かれています。ストリーミングと組み合わせると、sequence_numberを目印にして、途中から再開できます。
AnthropicとGoogleのバッチは、状態の名前が違います。Anthropicはin_progress、canceling、endedの3つで、成功か失敗かは、バッチ全体ではなく1件ごとの結果の型(succeeded、errored、canceled、expired)で見ます。GoogleはJOB_STATE_PENDINGからJOB_STATE_EXPIREDまでの6つで、OpenAIはvalidatingからcancelledまでの8つです。状態の数も、「全体の状態」と「1件ごとの結果」のどちらで見るかも、会社ごとに違います。
バッチの数字を3社で並べる(2026年10月16日に確認、10月22日に読み直し)
公式のBatch系ページに書かれていた数字です。すべて、読んだページの記載をそのまま書き写しています。
| 項目 | OpenAI | Anthropic | |
|---|---|---|---|
| 割引 | 50% | 50% | 50% |
| 完了の目安 | 24時間(24hのみ) |
24時間で期限切れ。ほとんどは1時間以内と書かれている | 目標24時間。48時間を超えると期限切れ |
| 1回の上限 | 5万件、入力ファイル200MB | 10万件または256MB | インラインは合計20MB未満、ファイルは2GBまで |
| 結果の保管 | 出力ファイルは完了の30日後に削除 | 作成から29日(終了からではない) | 既定で6週間 |
| 期限切れの扱い | 未処理分はエラーファイルにbatch_expiredで入る。完了した分のトークンが課金される |
expiredの1件は課金なし |
48時間を超えるとJOB_STATE_EXPIRED(結果の扱いは確認できていません) |
| 結果の順序 | 入力と一致しないことがある | 順序は保証されない | 順序の記載は読んだ範囲になし |
| 照合のキー | custom_id |
custom_id(1〜64文字、英数字と-と_) |
入力の各行に付けるユーザー定義のキーが、応答でどの依頼の結果かを示すのに使われる |
| 取り消し | cancellingは最大10分かかる |
cancelingを経てended |
状態にJOB_STATE_CANCELLEDがある |
OpenAIには、1時間あたり2,000バッチまでの作成という上限もあります。
Anthropicのページには、バッチ内の個々の依頼の検証は非同期で、エラーはバッチ全体が終わってから返る、という注意があります。
Geminiのページには、ジョブの作成は冪等ではなく、同じ依頼を再送すると2つ目のジョブができる、と書かれています。
Webhookの仕様:3社が共通の土台を使っている
OpenAIのWebhookは、Standard Webhooksという共通仕様に沿っています。GoogleのGemini APIも、静的に登録するWebhookは同じ仕様だと書いています。つまり、受け取る側のコードは、両社で大部分を共有できます。
共通仕様(Standard Webhooks v1.0.0)が決めている主な点は、次のとおりです。
- 3つのヘッダー:
webhook-id(冪等キーに使うID)、webhook-timestamp(時刻)、webhook-signature(署名。OpenAIの例の値はv1,で始まる)。 - 署名の対象:
ID.タイムスタンプ.本文をピリオドでつないだ文字列です。署名した本文と送った本文は同じでなければならず、受け取ってJSONとして読み込み、もう一度文字列にしてから署名を確かめると、検証に失敗しうる、とあります。 - 署名の方式:共有の秘密鍵を使うHMAC-SHA256(接頭辞
v1)と、公開鍵のed25519(v1a)があります。 - 応答のコード:410は配信を止める(無効化)、429、502、504は送る側が速度を落とす、
retry-afterがあれば尊重する、とあります。OpenAIは、2xxが成功で、3xxは追従せず失敗扱いにします。 - タイムアウトの推奨:15〜30秒です。ただしOpenAIとGoogleの文書は、どちらも「数秒」としか書いておらず、正確な秒数は読んだ範囲では分かりませんでした。
- 再試行:仕様は、数日にわたる指数バックオフ(ジッタつき)を勧めています。例の表の最後の行には75時間35分5秒とあります。
会社ごとの違いは、次のとおりです。
| OpenAI | Google(Gemini API) | |
|---|---|---|
| 再試行の期間 | 最大72時間、指数バックオフ | 24時間、指数バックオフ |
| 重複 | まれにあり。webhook-idで重複を捨てる |
少なくとも1回の配信。webhook-idで重複を捨てる |
| 古い通知の拒否 | 時刻の許容幅は、読んだ範囲に具体値なし | webhook-timestampが5分より古いものを拒否、が推奨 |
| 通知の中身 | response.completedなどのイベント種別がある(例) |
IDと出力先のポインタだけの「薄い」中身 |
| 秘密の交換 | 作成直後だけ表示される | 作成時の1回だけ表示。入れ替え時は旧秘密を24時間残す指定の例がある |
Googleの文書は、リクエストごとに指定する「動的Webhook」も説明しています。こちらは共有の秘密ではなく、署名つきのトークン(JWT)で本物かを確かめます。静的と動的で検証の方法が違うので、両方使うなら別々の処理が必要です。なお、そのページには公開鍵を取る場所のURLが2種類あり、食い違っていました。実際に実装するときは、公式ページで直接確かめてください。
受け取る側の設計:5つの決めごと
ここからは、公式の記載を材料にした、僕らの見立てです。公式が「こう設計しなさい」と書いた部分と、僕らがそこから読み取った部分は区別して書きます。
1. ジョブの表を、自分のデータベースに持つ
状態の名前は会社ごとに違うので、自分の側で、共通の状態(受付、実行中、成功、失敗、期限切れ、取り消し)を決めて、各社の状態を対応づけるのがよいと思います。表には、各社のID(OpenAIのレスポンスID、Anthropicのバッチ、Googleのジョブ名)と、自分のジョブID、依頼の内容への参照、結果の保管場所を入れます。結果の保管期間が29日、30日、6週間と有限なので、取れたら自分の側に保存するのが前提です。これは公式の保管期間の記載から導いた見立てです。
2. 依頼を二重に出さない
Geminiは、ジョブの作成が冪等でないと書いています。冪等性は、同じ操作を何度繰り返しても、結果が1回分と同じになる性質です。作成の呼び出しがネットワークの都合で成功したのか分からないとき、慌てて再送すると、課金される処理が2つ走ります。自分のジョブIDを先に作って表に書き、各社のIDが返ってきたら表に結びつける、という順序が安全だと思います。OpenAIとAnthropicの文書では、作成の呼び出しの冪等性についての記載は、読んだ範囲では見つかりませんでした。
3. Webhookは「受けて、すぐ返し、後で処理する」
OpenAIもGoogleも、2xxを数秒以内に返すよう書いています。OpenAIは、重い処理をバックグラウンドに回すよう書いています。署名を確かめて、イベントを自分のキュー(処理待ちの列)に書き込んだら、すぐ2xxを返す、という形が考えられます。
4. 重複と順序の乱れを前提にする
公式はwebhook-idを冪等キーに使うよう書いているので、処理済みのIDを覚えておき、2回目以降は何もせず2xxを返します。共通仕様の例は5分間の保存ですが、OpenAIは再試行が72時間続くので、保存の長さは、使う会社の再試行の期間に合わせる必要がありそうです。これは僕らの見立てです。別のやり方として、状態を「進める方向にだけ」書き換える(成功のあとに実行中で上書きしない)作りにすれば、順序が入れ替わっても壊れにくくなります。
5. 通知が来なくても、状態を取り直す
Webhookの再試行には期限があります。自分の受け口が止まっていたり、URLが変わっていたり、3xxを返していたりすると、期限を過ぎた通知は戻ってきません。そこで、通知を受けるのとは別に、「一定時間、状態が動いていないジョブ」を定期的に各社のAPIで確かめ直す処理を持つのが堅いと思います。各社の文書も、ポーリングの例を載せています(Anthropicは60秒おき、Geminiは30秒おき、OpenAIのバックグラウンド実行は1〜2秒おきの例)。これらは例であって、推奨値ではありません。
Anthropicの文書には、状態がendedになるまで60秒おきに取り直す例があります(コードは、細部を確認できていないので省略します)。
部分的な失敗の扱い
バッチは、1件の失敗が他に影響しない仕組みです(Anthropicの文書)。ですから、終わったあとに「成功の分を取り込み、失敗の分だけ作り直す」処理が必要になります。
Anthropicでは、結果の型がsucceeded(課金あり)、errored、canceled、expired(いずれも課金なし)に分かれます。expiredは、24時間の期限が来た件です。OpenAIは、期限切れの件がerror_file_idのファイルにbatch_expiredで入り、custom_idから元の依頼を取り出せる、と書いています。Googleは、batchStatsのfailedRequestCountを確かめるよう案内しています。
つまり3社とも、「バッチ全体が成功した」と「全件が成功した」は別だと読めます。完了の通知を受けただけで済ませず、件数の内訳を見る必要があります。
気をつけること
- この記事は、実際に使って確かめた記録ではありません。内容は公式の文書の記載です。
- 公式ページは、WebFetchという取得の仕組みを通して読んでいます。この仕組みは要約を返すため、細部(特に表の数字)が抜けることがあります。実装の前に、公式ページで直接確かめてください。
- AnthropicのWebhookに当たる仕組みは、読んだページに見つかりませんでした。「ない」とは断定できません。OpenAIのBatchでWebhookを使えるかも、Batchのページには書かれていませんでした。Webhookのページには、完了を通知する例としてバッチが挙げられています(イベントの一覧そのものは読んでいません)。
- OpenAIのバックグラウンド実行について、最大の実行時間やレート上限は、読んだページに書かれていませんでした。また、ZDR(Zero Data Retention)の場合は、約10分だけ一時保存されると書かれています。結果を10分より後に取りに行く設計が許されるかは、文書から確かめられませんでした。使う前に公式で確認してください。
- Standard Webhooksの仕様は、通知の古さを何秒まで許すかの具体値を書いていません。Googleの文書の「5分」は、同社の推奨です。
- 「保管期間は有限なので、自分の側に写す」「作成の再送で二重にならない設計にする」などの設計の話は、僕らの見立てで、公式の推奨ではありません。サービスの契約や個人情報の扱いについての助言でもありません。
- 数字、状態の名前、対応するモデルは変わります。この記事は2026年10月22日に読み直したページに基づいています。
出典
- Webhooks(OpenAI API、OpenAI、10月22日確認)
- Background mode(OpenAI API、OpenAI、10月22日確認)
- Batch API(OpenAI API、OpenAI、10月22日確認)
- Batch processing(Claude Platform Docs、Anthropic、10月22日確認)
- Batch API(Gemini API、Google AI for Developers、Google、10月22日確認)
- Webhooks(Gemini API、Google AI for Developers、Google、10月22日確認)
- Standard Webhooks仕様(Standard Webhooksプロジェクト、GitHub、10月22日確認)
- API errors(Claude Platform Docs、Anthropic、10月22日確認)
更新履歴
-
2026年10月25日 書き方を直しました。
- 前:運営者が使っていないと読める書き方 → いま:「この記事は、実際に使って確かめた記録ではありません」。公式の資料を読んでまとめた記事で、使い心地は書いていない、という意味に直した
-
2026年10月22日 公式の資料を読み直して、次の点を直しました。
- 前:署名の確認は、本文を送ったものとバイト単位で同じにし、JSONに読み直してはいけない → いま:JSONに読み込んで文字列に戻してから確かめると、検証に失敗しうる
