BatchRouter Docs

SLA-aware lanes & routing

Control how BatchRouter places your batch — the 24-hour SLA, routing modes, multi-lane routing, rejection receipts, and privacy tiers explained.

BatchRouter doesn't just forward your batch to one provider — it evaluates every eligible provider/model lane, prices them, and picks placement based on the controls you set. This page explains the SLA every batch gets, the controls you set (routing_mode, privacy_tier), how multi-lane routing works, and how to read the rejection receipts that explain why a lane wasn't used.

Both controls are set per quote and per batch. Set them when you request a quote so the estimate reflects your constraints, then pass the same values (plus the quote_id) to POST /v1/batches.

What a lane is

A lane is one candidate execution path — a specific provider running a specific model that could fulfill some or all of your items. When you request a quote, BatchRouter expands your items into every eligible lane, prices each one, and returns them as quote_lanes. The selected lane(s) carry selected: true; the rest come back with a rejection receipt explaining why they were skipped.

Because different items in one batch can declare different models, a single customer batch can be split across multiple internal lanes — each handling the items it's best suited for. You still get one batch.id, one status to poll, and one merged result set; the per-lane execution is BatchRouter's concern. The completed batch's billing receipt lists the provider_lanes that actually ran and the rejected_lanes that didn't.

Multi-lane routing optimizes for cost, SLA, and live capacity simultaneously. If one provider is at capacity for part of your batch, eligible items can still flow to another lane instead of failing the whole batch.

The SLA (sla_tier)

Every batch has one SLA: BatchRouter targets completion within 24 hours. You don't choose a tier.

The sla_tier field accepts standard, flex, and priority for compatibility, and all three are treated as standard. The value doesn't change the deadline or the price, and the batch reports sla_tier: "standard" whichever one you send. The default is standard, so you can leave the field out.

{ "sla_tier": "standard" }

The deadline is surfaced as deadline_at on the batch you poll. A sweep can ask for an earlier deadline with deadline_seconds. The SLA also affects which lanes are eligible: a lane that can't finish the work before the deadline is not selected for that batch.

Routing modes (routing_mode)

routing_mode controls which lanes BatchRouter is allowed to consider and how it ranks them. Default is cheapest.

ModeWhat it does
cheapestLowest total price across all eligible lanes (default).
sla_awareBalances price against the lane's ability to comfortably hit the 24-hour SLA.
public_onlyRoutes only to public provider lanes (OpenAI, Anthropic, and other public APIs).
edge_onlyRoutes only to edge / BatchProviderApi provider lanes.
hybridConsiders both public and edge lanes and picks the best fit.
privacy_constrainedRestricts to lanes that satisfy your privacy requirements (pair with privacy_tier).
{ "routing_mode": "sla_aware" }

Start with cheapest and let the quote show you the lane spread. Switch to sla_aware if a cheap lane is cutting your deadline close, or to public_only / edge_only when you have a hard requirement about where compute runs.

Privacy tiers (privacy_tier)

privacy_tier constrains placement by the privacy tiers each provider declares. Default is standard. Each lane carries a data-privacy proof (declared retention window, output handling, declared privacy tiers). BatchRouter checks the declared tiers against this value. It publishes the declared retention window but does not compare it against your batch.

TierRouting constraint
standardNo additional data-handling constraint (default).
confidentialRoutes only to providers that declare the confidential tier. Not a zero-data-retention guarantee: the router adds that check only when routing_mode is privacy_constrained, so in every other mode a confidential batch can reach a provider that declares no zero data retention.
restrictedOnly providers that declare the restricted tier are eligible. Like every tier, it is the provider's own declaration, and no built-in provider declares it.
{ "routing_mode": "privacy_constrained", "privacy_tier": "confidential" }

This pairing adds the zero-data-retention check: only lanes whose provider declares data_collection: deny and zero data retention (supports_zdr) stay eligible. No built-in provider declares zero data retention today, so read supports_zdr in the model catalog before you rely on this pairing.

If no eligible lane can satisfy the requested tier, the lanes that fail it come back rejected with privacy_tier_mismatch — and if none qualify, quote creation fails rather than silently routing you somewhere that doesn't meet the constraint.

Rejection receipts

Every non-selected lane in a quote (and every rejected lane in a completed batch's receipt) carries a normalized rejection receipt: a stable rejection_code, a customer-safe rejection_reason, and a rejection_receipt object with the failed eligibility checks. This makes routing auditable — you can always see why a cheaper or different lane wasn't used.

Common rejection codes:

CodeMeaning
capacity_fullThe lane had no live routable capacity for your SLA/volume.
stale_heartbeatThe provider's capacity heartbeat was stale, so it wasn't treated as live supply.
context_window_exceededAn item exceeds the model's context window on that lane.
privacy_tier_mismatchThe lane can't satisfy your requested privacy_tier.
region_unavailableThe lane's provider doesn't match your allowed_regions. A provider that declares the countries it runs in must have every one of them covered by your list. A provider marked global always passes this check, so allowed_regions is not a residency guarantee. See Region matching.
missing_web_search (and similar missing_<tool>)The lane's provider doesn't declare a hosted tool you required via required_tools.

Inspect rejected lanes straight from the quote response:

curl -s https://api.batchrouter.com/v1/quotes/model \
  -H "Authorization: Bearer $BATCHROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "models": ["gpt-5.4-mini"],
    "routing_mode": "sla_aware",
    "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."}]}}
    ]
  }' | jq '.quote_lanes[] | {provider, model, selected, rejection_code, rejection_reason}'

gpt-5.4-mini is a model id from the live catalog. List the models and the providers/regions/tools each one actually supports via GET /v1/catalog/models before pinning a model or region.

Putting it together

A single batch can combine both controls. For example, "only lanes whose provider declares the restricted tier, denies data collection, and declares zero data retention":

{
  "quote_id": "qlock_...",
  "routing_mode": "privacy_constrained",
  "privacy_tier": "restricted"
}

Set the same values on the quote first so the price you're quoted reflects the constrained lane set — then carry them onto the batch.

The router checks those declarations; it does not check that a node is private. No built-in provider declares the restricted tier or zero data retention today, so with built-in providers alone this combination finds no eligible lane.

Next steps

On this page