Message Batches — 50% オフの非同期ジョブ
- Message Batches API が 1 日で元を取れるワークロードを見分ける
- バッチを送信し、ポーリングし、結果をストリーミングで取り戻す — エンドツーエンドで
- どちらも壊さずに、バッチ割引とプロンプトキャッシュを重ねる
- リクエストをバッチ対象から静かに外してしまう 4 つの機能を避ける
- 同じパターンを OpenAI と Gemini に移植する — どこも非同期処理を 50% 割引している
Anthropic の請求額の半分は、おそらく同期処理である必要がありません。夜間の評価、バックフィル、モデレーションのスイープ、大量のコンテンツ生成は、回答が 800 ms で返るか 40 分で返るかを気にしません — そしてこのトレードオフを受け入れれば、Anthropic は 50% 安く請求します。Message Batches API はそれを手に入れる方法です。
バッチが元を取れるとき
以下がすべて当てはまるときにバッチを選びます — 1 つでも「いいえ」があれば、おそらく通常の Messages API を使うべきです。
| シグナル | バッチが適するのは… |
|---|---|
| レイテンシ | 最大 24 時間待てる。ほとんどのバッチは 1 時間以内に完了しますが、SLA は 24 時間です。 |
| ボリューム | 送るリクエストが少なくとも数百件ある。API はバッチごとに最大 100,000 件を受け付けます。 |
| インタラクティブ性 | スピナーを見ているユーザーがいない。これはオフライン作業向けです。 |
| 結果の形 | ライブのトークンストリームではなく、JSONL の結果ファイルを消費できる。 |
典型的な勝ちパターン:
- 評価(Evals) — リリース前に 5,000 件のモデル出力をルーブリックで採点する。
- バックフィル — プロンプトを変更したときに既存データセットを再分類する。
- 一括生成 — 商品説明、要約、メタタグ、翻訳をカタログ規模で。
- モデレーション/ラベリング — スケジュールに沿ってユーザーコンテンツを毎日スイープする。
- 合成データ — 小さな下流モデル向けの学習ペアを生成する。
ジョブを形作る制限
| 制限 | 値 |
|---|---|
| 割引 | サポートされるすべてのモデルで、標準の入力および出力トークン価格の 50% オフ。 |
| バッチサイズ | 100,000 リクエストまたは 256 MB のいずれか早く到達した方。 |
| 処理時間 | ほとんどは 1 時間以内。24 時間のハード期限 — 24 時間後に未処理のものは expired を返します。 |
| 結果の保持 | バッチ作成から 29 日間。その後、結果は利用できなくなります(バッチのメタデータは残ります)。 |
| モデル対応 | すべての現行 Claude モデル — Fable 5、Mythos 5、Opus 5、Opus 4.x、Sonnet 5、Sonnet 4.x、Haiku 4.5。 |
| スコープ | Workspace 単位。Workspace A のキーで Workspace B のバッチは見えません。 |
| 支出上限 | 並行処理のため、バッチは設定した Workspace の支出上限をわずかに超えることがあります。 |
3 ステップのループ
すべてのバッチジョブは同じ形をたどります — 作成、ポーリング、結果のストリーミング。
- requests 配列を付けて POST /v1/messages/batches を呼びます。各項目は一意の custom_id(自分のデータに戻るための結合キー)と、通常の Messages 呼び出しとまったく同じ形の params ブロックを持ちます。API はバッチ id と in_progress の processing_status を返します。
- GET /v1/messages/batches/[id] をループで呼びます — 60 秒間隔で十分です。バッチは秒未満の作業ではありません。processing_status が ended に変わったら停止します。request_counts を見ればライブの進捗(processing / succeeded / errored / canceled / expired)が読めます。
- バッチのレスポンスは JSONL ファイルを配信する results_url を公開します — リクエストごとに 1 行、custom_id でタグ付けされています。バッファせずにストリーミングしてください:100k リクエストの結果ファイルは数百メガバイトになることがあり、SDK は行単位で反復するのですべてをメモリに読み込むことはありません。
バッチを作成する(cURL)
最小限のバッチ — Opus 5 の呼び出し 2 件。結果をソース行に結合し直せるよう、それぞれにタグを付けています。
POST /v1/messages/batches
curl https://api.anthropic.com/v1/messages/batches \
--header "x-api-key: $ANTHROPIC_API_KEY" \
--header "anthropic-version: 2023-06-01" \
--header "content-type: application/json" \
--data '{
"requests": [
{
"custom_id": "row-00001",
"params": {
"model": "claude-opus-5",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Summarize: ..."}
]
}
},
{
"custom_id": "row-00002",
"params": {
"model": "claude-opus-5",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Summarize: ..."}
]
}
}
]
}'custom_id は ^[a-zA-Z0-9_-]{1,64}$ にマッチし、バッチ内で一意でなければなりません。外部キーとして扱ってください — 多くの人は結果をきれいにマージし直せるよう、行 id か入力のハッシュを設定します。
ステータスをポーリングする(Python)
バッチが完了するまで待つ
import time, anthropic
client = anthropic.Anthropic()
batch_id = "msgbatch_..."
while True:
batch = client.messages.batches.retrieve(batch_id)
if batch.processing_status == "ended":
break
print(batch.request_counts) # live progress
time.sleep(60)結果をストリーミングし、種類ごとに処理する
すべてのリクエストは 4 つのバケットのいずれかに入ります。課金されるのは succeeded のみです — エラー、キャンセル、期限切れは無料です。
| 結果の種類 | 意味 | アクション |
|---|---|---|
succeeded | Message が返ってきた。 | custom_id でマージする。 |
errored | 不正なリクエストか一時的なサーバーエラー。 | invalid_request_error なら修正して再送信。サーバーエラーならそのままリトライ。 |
canceled | この項目が実行される前にバッチをキャンセルした。 | まだ必要なら再送信。 |
expired | このリクエストが実行される前に 24 時間の枠が過ぎた。 | 再送信 — 通常はバッチが大きすぎたか、プラットフォームが混雑していたサイン。 |
結果をストリーミングする(Python)
for r in client.messages.batches.results(batch_id):
match r.result.type:
case "succeeded":
save(r.custom_id, r.result.message)
case "errored":
if r.result.error.error.type == "invalid_request_error":
log_bad_row(r.custom_id, r.result.error)
else:
retry_queue.append(r.custom_id)
case "expired":
retry_queue.append(r.custom_id)結果ファイル全体を素朴な requests.get(...).text でダウンロードしないでください — 大きなバッチでは RAM を使い尽くします。SDK のヘルパーはすでに行単位で反復します。cURL での同等の方法は、レスポンスを実体化するのではなく jq -c にパイプすることです。
複利効果:バッチとプロンプトキャッシュを重ねる
この割引はプロンプトキャッシュと重ねられ、ここで経済性が馬鹿げたレベルになります。キャッシュヒットは入力価格の 10% で、バッチがそれをさらに半分にします。すべてのリクエストが同じ 20k トークンのシステムプロンプトとルーブリックを共有する大規模な評価では、入力の項目は定価の 5% まで下がります — 20 分の 1 です。
知っておくべき 2 つのこと:
- 1 時間のキャッシュ期間を使う。 デフォルトの 5 分 TTL は、大きなバッチが処理を終える前に期限切れになるのが普通です。Anthropic 自身のドキュメントも、バッチ処理には特に 1 時間キャッシュを推奨しています。
max_tokens: 0(キャッシュの事前ウォームアップ)はバッチ内では許可されません — バッチ処理中に書き込まれた一時的なエントリは後続の処理が走る前に期限切れになるため、プラットフォームはこれを即座に拒否します。
勝ちパターンの形:通常の Messages 呼び出しを 1 回行ってキャッシュを事前にウォームアップし、書き込まれるのを待ってから、そのキャッシュプレフィックスを次の 1 時間再利用するバッチを投入します。
バッチに入れられるもの — 入れられないもの
はい、バッチ可能: ビジョン、すべてのサーバーツール(Web 検索、Web フェッチ、コード実行、MCP コネクタ、アドバイザー、ツール検索)、システムメッセージ、マルチターン、拡張思考、ほとんどのベータ機能。通常の Messages API で動くものは、ほぼ確実にバッチ内でも動きます。
いいえ、検証時に拒否:
| パラメーター | ブロックされる理由 |
|---|---|
stream: true | 結果はライブストリームではなくファイルで返ります。 |
speed(Fast モード) | Fast モードは同期レイテンシを調整するもの — 非同期では意味がありません。 |
store / previous_thread_event_id(Threads) | Threads はステートフルで、バッチはそうではありません。 |
cache_hint / context_hint | ルーティングヒントは同期スケジューリングにのみ影響します。 |
max_tokens: 0 | 使う前に期限切れになるキャッシュエントリを書き込むことになります。 |
research_preview_2026_02: "active" | リサーチプレビューモードはバッチの経路にありません。 |
検証は非同期で実行されるため、不正なリクエストはバッチ全体が終了したときにしか報告されません。50,000 件のリクエストを送信する前に、1 件を通常の Messages API に通して形が検証を通ることを確認してください。
プロバイダーをまたいで同じパターン
バッチ料金は収束しました。3 大フロンティアプロバイダーはいずれも同じ見出しの形 — 50% オフ、約 24 時間の SLA、JSONL の入出力 — を提供しており、Claude だけの小技ではなく、真に移植可能なパターンになっています。
| プロバイダー | 割引 | SLA | 送信方法 | 参照 |
|---|---|---|---|---|
| Anthropic — Message Batches | 入力+出力 50% オフ | ほとんどは 1 時間未満、24 時間のハード期限 | JSON ボディ(requests[])、最大 100k / 256 MB | ドキュメント |
| OpenAI — Batch API | 入力+出力 50% オフ | ほとんどは 1〜6 時間、24 時間の SLA | JSONL ファイルのアップロード、バッチごとに最大 50k リクエスト | ドキュメント |
| Google — Gemini Batch API | 入力+出力 50% オフ | ほとんどは 24 時間未満の SLA | インラインまたは GCS ベースのバッチジョブ。コンテキストキャッシュ対応 | ドキュメント |
移植可能なアーキテクチャ:ソーステーブルを読み、行を 10k 行のバッチに分割し、プロバイダーごとに送信し、ポーリングし、custom_id で結果をマージする小さな「ジョブランナー」です。異なるのは送信/ポーリング/結果取得の呼び出しだけで、パイプラインの残りはプロバイダー非依存です。同じ考え方をプロンプト自体に適用したものは Cross-AI プロンプト翻訳を参照してください。
よくある間違い
- インタラクティブなトラフィックをバッチにする。 ユーザーがページにいる — 60 秒の待ち時間ですら壊れたプロダクトです。バッチはオフラインのスケジュールされた作業向けです。
- 小さすぎるジョブをバッチにする。 20 件のリクエストではポーリングループと 24 時間の上限に見合いません。数百件未満なら、並行実行付きの通常の Messages API を使ってください。
- ストリーミングできるのにバッチ全体でブロックする。 JSONL ファイル全体をメモリにダウンロードするのは 50 行なら動きますが、50,000 行では OOM になります。反復してください。
expiredの結果を無視する。 無料ですが、未完了の作業です。追跡して再キューに入れてください — さもないと、需要ピーク時にパイプラインが静かに行を失います。- 検証が同期だと思い込む。 1 行の不正なリクエストはフェイルファストしません。最後に残りと一緒に返ってきます。まず 1 行を通常の Messages API でテストしてください。
- 結合キーを失う。 バッチは順不同で完了し、結果ファイルは入力順にソートされていません。意味のある
custom_idを設定しなければ、結果をソースデータに確実にマージし直すことはできません。 - 29 日間の保持を忘れる。 その後、
results_urlは何も返しません。パイプラインの一部として結果をダウンロードし、自分のストレージに永続化してください。
理解度チェック
0/4- バッチ処理はオフラインワークロードにおける Anthropic の支出を減らす最大のレバー — 50% オフで、プロンプトの書き換えは不要。
- トレードオフはレイテンシとインタラクティブ性であり、品質ではない。同じモデル、同じ出力形式、同じ機能。
- 本当の経済性はプロンプトキャッシュと 1 時間 TTL を重ねること — プレフィックスを共有するワークロードでは定価入力価格の 5% に届く。
- 常に意味のある custom_id を設定し、常に結果ファイルをストリーミングし、常に期限切れの行を再キューに入れる。
- パターンは移植できる:OpenAI と Gemini も非同期処理を約 24 時間の SLA で 50% 割引している — ジョブランナーを 1 つ作り、プロバイダー間でルーティングする。
次のステップ
- 複利の割引を重ねる → プロンプトキャッシュとコスト最適化
- バッチを使うオフライン評価を設計する → 評価(Evals)
- 同じジョブランナーをプロバイダー間で移植する → Cross-AI プロンプト翻訳
- スケールアップ前にコストをエンドツーエンドで監視する → トークンと料金