> ## Documentation Index
> Fetch the complete documentation index at: https://tracevell.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenRouter observability

> Trace every OpenRouter request with the model that served it, token usage, cost, and latency, all readable in Tracevell's transcript view.

## Overview

Tracevell is an open-source, OpenTelemetry-native observability platform for AI agents. Trace, debug, and monitor every OpenRouter call your app makes, with prompts, responses, token counts, cost, and latency on each span, in both TypeScript and Python.

OpenRouter gives you access to hundreds of models through a single API, so teams use it to switch models and providers without changing code. Tracevell auto-instruments the OpenRouter SDK: initialize Tracevell once and every chat, responses, and embeddings call is traced.

> [!NOTE]
>
> OpenRouter-specific headers are optional. Setting them allows your app to appear on the OpenRouter leaderboards.

## Getting Started

> [tab] TypeScript
>
> ### 1. Install Tracevell and the OpenRouter SDK
>
> ```bash theme={null}
> npm install @lmnr-ai/lmnr@latest @openrouter/sdk
> ```
>
> ### 2. Set up your environment variables
>
> Store your API keys in a `.env` file:
>
> ```bash theme={null}
> # .env file
> LMNR_PROJECT_API_KEY=your-tracevell-project-api-key
> OPENROUTER_API_KEY=your-openrouter-api-key
> ```
>
> Then load them in your application using a package like [dotenv](https://www.npmjs.com/package/dotenv).
>
> ### 3. Initialize Tracevell
>
> `@openrouter/sdk` is an ES module, so pass it to `instrumentModules` for Tracevell to patch it:
>
> ```typescript {5-7} theme={null}
> import { Tracevell } from '@lmnr-ai/lmnr';
> import { OpenRouter } from '@openrouter/sdk';
> import 'dotenv/config';
>
> Laminar.initialize({
>   instrumentModules: { openrouter: OpenRouter },
> });
>
> const client = new OpenRouter({
>   apiKey: process.env.OPENROUTER_API_KEY,
>   defaultHeaders: {
>     'HTTP-Referer': '<YOUR_SITE_URL>', // Optional
>     'X-Title': '<YOUR_SITE_NAME>', // Optional
>   },
> });
> ```
>
> ### 4. Use the OpenRouter SDK as usual
>
> ```typescript theme={null}
> const result = await client.chat.send({
>   chatRequest: {
>     model: 'openai/gpt-5-mini',
>     messages: [
>       { role: 'user', content: 'What is the meaning of life?' },
>     ],
>   },
> });
>
> // `send` returns a stream when `stream: true` is set, so narrow the result.
> if ('choices' in result) {
>   console.log(result.choices[0].message.content);
> }
> ```
>
> All OpenRouter SDK calls are now traced in Tracevell.
>
> > [!NOTE]
> >
> > Using OpenRouter inside Next.js? The [Vercel AI SDK page](https://tracevell.com/docs/integrations/vercel-ai-sdk.md#next-js-setup) covers the Next.js-specific setup (`serverExternalPackages` and `instrumentation.ts`).

> [tab] Python
>
> ### 1. Install Tracevell and the OpenRouter SDK
>
> ```bash theme={null}
> pip install -U lmnr openrouter python-dotenv
> ```
>
> ### 2. Set up your environment variables
>
> Store your API keys in a `.env` file:
>
> ```bash theme={null}
> # .env file
> LMNR_PROJECT_API_KEY=your-tracevell-project-api-key
> OPENROUTER_API_KEY=your-openrouter-api-key
> ```
>
> ### 3. Initialize Tracevell
>
> Tracevell instruments the OpenRouter SDK automatically when the `openrouter` package is installed:
>
> ```python {9} theme={null}
> import os
>
> from dotenv import load_dotenv
> from lmnr import Tracevell
> from openrouter import OpenRouter
>
> load_dotenv()
>
> Laminar.initialize()
>
> client = OpenRouter(api_key=os.environ["OPENROUTER_API_KEY"])
> ```
>
> ### 4. Use the OpenRouter SDK as usual
>
> ```python theme={null}
> response = client.chat.send(
>     model="openai/gpt-5-mini",
>     messages=[
>         {"role": "user", "content": "What is the meaning of life?"}
>     ],
> )
>
> print(response.choices[0].message.content)
> ```
>
> All OpenRouter SDK calls are now traced in Tracevell.
>
> > [!NOTE]
> >
> > To see an example of how to integrate Tracevell within a FastAPI application, check out our [FastAPI integration guide](https://tracevell.com/docs/guides/fastapi.md).

## What Tracevell captures

Each call becomes an LLM span named after the resource you called:

* `openrouter.chat` for `client.chat.send`.
* `openrouter.responses` for `client.responses.send`.
* `openrouter.embeddings` for `client.embeddings.generate`.

Every span carries the model, the request messages, the response, token counts, and cost, whether the call returns a single response or a stream. Streamed responses are recorded once the stream is consumed.

## Using the OpenAI SDK with OpenRouter

If you reach OpenRouter through the OpenAI SDK, Tracevell traces those calls too. Point the OpenAI client's base URL at OpenRouter and initialize Tracevell as usual.

> [tab] TypeScript
>
> ```typescript {5-7} theme={null}
> import { Tracevell } from '@lmnr-ai/lmnr';
> import OpenAI from 'openai';
> import 'dotenv/config';
>
> Laminar.initialize({
>   instrumentModules: { OpenAI: OpenAI },
> });
>
> const openai = new OpenAI.OpenAI({
>   baseURL: 'https://openrouter.ai/api/v1',
>   apiKey: process.env.OPENROUTER_API_KEY,
> });
>
> const response = await openai.chat.completions.create({
>   model: 'openai/gpt-5-mini',
>   messages: [
>     { role: 'user', content: 'What is the meaning of life?' }
>   ],
> });
>
> console.log(response.choices[0].message.content);
> ```

> [tab] Python
>
> ```python {9} theme={null}
> import os
>
> from dotenv import load_dotenv
> from lmnr import Tracevell
> from openai import OpenAI
>
> load_dotenv()
>
> Laminar.initialize()
>
> client = OpenAI(
>     base_url="https://openrouter.ai/api/v1",
>     api_key=os.environ["OPENROUTER_API_KEY"],
> )
>
> response = client.chat.completions.create(
>     model="openai/gpt-5-mini",
>     messages=[
>         {"role": "user", "content": "What is the meaning of life?"}
>     ],
> )
>
> print(response.choices[0].message.content)
> ```

## Using the OpenRouter API directly

> [!TIP]
>
> You can use the interactive [Request Builder](https://openrouter.ai/request-builder) to generate OpenRouter API requests in the language of your choice.

> [!NOTE]
>
> Direct HTTP calls are captured as custom spans via `observe()`. LLM-specific fields (tokens, cost) are not extracted automatically from raw responses.

```python title="Python" theme={null}
import json
import requests

from lmnr import Tracevell, observe

Laminar.initialize()

@observe()
def call_openrouter():
    response = requests.post(
        url="https://openrouter.ai/api/v1/chat/completions",
        headers={
            "Authorization": "Bearer <OPENROUTER_API_KEY>",
            "HTTP-Referer": "<YOUR_SITE_URL>", # Optional
            "X-Title": "<YOUR_SITE_NAME>", # Optional
        },
        data=json.dumps({
            "model": "openai/gpt-5-mini",
            "messages": [
                {"role": "user", "content": "What is the meaning of life?"}
            ]
        })
    )
    return response.json()

print(call_openrouter())
```

```typescript title="TypeScript (fetch)" theme={null}
import { Tracevell, observe } from '@lmnr-ai/lmnr';

Laminar.initialize();

const response = await observe({ name: 'openrouter.fetch' }, async () =>
  fetch('https://openrouter.ai/api/v1/chat/completions', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer <OPENROUTER_API_KEY>',
      'HTTP-Referer': '<YOUR_SITE_URL>', // Optional
      'X-Title': '<YOUR_SITE_NAME>', // Optional
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'openai/gpt-5-mini',
      messages: [
        {
          role: 'user',
          content: 'What is the meaning of life?',
        },
      ],
    }),
  })
);

console.log(await response.json());
```

```shell title="Shell" theme={null}
curl https://openrouter.ai/api/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  -d '{
  "model": "openai/gpt-5-mini",
  "messages": [
    {
      "role": "user",
      "content": "What is the meaning of life?"
    }
  ]
}'
```

## See what happened in a trace

Open a trace in Tracevell and you land on the transcript view: Tracevell extracts the agent input, the model's turns, and any tool calls into a conversation, so you read what happened instead of clicking through a tree of span names. Token counts, cost, and latency sit on each LLM span, and you can open any span in the [playground](https://tracevell.com/docs/playground.md) to iterate on the prompt.

More on the trace UX: [Viewing traces](https://tracevell.com/docs/platform/viewing-traces.md).

## Track outcomes with Signals

Traces answer *what happened on this run*. **[Signals](https://tracevell.com/docs/signals/introduction.md) answer the cross-trace question**: *how often does a model refuse, which runs fell back to a second provider, when do responses come back empty*. A Signal pairs a plain-language instruction with a JSON output schema. Tracevell runs it live on new traces (Triggers) or backfills it across history (Jobs) and records a structured event every time it matches. From there you [query](https://tracevell.com/docs/platform/sql-editor.md), [cluster](https://tracevell.com/docs/signals/clusters.md), and [alert](https://tracevell.com/docs/signals/alerts.md) on events across every trace.

> [!NOTE]
>
> Every new project ships with a **Failure Detector** Signal that categorizes issues on any trace over 1000 tokens. Open it from the Signals sidebar to see events as soon as your OpenRouter traces arrive.

## Query across traces

* **[SQL editor](https://tracevell.com/docs/platform/sql-editor.md)** for ad-hoc queries across traces, spans, signals, and evals.
* **SQL API** for programmatic access from scripts and pipelines.
* **[CLI](https://tracevell.com/docs/platform/cli.md)** (`lmnr-cli sql query`) for terminal-driven queries and piping JSON into shell tools or coding agents.
* **[MCP server](https://tracevell.com/docs/platform/mcp.md)** to query Tracevell directly from Claude Code, Cursor, Codex, or any MCP-aware client.

## Enrich your OpenRouter traces

* Attach sessions, user IDs, metadata, and tags to OpenRouter spans via the [SDK reference](https://tracevell.com/docs/sdk/reference).
* Wrap the functions around your OpenRouter calls with `observe` and mark them as [TOOL spans](https://tracevell.com/docs/tracing/structure/span-types.md) so they show up in the transcript.
* Images you send to vision-capable models are captured automatically ([Tracing Images](https://tracevell.com/docs/tracing/structure/images.md)).

## What's next

> [card] [Viewing traces](https://tracevell.com/docs/platform/viewing-traces.md)
>
> Read the transcript view, filter, and search across traces.

> [card] [Signals](https://tracevell.com/docs/signals/introduction.md)
>
> Detect behaviors and failures across every run, then query, cluster, and alert on them.

> [card] [SQL editor and MCP server](https://tracevell.com/docs/platform/sql-editor.md)
>
> Query traces programmatically from the UI, API, or your IDE.

> [card] [Tracing structure](https://tracevell.com/docs/tracing/structure/overview.md)
>
> Add sessions, metadata, and tags, and group calls under your own spans.

## Related integrations

> [card] [OpenAI](https://tracevell.com/docs/integrations/openai.md)
>
> Trace the OpenAI SDK directly in TypeScript and Python.

> [card] [Anthropic](https://tracevell.com/docs/integrations/anthropic.md)
>
> Trace the Anthropic SDK directly in TypeScript and Python.

> [card] [LiteLLM](https://tracevell.com/docs/integrations/litellm.md)
>
> One integration for every model LiteLLM routes to.

> [card] [TypeSafe AI](https://tracevell.com/docs/integrations/typesafe.md)
>
> Trace typed Jev decisions, direct or through OpenRouter.

> [card] [Vercel AI SDK](https://tracevell.com/docs/integrations/vercel-ai-sdk.md)
>
> Trace `generateText` and `streamText` in Next.js and Node.js.

> [card] [All integrations](https://tracevell.com/docs/integrations.md)
>
> Browse every provider, framework, coding agent, and browser integration Tracevell supports.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.