Monitor OpenRouter with Sazabi
Last verified on Nov 8, 2026.
If your application calls many models through OpenRouter, the gateway sees every request. That makes it the one place that knows which model and upstream provider answered, what each generation cost and which end user it was for. Your application logs usually know only half of that.
OpenRouter's Broadcast feature sends that record out as OpenTelemetry. Sazabi, the AI-native observability and security platform, receives it as a manual log source. You change a dashboard setting, not your code.
What Sazabi reads from OpenRouter
The flow runs one way. After you enable Broadcast and set an OpenTelemetry Collector destination, OpenRouter pushes one trace span per generation to your Sazabi intake URL.
Per the Sazabi docs for this source, each span names the model and the upstream provider. It carries token counts, the cost of the request and the prompt and completion content, unless OpenRouter's Privacy Mode is on. The destination form saves only after Test Connection passes.
OpenRouter's collector page explains how the span is built. It sends OTLP over HTTP with JSON encoding, and model, token and cost values use the gen_ai.* conventions. Prompt and completion text is on the generation span as gen_ai.prompt and gen_ai.completion. Cache writes appear as numeric attributes such as gen_ai.usage.input_tokens.cache_write.
Request fields you add become span attributes. The user field maps to user.id, and the session_id field maps to session.id. Anything in the request's trace object lands under trace.metadata.*, so an environment key becomes trace.metadata.environment. A trace_id in that object groups several requests into one trace.
Questions it answers
- What did each model cost per day, across all upstream providers?
- Which end user, by
user.id, generates the most spend? - Which session made the most generations?
- How much of our input is cache-write tokens?
- Which requests from
stagingended up in the production destination? - How do two providers compare on tokens for the same model slug?
A worked example: cost per feature
Cost questions need a label. OpenRouter lets you add one in the request body, using fields from its docs. This request, with invented values, tags a generation:
{
"model": "openai/gpt-4o",
"messages": [{ "role": "user", "content": "Summarize this ticket" }],
"user": "user_12345",
"session_id": "session_abc",
"trace": {
"trace_id": "workflow_12345",
"generation_name": "Summarize ticket",
"feature": "customer-support",
"environment": "production"
}
}In Sazabi the span carries user.id of user_12345, session.id of session_abc and trace.metadata.feature of customer-support. Ask the Sazabi agent in a thread:
Sum the cost of OpenRouter generation spans by trace.metadata.feature for the
last 7 days, and show the top three features with their most-used model.If customer-support is far ahead, group by user.id inside it. A handful of users driving the spend means a loop or an abusive client. An even spread means the feature is simply expensive, and the lever is the model choice, a shorter prompt or caching. Spans with trace.metadata.environment set to staging in a production project suggest a test key was not excluded from the destination.
Privacy and volume controls
These are set in OpenRouter. Privacy Mode removes prompt and completion text from spans while keeping the rest. OpenRouter's docs say a destination can be limited to chosen API keys, with exclusions taking precedence, and can use a sampling rate between 0 and 1. Sampling is deterministic by session_id, so a session is kept or dropped whole. The user field takes up to 128 characters and session_id up to 256.
How it fits with other integrations
OpenRouter reports at the gateway, so it pairs with spans from your own code. Mastra and LangChain cover the framework side, and both are listed under AI frameworks. The OpenTelemetry integration describes the transport. Span volume counts toward your plan, so read pricing before sending all keys at a sampling rate of 1.0.
Log sources
- OpenRouter (manual log source, ai frameworks): Enable Broadcast in the OpenRouter dashboard and set your Sazabi intake URL as the OpenTelemetry Collector destination to receive an LLM trace span per generation.
Frequently Asked Questions
Do I need to change my application code?
No. OpenRouter pushes a trace span for each generation from its side, so there is no SDK to add. Optional fields on your requests, such as user, session_id and trace, add context to the spans.
Will Sazabi see my prompts and completions?
By default each span carries prompt and completion content. OpenRouter's Privacy Mode suppresses that content from spans. Turn it on if you want model, provider, token counts and cost without the text.
Why does Test Connection fail?
Check that you pasted the full intake URL including the https:// scheme. OpenRouter saves the destination only after the connection test passes.
Can I send only some traffic?
Yes. A Broadcast destination can be limited to selected API keys, and each destination has a sampling rate. When you pass a session_id, sampling keeps or drops all traces in that session together.
Sources
- OpenRouter connects to Sazabi as a manual log source (accessed Nov 8, 2026)
- One trace span per generation naming the model and upstream provider, with token counts, cost per request and prompt and completion content unless Privacy Mode is on; Test Connection must pass before OpenRouter saves the destination (accessed Nov 8, 2026)
- OTLP/HTTP with JSON encoding on the /v1/traces path, trace metadata keys, user and session_id mapped to user.id and session.id, trace.metadata.* attributes, gen_ai.* conventions and cache-write token attributes (accessed Nov 8, 2026)
- Per-destination API key filters, sampling rate, deterministic sampling by session_id, the user field limit of 128 characters and the session_id limit of 256 characters (accessed Nov 8, 2026)