Migrate from OpenRouter
Onlist speaks the same OpenAI- and OpenRouter-compatible /v1 surface you already
call. Migrating is a two-line change: point your SDK at https://onlist.io and
swap in an Onlist token. Your request bodies, response shapes, and the official SDKs
stay the same.
This page shows the swap, lists what carries over unchanged, and documents every way Onlist behaves differently from OpenRouter. The differences are real and specific; we list them all rather than imply parity we do not deliver.
The swap
Change two values. Everything else in your code is untouched.
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key="sk-or-v1-...",
)
resp = client.chat.completions.create(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)Only base_url and api_key changed. The Onlist token is a plain bearer token issued
in the buyer dashboard — there is no sk-or- prefix (a leading sk- is tolerated and
stripped). See Authentication for header normalization across the
OpenAI, Anthropic, and Gemini SDKs.
What stays the same
These work on Onlist exactly as they do on OpenRouter — no code changes:
- Official SDKs. There is no bespoke Onlist SDK. Use the official OpenAI, Anthropic,
and Gemini SDKs pointed at Onlist via
base_url. - Request and response bodies. The OpenAI chat/completions, Anthropic Messages, and
Gemini
generateContentschemas are unchanged. Modeled fields are forwarded upstream. - The
providerobject. The OpenRouter-compatibleproviderrouting object is read on JSON endpoints, with the same field names. - The
models[]fallback list. The top-levelmodelsarray is honored as a try-in-order fallback (the bodymodelis rewritten per attempt), gated byprovider.allow_fallbacks. - Streaming.
stream: truereturns an SSE stream of chunks terminated bydata: [DONE]. See Streaming. - Tools and structured outputs. Tool/function calling and JSON/structured-output request fields pass through unchanged.
What is different
Every documented divergence from OpenRouter. Each row pairs the OpenRouter behavior with what Onlist does instead.
| Area | OpenRouter | Onlist |
|---|---|---|
| Token prefix | Tokens are prefixed sk-or-... | Plain bearer token, no sk-or- prefix (a leading sk- is tolerated and stripped) |
Unknown provider fields | Validated; bad fields can error | Accepted and silently ignored (not 400), then reported in X-Onlist-Warnings |
provider.only[] | Multi-provider allowlist | Single pin — only only[0] is honored; the rest are dropped and reported as ignored: provider.only[1:N]. Use provider.allow for a multi-provider allowlist |
onlist/auto / openrouter/auto | Per-prompt smart model selection | One static platform-default model, not per-prompt selection (422 on endpoints that do not support it) |
usage.cost | Returned in the response body | Not in the response body. Cost is out of band — correlate a request to its cost via the X-Onlist-Route-Id header |
stream_options.include_usage | Optional; you set it | Forced to true when streaming (a terminal usage chunk is required for platform settlement) |
Privacy (zdr, data_collection) | Routes to matching providers | Universal provider self-attestation — accepted but not platform-verified and not end-to-end (the upstream may still retain). Does not influence routing; warned |
| Rate-limit headers | X-RateLimit-* headers | No X-RateLimit-* headers. Only Retry-After on 429/503 |
/v1/moderations | Available | Not yet available — returns 502 |
/v1/audio/transcriptions, /v1/audio/translations | Available | Not yet available — return 501 |
plugins and the :online model suffix | Web search / file parsing | Not supported. Plugins are a no-op; :online is unsupported |
min_p, top_a, repetition_penalty | Forwarded as sampling extensions | Dropped. These (and other unknown top-level body keys) are silently removed, with no X-Onlist-Warnings entry for those top-level drops |
require_parameters and quantizations are likewise accepted but not implemented
(silently ignored, with a warning). For the full provider field reference and how to
read X-Onlist-Warnings, see Provider routing.
The three that change behavior most
Three differences will surprise you if you assume OpenRouter behavior:
provider.onlyis a single pin. Only the first slug is routed to; additional entries are dropped and reported inX-Onlist-Warnings. If you relied on a multi-provider allowlist, switch toprovider.allow. See Provider routing.autois a static default, not smart selection.onlist/autoandopenrouter/autoresolve to one fixed platform-default model. If your code expects per-prompt model picking, name a specific model instead. See Models.usage.costis out of band. Cost is not in the response body. Capture theX-Onlist-Route-Idresponse header and look the cost up afterward. See Usage & cost and Errors.
Next steps
- Provider routing — Every provider field, single-pin semantics, and how to read X-Onlist-Warnings.
- Privacy & data handling — The provider self-attestation model — what zdr and data_collection actually do.
- Usage & cost — Token usage fields, out-of-band cost, and correlating via X-Onlist-Route-Id.
- Errors — The error envelope, string code slugs, and the code-to-HTTP table.