Router Metadata
Surface routing decisions on every response with a single opt-in header
Experimental
Router metadata is experimental. The openrouter_metadata response shape is unstable: fields and pipeline stage types may be added, renamed, removed, or change semantics at any time, without a deprecation cycle. Do not pin production tooling to specific field names or values yet.
OpenRouter’s router runs every request through a multi-stage pipeline: it picks a provider, may compress context, may run guardrails, may invoke server-side tools, and may retry against fallbacks. By default, none of that is visible on the response.
Router metadata is a per-request opt-in that adds an openrouter_metadata field to successful responses, capturing exactly what the router did. It’s intended for debugging routing decisions, attributing latency or cost, and auditing pipeline behavior.
Enabling Router Metadata
Opt in by sending the X-OpenRouter-Experimental-Metadata request header with the value enabled:
Accepted Values
The header accepts the following values, matched case-insensitively:
Any other value (including misspellings, empty strings, and unknown levels) falls back to disabled. The default behavior — when the header is absent — is disabled.
Supported Endpoints
Router metadata is wired into every public completion route:
/api/v1/chat/completions(OpenAI Chat Completions)/api/v1/messages(Anthropic Messages)/api/v1/responses(OpenAI Responses)/api/v1/completions(legacy text completions)
Both streaming and non-streaming requests carry the field when opted in. For streaming responses, openrouter_metadata is delivered on the final chunk before data: [DONE] (Chat Completions / Responses) or as part of the terminal message_stop event (Anthropic Messages).
Response Shape
When opted in, successful responses include an openrouter_metadata object alongside the rest of the response payload:
Field Reference
The full schema is documented under OpenRouterMetadata in the OpenAPI spec, including SDK type definitions for TypeScript and other generated clients.
Pipeline Stages
The pipeline array records every plugin that materially affected the request. A plugin only emits a stage when it actually ran; a no-op plugin (e.g. context compression that found the input already fit the budget) is omitted. Today’s stage types include:
Multiple plugins can share a type. To find a specific guardrail (say, the content filter), iterate the array and match on both type === 'guardrail' and name === 'content-filter'. The full set of guardrail-level plugins emits type: 'guardrail' so you can filter all of them together (pipeline.filter(s => s.type === 'guardrail')) without enumerating individual plugins.
The list grows over time. Treat unknown stage types as opaque — data is a free-form record by design so plugins can attach plugin-specific telemetry without a schema bump.
Cache Hits
Cache hits never include openrouter_metadata. Both streaming and non-streaming cache replays strip the field so clients cannot pin behavior on stale routing data. This is intentional: the metadata you see on a cache miss may not reflect the routing that produced the cached payload.
Error Responses
Opt-in error responses surface openrouter_metadata at the top level of the error envelope, mirroring the success-path placement (sibling of error rather than nested inside it). This applies to all four routes — Chat Completions, Messages, Responses, and legacy Completions — and to both streaming and non-streaming requests. The same opt-in rules apply: send X-OpenRouter-Experimental-Metadata: enabled and the snapshot is included on failure; omit it and it isn’t.
A few things to know:
attemptreflects how far the router got. A value of0means the request never reached a provider — typically because every candidate was filtered out before submission (e.g.provider.onlyexcluded the last endpoint, or an allowed-providers / max-price filter rejected everything). Values≥ 1mean every attempted provider failed and fallbacks were exhausted.- No endpoint is marked
selectedon failure. None of theendpoints.available[].selectedflags aretruebecause no endpoint actually served a 200. - Internal-error masking still applies. Responses with a
500status are scrubbed to a generic message, andopenrouter_metadatais omitted from those envelopes by design — we don’t surface internal routing details on errors whose cause is already hidden. Other 5xx classes (502,503,504,529) still include the metadata when the client opted in. - Some failure modes won’t carry it. Authentication / rate-limit failures and other errors that fire before the router has usable routing state (for example, validation rejections at the API edge) will not include the field. If you need post-mortem routing context for a request that completed past the API edge but before the router materialised state, fetch the generation record via
GET /api/v1/generationusing theX-Generation-Idresponse header.
Stability
Router metadata is experimental. The openrouter_metadata response shape is unstable — fields and pipeline stage types may be added, renamed, removed, or change semantics at any time, without a deprecation cycle. Treat the payload as best-effort debugging telemetry, not as a stable contract.
The X-OpenRouter-Experimental-Metadata opt-in header is the supported way to enable the feature, but the header name and accepted values may also change while the feature is experimental.
If you consume the field in code, decode it permissively (treat unknown fields and stage types as opaque) and be prepared to update on every release.