Skip to content

Poll & retrieve results

After you submit a batch, BatchRouter routes and executes it asynchronously. This guide shows how to track a batch through its status lifecycle and pull the results back out — either page by page or as a single signed download.

A batch advances through a fixed sequence of statuses. Poll GET /v1/batches/{batchId} and read the top-level status field until it reaches a terminal state.

PhaseStatusMeaning
ActivependingAccepted; not yet enqueued.
ActivequeuedWaiting for a routing slot.
ActiveroutingBatchRouter is selecting a provider lane.
ActivedispatchedHanded off to the chosen provider.
ActiveprocessingProvider is running your items.
ActivecompletingAssembling and finalizing results.
TerminalcompletedDone — results are available.
TerminalfailedThe batch could not complete.
TerminalcancelledYou cancelled the batch.
TerminalexpiredThe SLA deadline passed before completion.

Stop polling once status is completed, failed, cancelled, or expired. Results are only available on completed.

Send your key as a bearer token and read the status field. Pass ?include_billing_receipt=true to embed the finalized billing receipt in the same response once the batch completes.

Terminal window
curl https://api.batchrouter.com/v1/batches/bat_123 \
-H "Authorization: Bearer $BATCHROUTER_API_KEY"

A successful response includes the batch id, current status, item_count, created_at, sla_deadline, and the routing_mode / sla_tier you submitted.

Batches are long-running, so poll on an interval — not in a tight loop. Use exponential backoff with a cap, and always stop on a terminal status.

import time, requests, os
TERMINAL = {"completed", "failed", "cancelled", "expired"}
def wait_for_batch(batch_id, max_delay=60):
delay = 5
while True:
res = requests.get(
f"https://api.batchrouter.com/v1/batches/{batch_id}",
headers={"Authorization": f"Bearer {os.environ['BATCHROUTER_API_KEY']}"},
)
batch = res.json()
if batch["status"] in TERMINAL:
return batch
time.sleep(delay)
delay = min(delay * 2, max_delay)

Once status is completed, fetch assembled output from GET /v1/batches/{batchId}/results. Results are paginated: use ?limit (1–1000, default 100) and follow next_cursor.

Terminal window
curl "https://api.batchrouter.com/v1/batches/bat_123/results?limit=100" \
-H "Authorization: Bearer $BATCHROUTER_API_KEY"

The response wraps a results array and a next_cursor (a string, or null on the last page). Each entry has this shape:

{
"customer_item_id": "item-1",
"status": "completed",
"output": { "messages": [ { "role": "assistant", "content": "..." } ] },
"error": null
}
  • customer_item_id — the id you assigned on submit, for joining back to your input.
  • statuscompleted or failed for that item.
  • output — the model output (object, or null when the item failed).
  • error — failure detail (object, or null when the item succeeded).

Keep requesting pages while next_cursor is non-null. Items can succeed or fail independently, so always check each item’s status rather than assuming a completed batch means every item passed.

Download the full artifact (large batches)

Section titled “Download the full artifact (large batches)”

For large batches, paginating thousands of items is slow. Instead, request a short-lived signed URL for the complete results JSONL artifact from GET /v1/batches/{batchId}/artifact-url, then download it directly.

Terminal window
# 1. Get the signed URL
curl https://api.batchrouter.com/v1/batches/bat_123/artifact-url \
-H "Authorization: Bearer $BATCHROUTER_API_KEY"
# 2. Download the artifact (no auth header — the URL is pre-signed)
curl -o results.jsonl "<url from step 1>"

The response is { "url": "<signed>", "expires_at": "<timestamp>" }. The signed URL is short-lived — download it promptly and re-request if it expires. The artifact is newline-delimited JSON (JSONL), one result object per line, using the same per-item shape as /results.