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

# OpenRouter

> Wrap the OpenRouter or OpenAI client to meter and gate calls, with the credits OpenRouter actually charged and the model that served.

## Set up

The SDK recognizes both ways of calling OpenRouter and selects the OpenRouter recipe automatically:

* **OpenRouter's own client**, `OpenRouter` from `@openrouter/sdk`, which OpenRouter recommends.
* **OpenAI's `openai` client** with its `baseURL` pointing at `openrouter.ai`, since OpenRouter's API follows OpenAI's format.

```typescript OpenRouter client theme={null}
import { OpenRouter } from '@openrouter/sdk';

const router = stigg.wrap(new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY }), {
  stiggContext: { customerId: 'customer-acme', entitlement: { currencyId: 'credits' } },
});

await router.chat.send({
  chatRequest: {
    model: 'anthropic/claude-opus-5',
    models: ['anthropic/claude-sonnet-5', 'openai/gpt-5'], // fallbacks
    messages,
  },
});
```

```typescript OpenAI client theme={null}
import OpenAI from 'openai';

const router = stigg.wrap(
  new OpenAI({ baseURL: 'https://openrouter.ai/api/v1', apiKey: process.env.OPENROUTER_API_KEY }),
);

await router.chat.completions.create({
  model: 'anthropic/claude-opus-5',
  messages,
  stiggContext: { customerId: 'customer-acme', entitlement: { currencyId: 'credits' } },
});
```

* **With the `OpenRouter` client, set the `stiggContext` in `wrap()`.** Its methods take a request object whose type does not include `stiggContext`. To serve several customers, wrap a client for each, or use the `OpenAI` client, which accepts a `stiggContext` on each call.
* **Ask for usage on streamed calls.** OpenRouter includes usage in a stream only when the request asks for it: `streamOptions: { includeUsage: true }` with the `OpenRouter` client, or `stream_options: { include_usage: true }` with the `OpenAI` client. Without it, the stream reports no usage and the call is not metered.
* **Iterate streams with `for await`.** A stream from the `OpenRouter` client that you read with `getReader()` instead is not metered.

### Counting `cost`

OpenRouter charges in credits, and a single call usually costs a small fraction of one credit. The recipe reports `cost` as a whole number, so by default it counts in millionths of a credit: a call that cost 0.0003 credits is reported as 300. If the feature or credit type you meter in Stigg uses a different unit, pass the recipe yourself with `unitsPerCredit`. For example, to report in hundredths of a credit:

```typescript theme={null}
import { openrouterRecipe } from '@stigg/agent-sdk-js';

const router = stigg.wrap(new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY }), {
  recipe: openrouterRecipe({ unitsPerCredit: 100 }),
});
```

A larger unit rounds each call up to a whole unit, so cheap calls are overcharged. Keep the default unless your pricing requires otherwise.

### `openrouterRecipe` options

| Option | Default | Description |
| - | - | - |
| `unitsPerCredit` | `1000000` (millionths of a credit) | How many `cost` units make one OpenRouter credit. |
| `modelDimension` | `model` | The dimension the model that served the call is reported under. |
| `estimate` | `() => 1` | The requested usage of the check before the call. See [Enforcing a limit on cost](/documentation/ai-agents/access-enforcement#enforcing-a-limit-on-cost). |

## What is reported

OpenRouter is a gateway: one API in OpenAI's format that routes each call to a model from many providers. Every response includes a `usage` object in OpenAI's format with the token counts, plus the cost of the call in OpenRouter credits. The wrapper reads these and reports them to Stigg as the metrics below. For details, see OpenRouter's [usage accounting docs](https://openrouter.ai/docs/cookbook/administration/usage-accounting).

| Client | Method | Event name |
| - | - | - |
| `OpenRouter` | `chat.send` | `openrouter.chat.send` |
| `OpenRouter` | `responses.send` | `openrouter.responses.send` |
| `OpenAI` | `chat.completions.create` | `openrouter.chat.completions.create` |
| `OpenAI` | `responses.create` | `openrouter.responses.create` |

The `OpenRouter` client returns usage in camelCase, such as `promptTokens`. The SDK reports it under the field names OpenRouter's API uses, such as `prompt_tokens`, so the metrics are the same whichever client you use. The `raw.` fields keep the client's own spelling. To meter both clients with one meter, filter on both event names, or set the same [`eventName`](/documentation/ai-agents/usage-metering#configuring-meters-for-async-events) for both.

**Chat Completions** (`chat.send` and `chat.completions.create`)

| Metric | What it counts |
| - | - |
| `prompt_tokens` | Input tokens, **including** cached tokens |
| `completion_tokens` | Output tokens |
| `cached_tokens` | Input tokens served from the cache |
| `total_tokens` | All input and output tokens, as reported by the vendor |
| `cost` | The credits OpenRouter actually charged for the call, in micro-credits by default. Set `unitsPerCredit` to match how your Stigg feature is defined. |

**Responses** (`responses.send` and `responses.create`)

| Metric | What it counts |
| - | - |
| `input_tokens` | Input tokens, **including** cached tokens |
| `output_tokens` | Output tokens |
| `cached_tokens` | Input tokens served from the cache |
| `total_tokens` | All input and output tokens, as reported by the vendor |
| `cost` | The credits OpenRouter actually charged for the call, in micro-credits by default. Set `unitsPerCredit` to match how your Stigg feature is defined. |

Cached tokens are counted **inside** `prompt_tokens` and `input_tokens`. If you price both the input metric and `cached_tokens`, cached tokens are charged twice. `total_tokens` includes all the token counts above, so price the total or the parts, never both.

Each event also carries:

| Reported | What it is |
| - | - |
| `model` dimension | The model that actually served the request. When OpenRouter falls back from the model you asked for, the change is visible in Stigg. |
| Idempotency key | The OpenRouter generation ID, so a retried report is never counted twice. |

When a response carries no cost (for example with bring-your-own-key upstreams), `cost` is omitted and the token metrics are still reported.

Every usage field the vendor returns is also included, with a `raw.` prefix. See [How usage is reported](/documentation/ai-agents/usage-metering#how-usage-is-reported).


## Related topics

- [Meter LLM gateway spend](/guides/ai-agents/meter-llm-gateway-spend.md)
- [AI agents](/documentation/ai-agents/overview.md)
- [Usage metering](/documentation/ai-agents/usage-metering.md)
- [Meter LLM token usage](/guides/ai-agents/meter-llm-token-usage.md)
- [JavaScript](/api-and-sdks/changelog/agent-sdk/javascript.md)


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