BatchRouter Docs

Poll & retrieve results

Track a BatchRouter batch through its state lifecycle, poll with backoff, paginate results, and fetch a signed artifact for large batches.

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

State lifecycle

A batch advances through a fixed sequence of states. Poll GET /v1/batches/{batchId} and read batch.state — the batch is wrapped under a top-level batch key, and the field is named state (there is no status field on a native batch) — until it reaches a terminal state.

PhaseStateMeaning
ActivereceivedThe batch record has been created.
ActivevalidatedIntake validation is complete; the batch is not yet priced.
ActivepricedAccepted and priced. This is the state a new batch has in the 202 create response.
ActivequeuedWaiting for a routing slot.
ActivedispatchingBeing handed off to the chosen provider lane(s).
ActiverunningThe provider is running your items.
ActivefinalizingAll items have an outcome; BatchRouter is assembling results and delivery artifacts.
TerminalcompletedDone — results are available.
TerminalfailedThe batch could not complete.
TerminalcanceledYou canceled the batch. Note the single-l spelling.
TerminalexpiredThe SLA deadline passed before completion.

Stop polling once state is completed, failed, canceled, or expired. Results are only available on completed. See the status lifecycle reference for what each state means and what to do at each one.

Prefer webhooks over tight polling

If you set a webhook on create (or an org-level default via PUT /v1/auth/account/delivery-webhook), BatchRouter pushes a signed event when the batch reaches a terminal state — no polling loop required. See Webhooks & delivery. Polling is the fallback when you have no webhook endpoint.

Poll batch status

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

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

A successful response wraps the batch under batch, which includes its id, current state, per-item counts, created_at, deadline_at, sla_tier, and the routing_mode you submitted. counts breaks the items down into total, pending, running, completed, failed, and canceled.

Polling with backoff

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 state.

import time, requests, os

TERMINAL = {"completed", "failed", "canceled", "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()["batch"]
        if batch["state"] in TERMINAL:
            return batch
        time.sleep(delay)
        delay = min(delay * 2, max_delay)

Start at roughly a 5-second interval and back off toward ~60 seconds. Batches target a 24-hour SLA, so a short polling cadence on a freshly created batch mostly wastes requests — webhooks are the efficient path for completion.

Retrieve results (paginated)

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

curl "https://api.batchrouter.com/v1/batches/batch_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.
  • status — completed 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)

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.

# 1. Get the signed URL
curl https://api.batchrouter.com/v1/batches/batch_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.

Use /artifact-url for bulk retrieval and /results for paging through or sampling output interactively. The artifact is the most efficient way to pull a complete large batch in one pass.

Next steps

On this page