Submit a batch
Create a BatchRouter batch with POST /v1/batches — inline items or an uploaded file, a quote_id, an Idempotency-Key, routing, and privacy.
Creating a batch submits your work for asynchronous processing and reserves credits against a quote. You send POST /v1/batches with either inline items or a reference to an uploaded file, accept a quote_id, and pass an Idempotency-Key so retries are safe.
This is the deep version of step 3 of the quickstart. It assumes you already have an API key (see Authentication) and a quote (see Quotes and pricing).
Before you start
You need two things in hand:
- An API key, sent as
Authorization: Bearer br_live_…. Keep it server-side. - A
quote_id(qlock_…) fromPOST /v1/quotes/model. The quote is free, pins your price estimate, and is required to reserve credits before dispatch.
Quotes are free; credits are charged at batch creation. If your workspace lacks credits you'll get a 402 — check GET /v1/auth/account and top up credits in the dashboard at https://batchrouter.com/app/billing.
Choose your input: inline items or a file
A batch's work comes from exactly one of two sources — they are mutually exclusive:
items— an inline array of batch items. Best for small-to-medium batches you assemble in memory.input_file_id— afile_…id from a JSONL file you uploaded withPOST /v1/files. Best for large batches or binary inputs. See JSONL format for the line shape and the file-based example below.
Send one or the other. Sending both — or neither — fails preflight with 400 and error.details.preflight.
The canonical item used across these docs:
{"customer_item_id":"item-1","operation":"responses","model":"gpt-5.4-mini","input":{"messages":[{"role":"user","content":"Summarize: BatchRouter routes batch-AI workloads across providers."}]}}gpt-5.4-mini is a model id from the live catalog. List the models available to you now, with per-provider pricing and capacity, via GET /v1/catalog/models.
The Idempotency-Key header
POST /v1/batches requires an Idempotency-Key header — a unique string of 8–128 characters per logical submission. If a network error leaves you unsure whether the batch was created, retry with the same key: BatchRouter returns the original batch instead of creating a duplicate. Use a fresh key for each new batch (a UUID works well).
Request options
Beyond the input and quote_id, the body accepts these optional fields:
| Field | Values | Default | What it does |
|---|---|---|---|
sla_tier | standard · flex · priority | standard | Every batch has one 24h SLA. All three values are accepted for compatibility and treated as standard. |
routing_mode | cheapest · sla_aware · public_only · edge_only · hybrid · privacy_constrained | cheapest | How BatchRouter selects a lane across providers. |
privacy_tier | standard · confidential · restricted | standard | Routes only to providers that declare the tier. confidential does not by itself require zero data retention (the router adds that check only under privacy_constrained); no built-in provider declares restricted. |
webhook | { url, secret } | — | Per-batch completion callback; secret is 8+ chars and signs X-BatchRouter-Signature. |
metadata | object | — | Arbitrary key/value pairs echoed back on the batch. |
Set these consistently with the quote_id you're accepting. If you quoted with a given routing_mode and max_price, create with the same intent so the reserved estimate matches the routing decision.
Create a batch (inline items)
-
Assemble your items and accept the quote.
-
Send the request with an
Idempotency-Key.curl -X POST https://api.batchrouter.com/v1/batches \ -H "Authorization: Bearer $BATCHROUTER_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "quote_id": "qlock_abc123", "sla_tier": "standard", "routing_mode": "cheapest", "privacy_tier": "standard", "items": [ { "customer_item_id": "item-1", "operation": "responses", "model": "gpt-5.4-mini", "input": { "messages": [ { "role": "user", "content": "Summarize: BatchRouter routes batch-AI workloads across providers." } ] } } ], "webhook": { "url": "https://example.com/hooks/batchrouter", "secret": "a-strong-shared-secret" }, "metadata": { "project": "docs-demo" } }' -
Store
batch.id(batch_…) and poll for completion. See Results.
Create a batch (file-based)
For large or binary inputs, upload a JSONL file first, then reference its file_id. Use input_file_id instead of items.
curl -X POST https://api.batchrouter.com/v1/batches \
-H "Authorization: Bearer $BATCHROUTER_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"quote_id": "qlock_abc123",
"input_file_id": "file_xyz789",
"sla_tier": "standard",
"routing_mode": "sla_aware"
}'The 202 response
A successful create returns 202 Accepted — the batch is queued for routing, not yet processed. The body wraps the batch summary (abridged here; the full batch object also carries sla_tier, routing_mode, pricing_estimate, file ids, and more):
{
"batch": {
"id": "batch_9f3c2a1b7e4d46c8a1b2c3d4e5f60718",
"state": "priced",
"counts": {
"total": 1,
"pending": 1,
"running": 0,
"completed": 0,
"failed": 0,
"canceled": 0
},
"created_at": "2026-06-18T10:00:00Z",
"deadline_at": "2026-06-19T10:00:00Z"
}
}| Field | Meaning |
|---|---|
batch.id | The batch_… identifier you poll and fetch results with. |
batch.state | The lifecycle field. A new batch is priced, then advances queued → dispatching → running → finalizing → completed. See the status lifecycle. |
batch.counts | Per-item state counts: total, pending, running, completed, failed, and canceled. counts.total is the number of items accepted into the batch. |
batch.created_at | When the batch was created. |
batch.deadline_at | The 24-hour SLA deadline by which the batch is expected to complete. |
Replaying the same Idempotency-Key returns this same response rather than creating a second batch.
Errors
| Status | Cause | Fix |
|---|---|---|
400 | Preflight failed (bad JSONL, both/neither input, context window exceeded, unsafe webhook URL). | Inspect error.details.preflight; see JSONL format. |
401 | Missing or invalid key. | Check your Authorization header — see Authentication. |
402 | Insufficient credits. | Check GET /v1/auth/account; top up credits in the dashboard at https://batchrouter.com/app/billing. |
409 | Conflict (e.g. reused key on a different payload). | Use a fresh Idempotency-Key for a new batch. |