Claude APIで長時間リクエストがタイムアウトする対策
Claude APIの非ストリーミングリクエストは10分の壁でSDKに事前ブロックされます。ストリーミングAPIとBatch APIの使い分け、TCP keep-aliveの設定まで実装方法を扱います。
Claude APIの長時間リクエストで何が起きるか
大きな max_tokens を指定した非ストリーミングのMessages APIリクエストは、処理に10分以上かかることがあります。ここでつまずくのは504エラーではなく、SDK側の事前ブロックです。公式SDKは送信前に「このリクエストは10分のタイムアウト見込みを超えないか」を検証しており、超えそうな設定はネットワークに投げる前にクライアント側で弾かれます。
超えなかった場合も油断はできません。失敗のパターンは2つあります。1つは経路上の問題です。ネットワークによってはアイドル状態の接続を一定時間で切断するため、レスポンスを待ち続けている間に経路上のロードバランサーやプロキシが接続を落とすと、リクエストはAnthropic側の処理とは無関係に失敗します。この場合はサーバーからの応答自体が届かないので、クライアント側は接続エラーとして検知します。もう1つはサーバー側の処理そのものが長引くケースです。こちらで返るのが timeout_error(HTTP 504)で、公式は「処理中にタイムアウトした(The request timed out while processing)」と定義しています。経路の接続切断とは別物で、リクエストはAnthropicのサーバーまで届いたうえで処理に時間がかかりすぎたことを示します。
なぜSDKは事前に検証するのか
SDKの事前検証は、失敗の種類を変えるための仕組みです。ネットワーク経路でタイムアウトすると、リクエストがどこまで処理されたか分からないまま接続が切れます。課金対象になったのかも判別しづらく、リトライすると同じ処理をもう一度実行してコストが二重にかかるリスクがあります。SDKが事前にブロックすれば、この不確実な失敗を避けて即座にエラーとして扱えます。
事前検証に引っかかったら、max_tokens を減らすのではなく処理方式そのものを見直すのが公式の推奨です。方式は2つあります。
ストリーミングAPIに切り替える
ストリーミングはレスポンスをイベント単位で受け取り続けるため、接続がアイドル状態にならず、10分の壁そのものを回避できます。逐次イベントを処理する必要がない場合は、SDKにストリームを消費させて完成した Message オブジェクトをまとめて受け取ることもできます。非ストリーミング呼び出しと同じ形で結果が返るため、既存コードの変更は最小限で済みます。以下はイベントを逐次出力するCLI呼び出しの例で、SDK側で集約させたい場合はストリーム消費用のヘルパーメソッドを使います。
ant messages create --stream --format jsonl <<'YAML'
model: claude-sonnet-5
max_tokens: 128000
messages:
- role: user
content: Write a detailed analysis...
YAMLBatch APIに切り替えてポーリングする
ストリーミングを組み込みたくない、あるいはネットワークが不安定でリクエスト中に接続を維持し続けられない環境では、Message Batches APIの方が向きます。Batch APIはリクエストを送信したあとサーバー側で非同期に処理し、結果が出るまで一定間隔でステータスを確認する「ポーリング」の設計です。接続を張りっぱなしにする必要が無いため、アイドルタイムアウトの影響をそもそも受けません。
バッチは基本的に1時間以内に処理が終わりますが、上限は24時間です。24時間以内に処理が終わらなかったリクエストは expired 扱いになり、課金もされません。ポーリングの実装は次のように processing_status が ended になるまで繰り返すだけです。
while :; do
status=$(curl -s "https://api.anthropic.com/v1/messages/batches/$BATCH_ID" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
| jq -r '.processing_status')
[ "$status" = "ended" ] && break
sleep 30
done処理を止めたくなった場合は、キャンセル用のエンドポイントでバッチをキャンセルできます。キャンセル直後は processing_status が canceling になり、先のポーリングと同じ実装でキャンセル完了(ended)を待てます。キャンセルされたバッチは、キャンセルまでに処理が終わっていたリクエスト分の結果を部分的に含んだ状態で ended になります。
結果が揃うと results_url にJSONLファイルが用意されます。件数が多い場合は一括ダウンロードではなく、ストリームで結果を読み出す実装が公式に推奨されています。1つのバッチは最大10万リクエストまたは256MBまでで、結果は作成から29日間ダウンロード可能です。Batch APIの料金体系や制限の詳細はClaude Batch APIの使い方にまとめています。
出力トークンが多いほどBatch APIが有利になる
長時間リクエストの多くは、出力トークン数そのものが大きいケースです。30万トークン級の生成は完了までに1時間を超えることもあり、こうした処理は最初からBatch APIの24時間窓を前提に設計したほうが安定します。output-300k-2026-03-24 ベータヘッダーをバッチリクエストに付けると、Claude Opus 5・Claude Opus 4.8・Claude Sonnet 5などで通常128kトークンの出力上限を30万トークンまで引き上げられます(公式ドキュメント上、この拡張はバッチリクエスト向けで、同期のMessages APIには適用されません)。長文生成をBatch APIに逃がす場合はこのヘッダーも合わせて検討する価値があります。
料金面でもBatch APIには利点があります。Batch APIの利用は入力・出力トークンとも標準料金の50%です。さらにプロンプトキャッシュとの併用も可能で、両方の割引は重ね掛けされます。バッチはリクエストが並行処理されるため、キャッシュのヒット率は保証されませんが、実測ではトラフィックの傾向によって30%から98%の範囲でキャッシュヒットが得られています。長時間かかる大量リクエストほど、ストリーミングへ逃がすよりBatch APIに寄せたほうがコストの効果が大きくなります。
504エラー自体はSDKが自動リトライする
SDKの事前検証をすり抜けたあとにネットワーク経由で発生した timeout_error(504)は、接続エラーや5xx系のサーバーエラーと同じ扱いで、公式SDKが指数バックオフ付きで自動的に最大2回までリトライします。retry-after ヘッダーがあればそれに従います。自前でリトライループを書き足す必要は無く、リトライ回数を変えたい場合はクライアント初期化時の最大リトライ数オプションを調整します。ここで自前のリトライを重ねて実装すると、SDKのリトライと合わせて二重にリトライが走ることになるため注意します。
ストリーミングに切り替えてもエラーが消えるわけではない
ストリーミングは10分の壁とアイドル切断のリスクを避けられますが、エラーそのものが起きなくなるわけではありません。サーバー送信イベント(SSE)でストリーミング中に受け取るレスポンスは、いったんHTTP 200を返したあとでもエラーイベントが送られてくることがあり、通常のステータスコード判定では拾えません。長時間のストリーミングを行う実装では、接続がタイムアウトや切断で終わるケースと、200番台の中でエラーイベントとして通知されるケースの両方をハンドリングする必要があります。長時間リクエストと合わせて、Claude APIのrefusal stop_reasonを検出してリセットする方法のような、200の中に隠れた異常系の扱いも押さえておくと実装が安定します。
ストリーミングとBatch API、どちらを選ぶか
| 状況 | おすすめ | 理由 |
|---|---|---|
| ユーザーの画面にリアルタイムで結果を出したい | おすすめ◎ ストリーミングAPI | 理由イベント単位で受信でき、アイドル切断が起きない |
| 数百〜数万件をまとめて処理したい | おすすめ◎ Batch API | 理由標準料金より安く、接続を維持せずに済む |
| 実行環境のネットワークが不安定(社内プロキシ経由等) | おすすめ◎ Batch API | 理由処理中に接続を張り続ける必要がない |
| 単発の対話的な問い合わせ | おすすめ△ どちらでも可 | 理由応答時間が短ければ非ストリーミングのままでも問題ない |
直接APIに接続する実装ではTCP keep-aliveを設定する
SDKを使わずHTTPクライアントを自前で組んでいる場合は、TCPソケットのkeep-aliveオプションを有効にしておくと、一部のネットワークでアイドル接続タイムアウトの影響を減らせます。公式SDK自体もこのソケットオプションを内部で設定しています。ロードバランサーやNATのタイムアウトより短い間隔でkeep-aliveパケットを送るよう調整するのが基本です。
Pythonで生のソケットにkeep-aliveを設定する場合は、次のように SO_KEEPALIVE を有効にします。
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)keep-aliveが効くのは「接続自体が生きているとネットワーク機器に伝える」ことによってです。NATやロードバランサーは一定時間データが流れない接続をテーブルから削除してしまうことがあり、いざレスポンスが返ってきたときには経路が切れていて届かない、という失敗が起こります。keep-aliveパケットを短い間隔で送り続けることで、この「無通信時間」を作らないようにするのが基本的な考え方です。ただし、これはあくまで一部のネットワーク環境での緩和策であり、10分の壁そのものをなくすものではありません。恒久的な対策としては、前述のストリーミングAPIかBatch APIへの切り替えを優先します。
Batch APIとストリーミングは併用できるか
Batch APIそのものはストリーミング配信をせず、結果はポーリング後にJSONLファイルとしてまとめて取得する設計です。大量のリクエストを裏側でBatch APIに投げつつ、個々の完了通知をユーザーにリアルタイムで見せたい場合は、Batch APIのポーリング結果を受け取った時点でアプリ側から利用者へ通知する、という2段構えの設計になります。1つのリクエストの中でBatch APIとストリーミングを同時に使うことはできません。
よくある質問
事前検証に引っかかったらmax_tokensを減らせば解消しますか
一時的には回避できますが、根本対策にはなりません。出力が長くなる処理は今後も10分に近づくたびに同じ検証で弾かれるため、max_tokensの調整はその場しのぎです。処理方式をストリーミングかBatch APIに変える方が再発しません。
ストリーミングにすればBatch APIの割引は受けられませんか
その通りです。Batch APIの50%割引はBatch APIを使った場合にのみ適用され、ストリーミングのMessages APIには適用されません。リアルタイム性が不要な処理はBatch APIに寄せたほうが、タイムアウト対策とコスト削減を同時に達成できます。
まとめ
非ストリーミングのMessages APIで10分を超えそうなリクエストはSDKが事前にブロックします。回避策はmax_tokensを削ることではなく、リアルタイム性が要るならストリーミングAPI、まとめて大量に処理するならBatch APIへ切り替えることです。自前でHTTPクライアントを実装している場合はTCP keep-aliveの設定も忘れずに行います。個別のエラーコードへの対処はClaude APIのエラーハンドリング設計で扱っています。