Batch Processing
The Message Batches API trades latency for roughly 50% cost savings — ideal for latency-tolerant work, wrong for anything a person is actively waiting on.
The Message Batches API processes large volumes of requests asynchronously at roughly 50% of the standard cost. In exchange you accept an up to 24-hour processing window with no latency guarantee, and no support for multi-turn tool calling within a single batch request. It is a throughput-and-cost tool, not a speed tool.
Synchronous versus batch
The decision rule is simple: if a human or a blocking workflow is waiting on the result, use the synchronous API. If the work is latency-tolerant, use batch.
| Use synchronous when | Use batch when |
|---|---|
| Pre-merge / CI code checks | Overnight report generation |
| Real-time, user-facing review | Weekly compliance audits |
| Anything blocking a pipeline | Bulk backfills and re-processing |
Moving every workflow to batch for the 50% saving stalls anything a person or pipeline is waiting on — there is no latency SLA and the window can reach 24 hours.
# CI code check submitted to batch:
batch.create(requests=[...]) # PR blocked
# for up to 24h waiting on resultsRoute by who is waiting: synchronous for blocking, user-facing work; batch only for latency-tolerant bulk jobs.
# Real-time review -> synchronous:
client.messages.create(...)
# Overnight report -> batch:
client.messages.batches.create(...)A scenario proposing to move all workflows to batch 'for the cost savings' is wrong when any of them block a person or a pipeline. Never assume batch completes quickly — there is no latency SLA.
Correlate results with custom_id
Each request in a batch carries a unique custom_id. Results come back unordered, so the custom_id is how you match a response to its request — and how you identify exactly which items failed for targeted resubmission.
import anthropic
from anthropic.types.messages.batch_create_params import Request
client = anthropic.Anthropic()
batch = client.messages.batches.create(
requests=[
Request(
custom_id=f"doc-{doc_id}",
params={
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [{"role": "user", "content": text}],
},
)
for doc_id, text in documents.items()
]
)
# Later, once status == "ended":
for result in client.messages.batches.results(batch.id):
if result.result.type == "succeeded":
store(result.custom_id, result.result.message)
else:
mark_for_resubmit(result.custom_id, result.result.type)Handling failures
- 1Identify failures by inspecting the
custom_idof each errored result. - 2Resubmit only the failures, with modifications — chunk oversized documents, simplify prompts, or add few-shot examples.
- 3Refine prompts up front on a 5-10 document sample covering edge cases and format variety before submitting the full batch.
Planning around the SLA
Work backward from your deadline. With a 30-hour SLA and a 24-hour maximum window, you have a 6-hour buffer. Submitting a fresh batch every 4-6 hours keeps one in flight at all times, so a slow batch never blows the deadline.
50% cheaper, up to 24-hour window, no latency SLA, no multi-turn tool calling per request. Use it only when nothing is actively waiting on the result.
How the exam will try to trick you
The distractors below look right under time pressure — learn the tell.
- The trap
Move all workflows to batch to capture the 50% cost saving.
Correct answerKeep blocking, user-facing work synchronous; batch only latency-tolerant jobs.
Why: Batch has no latency SLA and can take up to 24 hours, so it stalls anything a person or pipeline waits on.
- The trap
Design a time-sensitive workflow assuming batch results arrive quickly.
Correct answerDesign around the 24-hour maximum window.
Why: Results often come faster, but there is no latency guarantee, so speed is unpredictable.
- The trap
Submit a multi-turn tool-calling workflow to the Batches API.
Correct answerUse the synchronous API for anything needing tool execution mid-processing.
Why: The batch API does not support multi-turn tool calling within a single request.
Key takeaways
- The Message Batches API gives ~50% cost savings with an up-to-24-hour window and no latency SLA.
- Use synchronous APIs for anything blocking a person or pipeline; batch only for latency-tolerant work.
- A unique custom_id correlates unordered results and pinpoints failures for resubmission.
- Resubmit only failed items with fixes (chunking, simpler prompts, few-shot examples).
- Refine prompts on a 5-10 document sample before running the full batch.
- Plan submissions around the 24-hour window: keep a fresh batch in flight to protect the deadline.
Frequently asked questions
When should you use Claude's Message Batches API?+
Use it for high-volume, latency-tolerant work — overnight reports, weekly audits, bulk backfills — where the ~50% cost saving matters and no one is waiting on individual results. Avoid it for real-time or pipeline-blocking tasks, since there is no latency guarantee.
How do you match batch results back to their requests?+
Assign each request a unique custom_id. Batch results return unordered, so you use the custom_id to correlate each response with its input and to identify exactly which requests failed for targeted resubmission.