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. The alias/ prefix (like auto/) 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:

Text
auto/mistralai/mistral-large-2512

You 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:

  1. 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.
  2. 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.
  3. 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.
  4. 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:

JSON
{
  "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 }
    }
  }
}
  • candidates is 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’s regions[] for exact figures, or pin it outright.
  • pricing_range and carbon_range are the bounds across those candidates — including the ones a failover may fall through to. The response’s usage.cost and lowrouter_metadata.carbon for 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 — candidates still 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-large resolves 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}]:

GoalHow
Pin the providerUse an explicit model ID — the first segment is the provider, e.g. vertex/anthropic/claude-opus-4.6.
Pin the regionAppend a UN/LOCODE as the 4th segment, e.g. vertex/anthropic/claude-opus-4.6/sg-sin.
Default regionOmit 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 sendWhat happens
mistral/mistralai/mistral-large-2512/euUsed exactly as written.
mistral/mistralai/mistral-large-2512Region defaulted — resolves to /eu.
mistral/mistralai/mistral-largeVersion and region defaulted — resolves to mistral-large-2512/eu.
anthropic/anthropic/claude-sonnetResolves 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-9999404.
  • A region the model isn’t served in is an error, not a reroute. mistral/mistralai/mistral-large-2512/us-iad503.
  • An explicit /global on 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.

  1. Encode the constraint. Filter the providers page to EU providers, then compare per-token carbon across the models that remain in the catalogue.
  2. 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.
  3. Verify, don’t trust. Check lowrouter_metadata.region on 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 route object or a prefer_low_carbon flag. Low carbon is already ranked ahead of price, so there is nothing to opt into.