Skip to content

SLA-aware lanes & routing

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 those controls (sla_tier, routing_mode, privacy_tier), how multi-lane routing works, and how to read the rejection receipts that explain why a lane wasn’t used.

All three 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.

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.

sla_tier sets the deadline BatchRouter commits to. Default is standard.

TierDeadlineUse it when
standard24h SLA (default)The normal balance of speed and price.
flexUp to 48h, lower priceYou’re not in a hurry and want the cheapest placement.
priorityExpeditedYou need results back sooner than standard.
{ "sla_tier": "flex" }

The deadline is surfaced on the batch you poll. Tier choice also affects which lanes are eligible — a lane that can’t meet priority within its capacity window is rejected for that batch.

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 your sla_tier.
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" }

privacy_tier constrains placement by data-handling guarantee. Default is standard. Each lane carries a data-privacy proof (retention behavior, output handling, declared privacy tiers) that BatchRouter checks against this value.

TierRouting constraint
standardNo additional data-handling constraint (default).
confidentialRoutes only to providers with data-retention opt-out.
restrictedRoutes only to private BatchProviderApi nodes.
{ "routing_mode": "privacy_constrained", "privacy_tier": "confidential" }

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.

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 isn’t available in your allowed_regions.
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:

Terminal window
curl -s https://api.batchrouter.com/v1/quotes/model \
-H "Authorization: Bearer $BATCHROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"models": ["gpt-4o-mini"],
"routing_mode": "sla_aware",
"items": [
{"customer_item_id":"item-1","operation":"responses","model":"gpt-4o-mini","input":{"messages":[{"role":"user","content":"Summarize: BatchRouter routes batch-AI workloads across providers."}]}}
]
}' | jq '.quote_lanes[] | {provider, model, selected, rejection_code, rejection_reason}'

A single batch can combine all three controls. For example, “run this confidentially, only on private nodes, and don’t sweat the deadline”:

{
"quote_id": "qlock_...",
"sla_tier": "flex",
"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.