Routing
Every request goes through the router. For an explicit model the router’s job is small — resolve it to a healthy upstream that serves it. For an auto-routed model it also chooses which provider and region serve it. This page describes both.
Three modes
- Explicit model. You send a full model ID (see available models); the router resolves it to the provider and region encoded in that ID and sends it upstream.
- Auto-routed model (
auto/<creator>/<model>). You name the model; the router picks which provider serves it and from where. See auto-routing a model below. - Alias (
alias/<name>). You send the name of one of your account’s model aliases; the router resolves it to the stored route and continues exactly like an explicit model. Thealias/prefix (likeauto/) is a reserved namespace — no provider can ever be named that.
The model field is required. There is no per-request route object
and no separate routing parameters — everything the router needs comes
from the model ID you send and the model catalogue. To constrain
provider or region, encode it in the model ID (below).
lowrouter/auto has been removed. It used to let the router pick
the model as well as the route, ranked on carbon per token — which
meant the greenest answer was always the smallest model in the
catalogue, a capability downgrade you had not asked for and could not
see. Choosing the model is your decision; choosing the route is ours.
Requests naming it now return 400 and point here.
Auto-routing a model
The same model is often served by several providers, in several
regions, at different prices and on very different electricity grids.
auto/<creator>/<model> lets you name the model and leave that choice
to the router:
auto/mistralai/mistral-large-2512You get the same canonical model — same weights, same output. Only the
route is decided for you. Auto-routed IDs appear in GET /v1/models alongside the explicit ones
(see available models) for every model served by more
than one route, so they are selectable in any OpenAI-compatible
client. They are omitted when you filter that listing with
?jurisdiction= — auto ranking is global and cannot promise to stay
inside a facet, so a filtered catalogue lists only explicit IDs, which
name their provider and region outright.
The priority order
The order is fixed and identical for every account — there is no policy to select. Each step only breaks ties left by the previous one:
- EU-sovereign routes first. A route counts as sovereign only when both halves hold: the provider is an EU-sovereign company and the serving region is in the EU/EEA. An EU-sovereign company serving from a US datacenter does not qualify, and neither does a US-controlled provider serving from Frankfurt.
- Lowest grid carbon intensity. A region whose grid intensity we have no data for ranks below every region we do — an unknown is treated as unknown, never as the greenest.
- Lowest price. Because carbon is decided first, two identically-priced routes can never resolve to the dirtier grid. A route with no published price ranks below every priced one.
- Round-robin across whatever is still tied.
Routes a provider has taken out of service are excluded outright, at every step.
Sovereignty here is a preference, not a guarantee
If a model has no sovereign route at all, an auto request does not
fail — it falls through to the greenest, then cheapest route
available anywhere. auto/anthropic/claude-sonnet-5 routes, to a
non-EU provider.
The response is what keeps this honest.
lowrouter_metadata.eu_sovereign tells you whether the route you got
was sovereign, alongside provider and region, and the model
field carries the fully resolved four-segment ID. If you need
sovereignty as a hard constraint rather than a preference, use an
explicit ID — it fails loudly instead of falling back outside your
constraint.
lowrouter_metadata.routing_reason names the step that decided:
eu_sovereign_preferred, lowest_carbon_intensity, lowest_cost,
round_robin, or only_route when the model has just one route.
Budgeting an auto ID before you send
An auto ID has no single price or carbon figure — the route is chosen per request — so its catalogue entry carries the envelope instead:
{
"id": "auto/openai/gpt-oss-120b",
"auto_routing": {
"candidates": [
"berget/openai/gpt-oss-120b/se-sto",
"nebius/openai/gpt-oss-120b/fi-hel",
"aws-bedrock/openai/gpt-oss-120b/br-gru"
],
"pricing_range": {
"prompt_per_1m_tokens": { "min": 0.13, "max": 0.20 },
"completion_per_1m_tokens": { "min": 0.50, "max": 0.80 },
"currency": "EUR"
},
"carbon_range": {
"carbon_per_token_gco2e": { "min": 1.2e-6, "max": 6.1e-6 },
"grid_carbon_intensity_gco2_per_kwh": { "min": 12, "max": 80 }
}
}
}candidatesis every concrete route the ID can resolve to, in the order a request would try them: the route auto would pick right now first, then that provider’s other regions, then the remaining providers in priority order. Each is a four-segment ID listed elsewhere in the same catalogue, so you can join it against that entry’sregions[]for exact figures, or pin it outright.pricing_rangeandcarbon_rangeare the bounds across those candidates — including the ones a failover may fall through to. The response’susage.costandlowrouter_metadata.carbonfor any request sent to this ID fall inside them.- A range is published only when every candidate carries that
figure. If one route has no published price, or no energy data, the
corresponding range is omitted rather than narrowed —
candidatesstill names the route, so you can see which one the gap is.
The envelope is derived from the same route set the router ranks, at the moment the catalogue is built, so it cannot drift from what a request will actually do.
Grid intensity is resolved the same way on both sides — the listing and
the response — by the most specific figure available: the serving
region’s own, else its country or bloc average (an eu region is
accounted at the EU average), else the worldwide average. A region’s
carbon_metrics key tells you which: the locode for a regional figure,
<CC>-AVERAGE or GLOBAL-AVERAGE for an averaged one.
What auto-routing does not do
- It never changes which model you get — that is your choice, and the whole point of naming it.
- It does not score latency or answer quality.
- It does not consult your aliases:
auto/is its own namespace and always applies the order above. - A version shorthand still works —
auto/mistralai/mistral-largeresolves to the latest version — but a region does not: there is no 4th segment to write, because choosing the region is the point.
Pinning a provider or region
Region and provider are part of the model ID, not a separate
field. The public ID has the form
{provider}/{creator}/{model}[/{locode}]:
| Goal | How |
|---|---|
| Pin the provider | Use an explicit model ID — the first segment is the provider, e.g. vertex/anthropic/claude-opus-4.6. |
| Pin the region | Append a UN/LOCODE as the 4th segment, e.g. vertex/anthropic/claude-opus-4.6/sg-sin. |
| Default region | Omit the 4th segment; see how IDs resolve below. |
If you request a region a model isn’t served in, the request is rejected rather than silently served elsewhere — the region you pin is the region you get.
How IDs resolve
You do not have to write the full four-segment ID. What you leave out is filled in from the catalogue, most-specific first, and the response always tells you what you actually got.
| You send | What happens |
|---|---|
mistral/mistralai/mistral-large-2512/eu | Used exactly as written. |
mistral/mistralai/mistral-large-2512 | Region defaulted — resolves to /eu. |
mistral/mistralai/mistral-large | Version and region defaulted — resolves to mistral-large-2512/eu. |
anthropic/anthropic/claude-sonnet | Resolves to the latest sonnet at /global. |
Missing version resolves to the most recent version of that same
model. The version is the only thing that moves: claude-sonnet
resolves to the newest sonnet, never to an opus, and
mistral-large never to a mistral-small. A shorthand that would be
ambiguous between genuinely different models — openai/openai/gpt-oss,
where gpt-oss-20b and gpt-oss-120b are separate models rather than
two versions of one — is rejected, not guessed.
Missing region resolves to global when the model has a global
endpoint. When it doesn’t, it resolves to the model’s only region, or,
where several exist, to the lowest-carbon one. A region whose grid
intensity we don’t have data for is never chosen over one we do — an
unknown is treated as unknown, not as zero.
The response’s model field always carries the fully resolved
four-segment ID, so a request is reproducible from its own response —
copy it back and you pin exactly what ran. lowrouter_metadata.region
reports the region independently.
Pinning still fails loudly
Resolution only ever fills in what you left out. It never overrides what you wrote:
- A version that doesn’t exist is an error, not a nudge to the nearest
one.
mistral/mistralai/mistral-large-9999→404. - A region the model isn’t served in is an error, not a reroute.
mistral/mistralai/mistral-large-2512/us-iad→503. - An explicit
/globalon a model with no global endpoint is an error, not a silent switch to a regional row.
That asymmetry is deliberate. Defaults exist so the IDs in our own
docs and model list are callable as printed; pins exist so that when
you name a version or a jurisdiction, the thing you named is the thing
that ran. Aliases are stricter still: an alias target must
be a full, concrete {provider}/{creator}/{model}/{locode} triple, so
a stored route can never drift under you.
Worked scenario: EU-only first, lowest carbon second
Say a data-residency commitment requires EU inference, and within that constraint you want the lowest carbon available. The order matters: sovereignty is a constraint, carbon is an optimisation, and the two are handled by different mechanisms.
- Encode the constraint. Filter the providers page to EU providers, then compare per-token carbon across the models that remain in the catalogue.
- Pin the winner with an explicit ID, e.g.
mistral/mistralai/mistral-large-2512/eu— or better, point an alias at it, so you can re-run the comparison next quarter and repoint without touching client code. - Verify, don’t trust. Check
lowrouter_metadata.regionon the responses; the pin is proven on every call.
Note what you did not do: ask the router for “EU only”.
Auto-routing does not treat a jurisdiction as a hard constraint.
auto/<creator>/<model> does prefer EU-sovereign routes above
everything else, and reports whether it got one — but when a model has
no sovereign route it falls back rather than failing, so it is an
optimisation, not a commitment. Per-key jurisdiction restrictions are
coming; until then the honest tool for a hard constraint is the
explicit ID, which fails loudly rather than falling back outside it.
What happens on failure
LowRouter does not silently move your request to a different provider:
an explicit ID is a pin, and an auto-routed request commits to the
winner of the ranking. If that route fails, the request returns 503
naming the provider that failed rather than reporting a different one
you never asked for. Substituting a provider would bill against
another pricing row and serve from another jurisdiction than the ID
promised.
Which providers were attempted is reported on the response in
lowrouter_metadata.providers_attempted, alongside
lowrouter_metadata.fallback_occurred.
What the router does not do
- It does not choose the model — that is what naming it is for.
- It does not benchmark output quality — it optimises for sovereignty, then carbon, then cost, not “is the answer good”.
- It does not accept a per-request
routeobject or aprefer_low_carbonflag. Low carbon is already ranked ahead of price, so there is nothing to opt into.
