For work that doesn't need an instant answer, batches cut cost in half and avoid rate-limit juggling.
Some jobs do not need answers right away: tagging 50,000 product descriptions overnight, or running a test set against a new prompt. The Message Batches API takes many Messages requests in one submission, processes them in the background, and charges about 50 percent of the normal price.
How it works:
- Build a list of requests. Each has a
custom_id(your own label, letters, digits,_and-) andparams, which are exactly the arguments you would pass tomessages.create. - Submit with
client.messages.batches.create(requests=[...]). You get a batch id likemsgbatch_.... - Poll
client.messages.batches.retrieve(id)untilprocessing_statusisended. Most batches finish within an hour; anything not done in 24 hours expires. - Read
client.messages.batches.results(id). Each result has thecustom_idand aresultwhosetypeissucceeded,errored,canceledorexpired.
Results are not in the order you sent them, so always match by custom_id. Only succeeded requests are billed. Errored ones split into invalid requests (fix them) and server errors (resend). Results can be downloaded for 29 days.
A batch can hold up to 100,000 requests or 256 MB. Each request is validated only when it runs, so test the request shape with a normal call first. Batches combine well with prompt caching for shared prefixes. Streaming is not available inside batches.
Example
import time
import anthropic
client = anthropic.Anthropic()
reviews = {"r1": "Loved it!", "r2": "Broke after a day.", "r3": "It's fine."}
batch = client.messages.batches.create(requests=[
{"custom_id": rid, "params": {
"model": "claude-haiku-5-5", "max_tokens": 5,
"messages": [{"role": "user", "content":
f"Sentiment (positive/negative/neutral), one word: {text}"}]}}
for rid, text in reviews.items()
])
while client.messages.batches.retrieve(batch.id).processing_status != "ended":
time.sleep(60)
for item in client.messages.batches.results(batch.id):
if item.result.type == "succeeded":
print(item.custom_id, item.result.message.content[0].text)
else:
print(item.custom_id, "->", item.result.type)Remember
- Batches: about half price, results within 24 hours
- Each request = custom_id + normal create params
- Match results by custom_id; order is not preserved
Watch out for
- Submitting 100,000 requests with an untested request shape and getting 100,000 errors.