Changelog
Page text on /v1/web-search (September 2026)
Added
include_textandmax_charactersonPOST /v1/web-search.include_text: truereturns page text on each result;max_characters(default 4000, 100–100000) caps it. Neither costs extra. See Getting page content.textandscoreon each result. Tavily now returns its relevance snippet and score by default, andacademicresults carry the paper’s abstract intext. Exa returns text only withinclude_text.
Notes
- All fields are additive. Existing calls keep the fields they had; the new ones are
nullwhere a provider has nothing to put in them.
Academic search on /v1/web-search (August 2026)
Added
typeonPOST /v1/web-search—"general"(the default) or"academic".academicsearches Consensus, which indexes peer-reviewed papers, without you naming a provider. Omitting the field keeps the previous behaviour exactly.- Consensus as a provider (
consensus). Web search only; it has no content-fetch capability, so pinning it onPOST /v1/fetchreturns a400.
Notes
- Nothing changes for existing calls.
typedefaults togeneral, and Consensus is never chosen by default, so a plain{"query": ...}search will not start returning papers. - An explicit
X-Quantized-Provideralways wins, in both directions:type: "academic"withX-Quantized-Provider: exasearches the general web, andX-Quantized-Provider: consensusreaches Consensus with notypeat all. typenever widens what your license can reach. If your license type is pinned to providers that exclude Consensus,type: "academic"returns a400rather than falling back to a general provider.- The response shape is unchanged, so the same client code reads both kinds of result. On
academic,urlis the paper’sconsensus.apppage,idis its DOI, andusage.search_timeis alwaysnull. Journal, citation count and abstract are not exposed today. - Consensus serves about one search at a time. Parallel academic searches return
503; sequential ones succeed. Each takes several seconds. Failed searches are not billed. - A
typeother thangeneraloracademicis a422, raised before a provider is chosen.
Providers validate models (August 2026)
Changed
- An unknown model id is now the provider’s call, not ours. The router no longer keeps a catalog gate in front of the providers. A model id it cannot resolve is forwarded as sent, and the status and message come back from OpenAI, Google or AWS. The same mistake therefore gets different answers per provider: AWS Bedrock returns
400 The provided model identifier is invalid., Google returns404, OpenRouter and OpenAI return404with their own wordings. On an endpoint with several eligible providers it is not stable across repeated calls either. Do not branch on message text, and do not assume an unknown model is always a404. See Who validates the model.POST /v1/aws-bedrock/embeddingspreviously answered an unseeded Bedrock model id with404. It is now a400carrying Bedrock’s message.POST /v1/images/generationspreviously answered an unknown id with404, and a retired model with400 No active providers available. Neither is produced any more.
- The modality error names providers, not the model.
"Model 'X' does not support audio input"is now"No available provider for model 'X' supports audio input". A model’s hosts can differ on what they accept, so the router skips a provider that cannot serve the request and only fails when every reachable one refuses.
Added
- Per-provider catalog attributes. Cost, reasoning dialect, context limits, capabilities and modality can now be stated per provider rather than once per model, so a model hosted in two places can carry each host’s real numbers.
GET /v1/modelsgainedcontext_limits,knowledge_cutoff,is_open_weightsandlogo_url. cost.prompt_batch— the discounted input rate for a provider’s batch endpoint, used by Gemini embeddings at half the single-content rate.
Fixed
max_tokensworks on OpenAI reasoning models again. The router sendsmax_completion_tokenson the OpenAI Direct route, so clients no longer have to special-case theo-seriesandgpt-5families.
Notes
costis not a fixed schema. Read the keys present rather than assuming any of them, and bill againstusage.credits_usedon the response. An emptycostmeans the catalog carries no rate, not that the call is free.GET /v1/modelsdoes not say which provider serves a model, so it cannot be used to check whether anX-Quantized-Providerpin will work.- Image generation is served by OpenAI and Google Gemini. AWS Bedrock image models are not currently routable, and DALL-E 2 and DALL-E 3 were retired by OpenAI in 2026.
Default embeddings provider is now OpenRouter (June 2026)
Changed
POST /v1/embeddingsdefaults to OpenRouter instead of OpenAI Direct. Default provider routing is now driven by data (theis_defaultflag per provider), not by endpoint-specific logic. OpenAI embeddings remains available asX-Quantized-Provider: openai(cheaper, native). Chat completions, responses, web search, and image generation defaults are unchanged.
Native OpenAI Responses API (June 2026)
Added
- OpenAI Direct on
POST /v1/responses— setX-Quantized-Provider: openaito drive OpenAI’s first-party/v1/responsesdirectly. Same seven first-party models, alias rewriting, and local-rate billing as the chat route.reasoningis passed as an object ({"effort": "..."}) unchanged — OpenAI’s Responses API takes the object directly, so there’s noreasoning_efforttranslation here. Usemax_output_tokensfor reasoning models.frequency_penalty/presence_penaltyare dropped on the OpenAI route (OpenAI’s Responses endpoint rejects them); they remain available via OpenRouter.- Streaming forwards OpenAI’s typed
event: <type>/data: {...}events and ends onresponse.completed(no[DONE]sentinel).
Native OpenAI chat completions (June 2026)
Added
- OpenAI Direct on
POST /v1/chat/completions— setX-Quantized-Provider: openaito call OpenAI’s first-party API directly instead of through OpenRouter.- Models in scope:
openai/gpt-4o,openai/gpt-4o-mini,openai/gpt-4.1-mini,openai/gpt-4.1-nano,openai/o4-mini,openai/gpt-5,openai/gpt-5.1. Open-weightgpt-ossmodels stay OpenRouter-only. - The catalog id is rewritten to OpenAI’s bare model name upstream and the public id is restored on the response.
- Billing: OpenAI returns no per-call cost, so
credits_usedis computed from the model’s catalog per-token rates (prompt/completion, plus cached input when reported). reasoning/reasoning_effortis translated to OpenAI’s top-levelreasoning_effort; the OpenRouter-onlyrepetition_penaltyis dropped on this route.
- Models in scope:
Notes
- OpenRouter remains the default for chat completions — the OpenAI route is opt-in via the header.
- OpenAI’s o-series models (
o4-mini,gpt-5family for reasoning) requiremax_completion_tokens;max_tokensis rejected upstream.
Embeddings endpoint (May 2026)
Added
POST /v1/embeddings— OpenAI-compatible embeddings endpoint.- Default provider: OpenAI Direct (new provider, slug
openai). - Alternative: OpenRouter via
X-Quantized-Provider: openrouter. OpenRouter passthrough uses the OpenAI rate table for billing because its response does not include cost data. - Supported models:
text-embedding-3-small,text-embedding-3-large,text-embedding-ada-002. - Accepted fields:
model,input(string or list of strings),dimensions,encoding_format(only"float"),user. - Strict validation (
extra="forbid") — unknown fields return422.
- Default provider: OpenAI Direct (new provider, slug
Out of scope (deferred)
- Native Bedrock embeddings (
/v1/aws-bedrock/embeddings) — Titan + Cohere — landing in a follow-up PR. - Token-array input (
list[int]/list[list[int]]). - Multimodal
ContentPart[]input. encoding_format: "base64".output_dtypequantized embeddings.input_type(relevant once Cohere/Gemini providers are wired in).
Multimodal chat completions (April 2026)
Added
- Chat Completions — new content part types:
input_audio— base64 audio withformat(wav/mp3/aiff/aac/ogg/flac/m4a/pcm16/pcm24)video_url— HTTPS URL ordata:video/...;base64,...data URIfile— PDF viafile_data(HTTPS ordata:application/pdf;base64,...) with optionalfilename; alternativelyfile_id
- Model modality validation: chat-completion requests are now validated against the target model’s
input_modalitybefore dispatch. A mismatch (e.g. audio to a text-only model) returns400with a clear message instead of an opaque upstream error.fileparts are exempt — they are handled by OpenRouter’s universal PDF parser across all models.
Provider support
OpenRouter: all four new modalities. Anthropic: text + image only (existing behavior unchanged).
v1.1 — API Lockdown (April 2026)
Strict parameter validation
The API now enforces strict validation on all request parameters. Unknown or unsupported fields are rejected with 422 Unprocessable Entity.
Chat Completions — removed parameters:
top_k, modalities, audio, web_search_options, metadata
Chat Completions — promoted parameters (now supported):
logprobs, top_logprobs, logit_bias, repetition_penalty, user, stream_options
Chat Completions — promoted message fields (now supported):
refusal, reasoning, reasoning_details (assistant role only)
Chat Completions — accepted message fields (not forwarded to providers):
annotations, audio, function_call (accepted from OpenAI SDK responses to avoid 422 in multi-turn conversations)
Chat Completions — removed message fields:
images (in request messages)
Responses API — removed parameters:
store, text, truncation, include, service_tier, background, top_k, metadata, user
Chat Completions — removed response fields:
system_fingerprint, choices[].message.annotations, choices[].message.audio, choices[].message.images
Chat Completions — promoted response fields (now supported):
choices[].logprobs, choices[].message.reasoning, choices[].message.reasoning_details
Strict content part validation
Message content arrays now only accept text and image_url content parts. Other types like input_audio, video_url, or file are rejected.
Strict tool definition validation
Chat completions tools field now validates tool structure: each tool must follow the OpenAI format (type: "function", function: {name, description, parameters}) or Anthropic format (name, input_schema).
Parameter range validation
All generation parameters are validated at the API boundary with clear error messages:
max_tokens/max_completion_tokens/max_output_tokens: minimum 1temperature: 0–2top_p: 0–1frequency_penalty/presence_penalty: −2 to 2
Out-of-range values return 422 with a message like: "Input should be greater than or equal to 16".
Type improvements
response_formatvalidates structure:{"type": "json_object"}or{"type": "json_schema", ...}reasoningvalidates effort:{"effort": "none" | "low" | "medium" | "high"}with optionalexcludebooleantool_choicevalidates values:"auto","required","none", or{"type": "function", "function": {"name": "..."}}urlsin/v1/fetchnow requireslist[string]
Error message sanitization
Provider error messages are now sanitized before reaching the user. URLs, email addresses, and provider names are stripped from all client-facing error messages. Internal error details are preserved in the database for debugging.
v1 — Initial Release
Endpoints
POST /v1/chat/completions— OpenAI-compatible chat completionsPOST /v1/responses— Stateful Responses APIPOST /v1/web-search— Web search with structured resultsPOST /v1/fetch— Extract text content from URLsGET /v1/models— List available models and pricingGET /v1/license— Check license info and credit balance
Providers
- OpenRouter — LLMs (default for chat completions, responses, models)
- Anthropic — Claude models directly
- Exa — Web search and content fetch (default)
- Tavily — Web search and content fetch (alternative)
Features
- OpenAI SDK compatibility
- SSE streaming for chat completions and responses
- Unified credit billing across all providers
- JWT authentication with auto-provisioning
- Per-institution configuration
- Provider routing via
X-Quantized-Providerheader