
Batch Your Offline Claude Calls and Pay Half: The Message Batches API
Chris Harper
2 min read
Aug 27, 2026 · 12:06 UTC
TL;DR: The Anthropic Message Batches API processes up to 100K requests asynchronously at 50% of synchronous pricing, per Anthropic's published rates — the right default for eval runs, nightly reports, and bulk tagging, with a 24-hour maximum wait and no streaming.
Most Claude API workloads split into two populations: calls that need a real-time response, and calls that don't. The second population — evaluation runs against a test set, nightly document tagging, bulk classification — is running at full synchronous pricing even though nothing is actually waiting on the result. The Message Batches API exists for exactly this case.
Submitting a batch:
import anthropic, time
client = anthropic.Anthropic()
# Submit — up to 100K requests per batch; each follows the Messages API schema
batch = client.messages.batches.create(
requests=[
{
"custom_id": f"doc-{i}",
"params": {
"model": "claude-sonnet-5",
"max_tokens": 200,
"messages": [{"role": "user", "content": f"Classify the sentiment of: {text}"}],
},
}
for i, text in enumerate(texts)
]
)
print(f"Submitted batch {batch.id}. Status: {batch.processing_status}")
# Poll for completion (most batches finish in minutes; SLA is 24 hours)
while True:
status = client.messages.batches.retrieve(batch.id)
if status.processing_status == "ended":
break
time.sleep(60)
# Retrieve results
for result in client.messages.batches.results(batch.id):
if result.result.type == "succeeded":
print(result.custom_id, result.result.message.content[0].text)
elif result.result.type == "errored":
print(result.custom_id, "ERROR:", result.result.error)
When to use it:
- Eval pipelines over a labeled test set: submit the whole set, retrieve results hours later
- Nightly ingestion: summarize or tag new documents overnight, read results in the morning
- Bulk classification at scale: 50K items in one API call, not 50K round trips
Limits — all real:
- Results take up to 24 hours. Most batches finish in minutes to a few hours, but there's no timing SLA, so this isn't for anything time-sensitive.
- No streaming. Poll or set a webhook; results don't come in incrementally.
- No per-request adaptation. If Claude's response to request 3 should influence request 4, you can't do that within a single batch.
- Results are stored for 29 days, then deleted.
Finding your biggest async opportunity: the /claude-api cost-optimize command added in Claude Code v2.1.247 (August 27, 2026) profiles your project's existing call patterns and flags high-frequency synchronous calls that look like batch candidates — a useful starting point before auditing manually.
Sources: Anthropic Message Batches API docs, Anthropic pricing — 50% batch discount, CloudZero: Claude pricing 2026, Claude Code v2.1.247 changelog