Provider Routing
Onlist routes every request to a platform provider that has an active listing for the
model you asked for. By default Onlist picks for you. To take control, add an optional
provider object to the JSON request body. It is the one Onlist-specific extension to the
OpenAI/OpenRouter wire format — everything else in your request is unchanged.
curl https://onlist.io/v1/chat/completions \
-H "Authorization: Bearer YOUR_ONLIST_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o",
"messages": [{"role": "user", "content": "Hello from Onlist"}],
"provider": { "sort": "price" }
}'
The provider field
A bare string is shorthand for pinning a single provider:
"provider": "alice-shop"
is exactly equivalent to:
"provider": { "only": ["alice-shop"] }
Provider slugs are bare and must match ^[a-z0-9_-]{1,64}$. There is no sub-slug
addressing: a slug containing a / returns 400 provider_sub_slug_removed.
The provider field is read only on JSON endpoints (/v1/chat/completions,
/v1/completions, /v1/embeddings, /v1/messages, /v1/responses, /v1/rerank,
/v1/audio/speech, /v1/images/generations, /v1/videos). It is not read on multipart
endpoints (/v1/images/edits, audio transcription/translation) or on the Gemini /v1beta
path — only model is read there. This mirrors OpenRouter.
Field reference
Onlist honors a strict subset of OpenRouter's provider controls. Unsupported fields are
accepted and silently ignored (never a 400) and reported in the X-Onlist-Warnings
response header so you can detect them.
| Field | Type | Status | Behavior |
|---|---|---|---|
only | string[] | Honored | Single pin — only only[0] is used; the rest are dropped and reported as ignored: provider.only[1:N]. For multiple providers, use allow. |
sort | "price" | "throughput" | Honored | price = cheapest first, throughput = fastest first. A :floor/:nitro model suffix overrides this. Other values (including OpenRouter's latency) are ignored + warned. |
order | string[] | Honored | Provider try-order priority for failover. Suppresses sticky-channel affinity when set. Non-string elements are skipped + warned. |
allow | string[] | Honored | Request-level allowlist, merged with token/account preferences. Draining the pool returns 503 no_provider_after_allow_deny. |
ignore | string[] | Honored | Request-level denylist. Deny wins over allow. |
allow_fallbacks | boolean | Honored | Default true. false disables cross-provider failover (and the top-level models[] fallback list). |
max_price | object | Partial | prompt/completion enforced fail-closed (drained pool → 503 no_provider_in_max_price); image/request ignored + warned. |
zdr | boolean | Ignored + warned | Accepted as universal provider self-attestation, not platform-verified, not end-to-end. Does not affect routing. See Privacy & data. |
data_collection | "allow" | "deny" | Ignored + warned | Accepted as provider self-attestation; not enforced for routing. See Privacy & data. |
require_parameters | boolean | Not implemented | Silently ignored + warned. |
quantizations | string[] | Not implemented | Silently ignored + warned. |
variant | string | Not implemented | Deprecated. Any value is ignored + warned. |
preferred_max_latency | number | Not implemented | Silently ignored + warned. |
preferred_min_throughput | number | Not implemented | Silently ignored + warned. |
Honored fields
only — pin one provider
only pins the request to a single provider. Only the first element (only[0]) is
honored. Any additional slugs are dropped and reported in X-Onlist-Warnings.
curl https://onlist.io/v1/chat/completions \
-H "Authorization: Bearer YOUR_ONLIST_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o",
"messages": [{"role": "user", "content": "Hi"}],
"provider": { "only": ["alice-shop"] }
}'Unlike OpenRouter, only is not a multi-provider allowlist — Onlist honors only[0]
and drops the rest. If you want to permit several providers, use allow instead. A pinned
slug that does not exist returns 404 provider_not_found; a pinned slug that exists but has
no listing for the model returns 422 no_listing_for_model_at_provider.
sort — price or throughput
sort chooses the ordering variant within the eligible pool.
"provider": { "sort": "price" } // cheapest provider first
"provider": { "sort": "throughput" } // fastest provider first
A model-slug suffix overrides this field: gpt-4o:floor forces price-first and
gpt-4o:nitro forces throughput-first, regardless of any sort value you send. See
Models for the suffix forms. Any other sort value (such as OpenRouter's
latency) is ignored and reported in X-Onlist-Warnings.
order — try-order priority
order is an ordered list of provider slugs. Failover advances through them in the order you
give. Setting order also suppresses sticky-channel affinity, so each request re-evaluates
from the top of your list.
"provider": { "order": ["alice-shop", "bob-relay", "carol-ai"] }
allow and ignore — allowlist and denylist
allow is a request-level allowlist (merged with any token/account preferences). ignore
is a request-level denylist. When a slug appears in both, deny wins.
"provider": {
"allow": ["alice-shop", "bob-relay"],
"ignore": ["carol-ai"]
}
If your allow/ignore rules exclude every viable provider, the request returns
503 no_provider_after_allow_deny.
allow_fallbacks — control failover
Failover across providers is on by default. Set allow_fallbacks: false to require the first
selected provider and disable cross-provider failover. This also disables the top-level
models[] fallback list (see below).
"provider": { "only": ["alice-shop"], "allow_fallbacks": false }
max_price — USD-per-token caps
max_price filters the pool to providers priced at or below your caps, in OpenRouter's native
USD-per-token format (for example 0.0000025 = $2.50 per million tokens).
"provider": {
"max_price": { "prompt": 0.0000025, "completion": 0.00001 }
}
promptandcompletionare enforced fail-closed: if no provider fits, the request returns 503no_provider_in_max_pricerather than silently overspending.imageandrequestare not implemented (the scope is chat-only): they are ignored and reported inX-Onlist-Warnings.
Because prompt/completion fail closed, an aggressive cap can drain the pool and return a 503. If you would rather get a result than a hard failure, widen the caps or omit them.
The models[] fallback list
Independently of the provider object, you can send a top-level models array — the
OpenRouter-compatible fallback list. Onlist tries each model in order, rewriting the body
model for each attempt, until one succeeds.
{
"model": "openai/gpt-4o",
"models": ["openai/gpt-4o", "anthropic/claude-sonnet-4-20250514"],
"messages": [{ "role": "user", "content": "Hi" }]
}
This list is gated by allow_fallbacks: with provider.allow_fallbacks: false, only the
primary model is attempted and models[] is not used.
The X-Onlist-Warnings header
Whenever Onlist ignores a field or fires a routing notice, it reports it in the
X-Onlist-Warnings response header. The value is a de-duplicated list joined by a comma and
a space (, ). Parse it by splitting on , .
X-Onlist-Warnings: ignored: provider.sort, ignored: provider.zdr, ignored: provider.only[1:3]
Entries take the form ignored: <field> (a dropped field) or a notice such as
cool_pool_exhausted_fallback_used. The header is present only when something was ignored or
a notice fired — a clean request returns no warnings header. Always check it in development
to confirm Onlist honored what you intended.
Next steps
- Privacy & data handling — What zdr and data_collection actually mean — provider-attested, not platform-verified.
- Errors — The 503 routing codes: no_provider_in_max_price, no_provider_after_allow_deny.
- Limits & headers — X-Onlist-Warnings, X-Onlist-Route-Id, Retry-After — and why there are no X-RateLimit-* headers.
- Models — Model id forms, onlist/auto, and the :floor / :nitro sort suffixes.