Skip to main content
Available for: Python and TypeScript.
OpenRouter provides a unified API for 200+ models. Wrap your OpenRouter client with wrapOpenRouter() (TypeScript) to trace every call, regardless of which underlying model is used — or patch the openai module with patch_openrouter() (Python), since OpenRouter uses an OpenAI-compatible API.

Installation

OpenRouter uses an OpenAI-compatible API — install the openai package as the client in both languages.

Setup

In Python, patch_openrouter() is an alias for patch_openai() — it monkey-patches the openai module’s chat completions method globally rather than wrapping a single client instance. This means: (1) any openai.OpenAI() client in your process is traced after calling it, whether it points at OpenRouter’s base_url or not; (2) traced events are tagged provider: "openai", not provider: "openrouter"; and (3) cost is calculated from token counts and generic pricing, not from OpenRouter’s own reported cost. The TypeScript wrapOpenRouter() wraps only the client instance you pass it, tags events provider: "openrouter", and prefers OpenRouter’s actual reported cost (see below). There is currently no separate Python codepath that gives OpenRouter-specific cost or provider tagging — patch_openrouter() and patch_openai() are the same function.

Example

What gets captured

Cost reporting accuracy in TypeScript depends on whether OpenRouter returns cost data in the response. For most popular models it does. For custom or fine-tuned routes, token-based calculation is used as a fallback.

Send traces without the SDK (Broadcast)

OpenRouter can also send traces on its own, without any Zespan SDK in your app, with its Broadcast feature. Use this instead of the wrapper for a given call, not as well (see below).
1

Add an OpenTelemetry Collector destination

In OpenRouter’s Broadcast settings add a destination of type OpenTelemetry Collector and set the endpoint to https://api.zespan.com/v1/traces. OpenRouter sends JSON.
2

Add the project key as a header

In the destination’s custom headers (a JSON object) add {"x-api-key": "zsp_..."}.
3

Test the connection

Press Test connection; OpenRouter saves the destination only if the test passes. OpenRouter documents the test (for its Webhook destination) as an empty export marked X-Test-Connection: true that passes on a 2xx or 400 answer; Zespan answers 200. Then send one real request and open Settings → Instrumentation → Gateway and OTLP data. A test alone shows as only a test connection has arrived. See Instrumentation.
What Zespan stores from a Broadcast span:
Attribute names come from OpenRouter’s published documentation and can change between versions. Use the Gateway and OTLP data card to see what actually arrives. Zespan recognises OpenRouter by the service name openrouter; if the card lists the data as OpenTelemetry, it is stored and read the same way but not marked as OpenRouter’s.
Do not use both the wrapper and Broadcast for the same calls. OpenRouter takes trace ids in its request body, not in headers, so the SDK cannot make Broadcast’s span a child of its own, and OpenRouter does not document the provider’s response id on the Broadcast span, so Zespan cannot tell the two rows are one call: it would count the call twice.

Next steps