Changelog

Page text on /v1/web-search (September 2026)

Added

  • include_text and max_characters on POST /v1/web-search. include_text: true returns page text on each result; max_characters (default 4000, 100–100000) caps it. Neither costs extra. See Getting page content.
  • text and score on each result. Tavily now returns its relevance snippet and score by default, and academic results carry the paper’s abstract in text. Exa returns text only with include_text.

Notes

  • All fields are additive. Existing calls keep the fields they had; the new ones are null where a provider has nothing to put in them.

Academic search on /v1/web-search (August 2026)

Added

  • type on POST /v1/web-search — "general" (the default) or "academic". academic searches 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 on POST /v1/fetch returns a 400.

Notes

  • Nothing changes for existing calls. type defaults to general, and Consensus is never chosen by default, so a plain {"query": ...} search will not start returning papers.
  • An explicit X-Quantized-Provider always wins, in both directions: type: "academic" with X-Quantized-Provider: exa searches the general web, and X-Quantized-Provider: consensus reaches Consensus with no type at all.
  • type never widens what your license can reach. If your license type is pinned to providers that exclude Consensus, type: "academic" returns a 400 rather than falling back to a general provider.
  • The response shape is unchanged, so the same client code reads both kinds of result. On academic, url is the paper’s consensus.app page, id is its DOI, and usage.search_time is always null. 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 type other than general or academic is a 422, 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 returns 404, OpenRouter and OpenAI return 404 with 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 a 404. See Who validates the model.
    • POST /v1/aws-bedrock/embeddings previously answered an unseeded Bedrock model id with 404. It is now a 400 carrying Bedrock’s message.
    • POST /v1/images/generations previously answered an unknown id with 404, and a retired model with 400 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/models gained context_limits, knowledge_cutoff, is_open_weights and logo_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_tokens works on OpenAI reasoning models again. The router sends max_completion_tokens on the OpenAI Direct route, so clients no longer have to special-case the o-series and gpt-5 families.

Notes

  • cost is not a fixed schema. Read the keys present rather than assuming any of them, and bill against usage.credits_used on the response. An empty cost means the catalog carries no rate, not that the call is free.
  • GET /v1/models does not say which provider serves a model, so it cannot be used to check whether an X-Quantized-Provider pin 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/embeddings defaults to OpenRouter instead of OpenAI Direct. Default provider routing is now driven by data (the is_default flag per provider), not by endpoint-specific logic. OpenAI embeddings remains available as X-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 — set X-Quantized-Provider: openai to drive OpenAI’s first-party /v1/responses directly. Same seven first-party models, alias rewriting, and local-rate billing as the chat route.
    • reasoning is passed as an object ({"effort": "..."}) unchanged — OpenAI’s Responses API takes the object directly, so there’s no reasoning_effort translation here. Use max_output_tokens for reasoning models.
    • frequency_penalty / presence_penalty are 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 on response.completed (no [DONE] sentinel).

Native OpenAI chat completions (June 2026)

Added

  • OpenAI Direct on POST /v1/chat/completions — set X-Quantized-Provider: openai to 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-weight gpt-oss models 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_used is computed from the model’s catalog per-token rates (prompt/completion, plus cached input when reported).
    • reasoning / reasoning_effort is translated to OpenAI’s top-level reasoning_effort; the OpenRouter-only repetition_penalty is dropped on this route.

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-5 family for reasoning) require max_completion_tokens; max_tokens is 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 return 422.

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_dtype quantized 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 with format (wav/mp3/aiff/aac/ogg/flac/m4a/pcm16/pcm24)
    • video_url — HTTPS URL or data:video/...;base64,... data URI
    • file — PDF via file_data (HTTPS or data:application/pdf;base64,...) with optional filename; alternatively file_id
  • Model modality validation: chat-completion requests are now validated against the target model’s input_modality before dispatch. A mismatch (e.g. audio to a text-only model) returns 400 with a clear message instead of an opaque upstream error. file parts 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 1
  • temperature: 0–2
  • top_p: 0–1
  • frequency_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_format validates structure: {"type": "json_object"} or {"type": "json_schema", ...}
  • reasoning validates effort: {"effort": "none" | "low" | "medium" | "high"} with optional exclude boolean
  • tool_choice validates values: "auto", "required", "none", or {"type": "function", "function": {"name": "..."}}
  • urls in /v1/fetch now requires list[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 completions
  • POST /v1/responses — Stateful Responses API
  • POST /v1/web-search — Web search with structured results
  • POST /v1/fetch — Extract text content from URLs
  • GET /v1/models — List available models and pricing
  • GET /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-Provider header