Skip to main content

What This Tutorial Builds

A pipeline that takes a list of support tickets and returns a triage decision for each one — severity, category, suggested team — by pushing them through run_agent_batch_v1_agent_batch_completions_post on the hosted MCP server. It covers the parts that only show up at scale:
  • Chunking a large input list to the endpoint’s hard 50-item cap
  • Running chunks concurrently over a single MCP session
  • Surviving partial failures without losing the whole run
  • Parsing model output that is supposed to be JSON and sometimes is not
  • Accounting for cost per record, not per run
Batch endpoints are premium-only. /v1/agent/batch/completions is restricted to Pro, Ultra, and Premium subscribers, and a free-tier key gets a 403. Upgrade your account to run this tutorial.

Why Batch Instead of a Loop

Calling the single-agent tool once per record works and is easy to reason about. It also pays connection and scheduling overhead on every one of ten thousand records, and it serializes work the server would happily parallelize. The batch tool takes up to 50 completions in one call and fans them out server-side. Your job shrinks to two things: chunk correctly, and run a bounded number of chunks at once.

Step 1: Setup

Step 2: Open One Session, Reuse It

The single most common performance mistake is opening a new MCP session per chunk. Connect once and pass the session down.
The 600-second timeout is deliberate. A 50-item batch of real work routinely runs for minutes, and the default httpx timeout will cut it off mid-flight.

Step 3: Define the Agent Once

Every record in the batch reuses the same agent_config. Only the task changes.
Low max_tokens and low temperature are what make batch economics work. You are asking for a classification, not an essay — cap the output so one verbose row cannot triple the bill.

Step 4: Chunk to the Cap

The batch tool accepts at most 50 items. Exceeding it is a validation error, not a silent truncation.

Step 5: Run One Chunk

One chunk is one tool call. Check isError before touching the payload — an upstream 403, 429, or 422 arrives as a result, not an exception.
The batch response shape:
results is positional — results[i] corresponds to body[i]. That is what lets you rejoin the model’s answer to your original record.

Step 6: Fan Out, but Bounded

Running every chunk at once will hit your rate limit. A semaphore keeps a fixed number in flight.
Returning the exception rather than raising is the important part. In a 10,000-record run, one chunk failing on a transient 429 should cost you 50 records to retry — not the other 9,950.
Start at CONCURRENCY = 5 and raise it while watching get_rate_limits_v1_rate_limits_get. The right number depends on your tier, not on your machine.

Step 7: Rejoin and Parse

The agent was told to return JSON. Sometimes it will return JSON wrapped in a code fence, or a sentence of preamble. Handle that instead of trusting it.
Every record ends up in exactly one of two lists. Nothing is silently dropped — that is the property you want when the input is a customer backlog.

Step 8: Account for the Cost

Each result carries its own usage, so the true cost of the run is a sum, not an estimate.

Step 9: The Complete Pipeline

Scaling Past 10,000 Records

Nothing in the code above changes — only the numbers do. The knobs, in the order worth turning:
  1. CONCURRENCY — the real throughput lever. Raise it until get_rate_limits_v1_rate_limits_get says you are close to your ceiling.
  2. max_tokens — the real cost lever. A classification needs 300 tokens, not 16,000.
  3. model_name — try the cheapest model that holds accuracy on a 100-record sample before running 10,000.
Validate on a small sample first. Run 100 records, read the output by hand, and confirm the JSON contract holds before you spend on the full backlog. A prompt that produces prose instead of JSON costs the same as one that works.

Common Errors

Next Steps