CloudCodeTree LogoCloudCodeTree
AI NewsTutorialsAbout
CloudCodeTree Logo
CloudCodeTree
  • AI News
  • Tutorials
  • About
← Back to AI News
Process 100,000 Claude Requests in One Call: The Message Batches API for Bulk Async Jobs

Process 100,000 Claude Requests in One Call: The Message Batches API for Bulk Async Jobs

Chris Harper

3 min read

Jul 14, 2026 · 12:05 UTC

AI
Tutorial
Agents
Best Practices

What you'll be able to do after this:

  • Submit thousands of Claude API requests in a single async batch with no rate-limit or retry machinery to write
  • Get a guaranteed 50% cost reduction on every input and output token in the batch
  • Reconcile results back to your original requests with stable custom IDs, and handle partial failures cleanly

TL;DR: The Message Batches API sends up to 100,000 Claude requests as one async job at half price — the right tool for eval harnesses, data labeling, and any pipeline that does not need real-time responses.

When you have 10,000 documents to classify, 5,000 support tickets to route, or an eval harness to run on a fresh fine-tune, the synchronous API works but costs money you do not need to spend and forces you to write retry and rate-limit machinery. The Message Batches API collapses all of that: submit once, poll for completion, stream results.

The three numbers that matter

  • Up to 100,000 requests per batch (256 MB total limit)
  • 50% cost on both input and output tokens — all Claude models, no configuration required
  • Results within 24 hours (most batches finish in under an hour)

Submit a batch in 15 lines

import anthropic

client = anthropic.Anthropic()

batch = client.messages.batches.create(
    requests=[
        {
            "custom_id": f"doc-{i}",
            "params": {
                "model": "claude-sonnet-5",
                "max_tokens": 50,
                "messages": [{"role": "user", "content": f"Classify as bug/feature/question: {doc}"}],
            },
        }
        for i, doc in enumerate(documents)
    ]
)
print(batch.id, batch.processing_status)  # ended | in_progress | validating

custom_id is your key for reconciliation — use something that maps back to your source data.

Poll until done, then stream results

import time

while True:
    status = client.messages.batches.retrieve(batch.id)
    if status.processing_status == "ended":
        break
    time.sleep(60)  # most batches finish in under 30 minutes

for result in client.messages.batches.results(batch.id):
    if result.result.type == "succeeded":
        label = result.result.message.content[0].text
        print(f"{result.custom_id}: {label}")
    elif result.result.type == "errored":
        print(f"{result.custom_id}: failed — resubmit")

When to use batches vs. real-time

ScenarioUse
User waiting on screenSynchronous Messages API
Eval harness, bulk annotation, nightly pipelineBatch API
Agent in an interactive loopSynchronous Messages API
Annotating a fine-tuning datasetBatch API

Power combo: batches + prompt caching

Mark your system prompt with "cache_control": {"type": "ephemeral"} on every request in the batch. Cached tokens get the 0.1x cache rate AND the 0.5x batch rate — effectively 5% of standard input cost on the prefix. For large system prompts or long retrieval contexts repeated across every item, this can cut total batch cost by 90%+.

Partial failures are normal

Each result has a type: "succeeded", "errored", or "expired" (rare: after 24 h without completion). Collect the non-success custom_ids and resubmit them as a smaller follow-up batch. The batch API does not retry internally — that is by design so you control the failure policy.

Sources: Batch processing — Claude Platform Docs · Batch processing cookbook — Claude Cookbook · Message Batches API reference