From OpenRouter
Field-by-field mapping from OpenRouter to ultrarouter.
ultrarouter is OpenRouter-shaped. In most cases you change the base URL and the
key and everything works — provider-prefixed model ids, the models fallback
array, provider preferences, and per-request stats all carry over.
- baseURL: "https://openrouter.ai/api/v1"
+ baseURL: "http://localhost:8080/v1" // or your gateway URL
- apiKey: "sk-or-v1-…"
+ apiKey: "ur_sk_live_…"Field-by-field mapping
| OpenRouter | ultrarouter | Notes |
|---|---|---|
model: "openai/gpt-4o" | model: "openai/gpt-5.2" | Provider-prefixed ids, same shape. |
models: [...] | models: [...] | Ordered fallbacks — identical. |
provider: { order, ignore, quantizations, data_collection, allow_fallbacks } | same | Same provider-preferences object. |
:nitro / :floor suffixes | :nitro / :floor | Throughput / price sort — supported. |
GET /api/v1/generation?id= | GET /v1/generation?id= | Post-hoc stats parity. |
GET /api/v1/models | GET /v1/models | Catalog with ?provider= / ?capability=. |
GET /api/v1/key | GET /v1/key | Key introspection. |
| HTTP-Referer / X-Title headers | (not required) | Omit; ultrarouter uses call types instead. |
Output cap & reasoning
The modern OpenAI fields are carried through to the provider — never silently dropped.
max_completion_tokens(the modern output-token budget, covering visible and reasoning tokens) is honoured everywhere. If you send bothmax_completion_tokensandmax_tokens,max_completion_tokenswins — it is the field OpenAI deprecatedmax_tokensin favour of, and the metered cost ceiling uses the same value. Each provider receives the cap in the field it accepts (see the table below).reasoning_effort(none|minimal|low|medium|high) and the OpenRouter-nativereasoningobject ({ effort, max_tokens, exclude }) are both accepted. When bothreasoning_effortandreasoning.effortare sent, the structuredreasoningobject wins.nonedisables reasoning and is always honoured (a no-op — never refused).
ultrarouter follows the OpenRouter reasoning-tokens reference:
effort-based providers receive reasoning_effort; budget-based providers receive a thinking-token
budget derived from the effort as a share of the output cap (high ≈ 80 %, medium ≈ 50 %,
low ≈ 20 %, minimal ≈ 10 %), or the direct reasoning.max_tokens (Anthropic-style, minimum 1024).
Where a provider has no reasoning knob the request is refused with a named capability_unsupported
error (400) before dispatch — never silently ignored. In auto/candidates routing a candidate that
cannot honour the effort is simply skipped, so an offer that can is chosen.
| Provider family | max_completion_tokens → | reasoning effort → |
|---|---|---|
OpenAI (reasoning models: gpt-5+, o-series) | max_completion_tokens | reasoning_effort (native) |
OpenAI (gpt-4.x / older) + every OpenAI-compatible provider (DeepSeek, Groq, xAI, Together, Fireworks, DeepInfra, Novita, Cerebras, Mistral, Qwen, Kimi) + custom base_url providers | max_tokens | reasoning_effort forwarded (the upstream honours or rejects it) |
| Anthropic | max_tokens | mapped → thinking: { type:"enabled", budget_tokens } (extended thinking requires temperature 1 / no top_p — a conflict is refused named) |
| Google Gemini | maxOutputTokens | mapped → thinkingConfig.thinkingBudget (none → 0) |
| Amazon Bedrock — Anthropic (Claude) models | maxTokens | mapped → Converse additionalModelRequestFields.thinking |
| Amazon Bedrock — non-Anthropic models | maxTokens | refused — capability_unsupported (400) |
| Cohere | max_tokens | refused — capability_unsupported (400) |
reasoning.exclude: true (drop reasoning tokens from the response) is not supported and is refused named.
What you gain
- Call types — attach
call_typeto tune routing + compression per workload (see call types). - Compression — off by default; opt-in
semantic_safe/adaptiveper call type. urblock — every response carries the request id, model + provider served, the routing trail, compression applied, and cost.- Savings accounting — an auditable ledger.
Provider preferences, fallbacks, and the stats endpoints keep the same semantics, so an OpenRouter integration typically migrates by swapping the constructor.