Skip to content

Errors & limits

Every BatchRouter error returns the same JSON envelope so you can handle failures consistently, and submission endpoints run a free preflight check before they ever spend credits. This page covers the error shape, the status codes you should handle, how to recover from a 402, and how Idempotency-Key makes retries safe.

All error responses (any non-2xx) share one shape. The envelope always carries a stable, machine-readable code and a human-readable message; some errors add a details object.

{
"error": {
"code": "insufficient_credits",
"message": "Your credit balance is too low to reserve this batch.",
"details": {}
}
}

Branch on error.code (stable) rather than on message (may change). Treat any unknown code as a generic failure for its status class.

StatusWhen you get itWhat to do
400Invalid request body or parameters, or a batch/quote preflight failure (see below).Fix the request. If error.details.preflight is present, surface the listed issues and retry.
401Missing or invalid API key.Send Authorization: Bearer br_live_…. Check the key wasn’t revoked and matches the environment (test keys don’t work on prod).
402Insufficient credits to reserve the batch.Top up credits, then retry. See Recovering from 402.
403Authenticated, but not authorized for this resource (for example, a batch belonging to another org).Use a key for the owning org; don’t retry as-is.
404Resource not found (unknown batchId, file_id, etc.).Verify the id. A just-created resource is available immediately, so a 404 means the id is wrong or not yours.
409Conflict with current state: a duplicate Idempotency-Key sent with a different payload, or an action on a batch already in a terminal state.Don’t blindly retry. See Idempotency & safe retries.
429Rate limited.Back off and retry. Honor the Retry-After header when present; otherwise use exponential backoff with jitter.
5xxTransient server-side error.Retry idempotent requests with backoff. For POST /v1/batches, reuse the same Idempotency-Key so a retry can’t create a duplicate.

Before charging credits, POST /v1/quotes/model and POST /v1/batches run a preflight check on your input. When it fails, you get a 400 whose error.details.preflight holds a structured result you can show the user.

{
"error": {
"code": "batch_preflight_failed",
"message": "Batch preflight validation failed.",
"details": {
"preflight": {
"ok": false,
"errors": [
{
"category": "context_window",
"code": "preflight_context_window_exceeded",
"message": "Item item-1 exceeds the model's context window.",
"action": "Reduce the input length or pick a model with a larger context window.",
"path": "items[0]"
}
],
"warnings": []
}
}
}
}

Each issue carries a category, a stable code, a message, and a customer-safe action (and optionally a path). The category values you’ll see:

CategoryWhat failedCommon fix
jsonl_shapeJSONL validation — a line isn’t a valid item, or required fields are missing.Fix the malformed line(s); each item needs customer_item_id, operation, model, and input.
file_typeUnsupported uploaded file type for the referenced purpose.Upload a supported type (for model_input, JSONL or the documented binary inputs).
context_windowAn item’s input exceeds the model’s context window.Shorten the input or choose a larger-context model from GET /v1/catalog/models.
tool_supportA required_tools entry isn’t offered by any eligible lane.Drop the tool, or pick a model/provider that advertises it in the catalog.
json_schemaA structured-output schema or runtime capability isn’t satisfiable.Align the schema with what the model/provider supports.
webhookThe webhook is unsafe — e.g. non-HTTPS or a disallowed host (code: "https_required").Use an https:// URL that resolves to a public host, with a secret of at least 8 characters.
routingNo eligible lane remains after applying your constraints.Relax routing_mode, privacy_tier, max_price, or region constraints.

Recovering from 402 (insufficient credits)

Section titled “Recovering from 402 (insufficient credits)”

A 402 means you don’t have enough credits to reserve the batch. Quotes are free; credits are reserved when you call POST /v1/batches. Recover in two steps.

  1. Read your current balance from your account:

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

    The response includes credit_balance as a money object ({ currency, amount }).

  2. Buy credits from the dashboard. Top-ups are handled through a Stripe-hosted checkout on the billing page — the public API has no credit-purchase endpoint, so send the user to:

    https://batchrouter.com/app/billing

    After checkout completes and the balance updates, retry your POST /v1/batches call. Reuse the same Idempotency-Key from the original attempt so the retry resolves to one batch.

POST /v1/batches requires an Idempotency-Key header (8–128 characters). It makes batch creation safe to retry: if a request times out or returns a 5xx, resend the exact same request with the same key.

  • Same key, same payload → BatchRouter returns the original 202 response and does not create a second batch.
  • Same key, different payload409 Conflict. The key is already bound to a different request; generate a fresh key for the new payload.
Terminal window
# First attempt — network drops before you see a response
curl -X POST https://api.batchrouter.com/v1/batches \
-H "Authorization: Bearer $BATCHROUTER_API_KEY" \
-H "Idempotency-Key: order-2026-06-18-abc123" \
-H "Content-Type: application/json" \
-d @batch.json
# Safe retry — identical key + body returns the same batch, no duplicate
curl -X POST https://api.batchrouter.com/v1/batches \
-H "Authorization: Bearer $BATCHROUTER_API_KEY" \
-H "Idempotency-Key: order-2026-06-18-abc123" \
-H "Content-Type: application/json" \
-d @batch.json

Guidance:

  • Generate one key per logical operation (a UUID or a deterministic hash of your batch payload both work) and persist it before sending the request.
  • Retry only on network errors, 429, and 5xx. Don’t auto-retry a 400/401/403 — the request itself needs fixing.
  • For 429, honor Retry-After; otherwise use exponential backoff with jitter.
  • GET requests (polling status, fetching results) are naturally safe to retry and need no key.