> ## 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.

# Meter LLM token usage

> Wrap your LLM gateway client, meter input and output tokens per customer, and enforce a monthly usage limit.

In this guide you meter the tokens each customer's requests consume. By the end, every call your application makes through an LLM gateway is reported to Stigg against the right customer, counted against the customer's monthly token limit, and denied once that limit is reached.

This guide uses [OpenRouter](/documentation/ai-agents/providers/openrouter), one of the most common LLM gateways. The same steps work for any [supported provider or gateway](/documentation/ai-agents/usage-metering#supported-providers-and-gateways): only the client you wrap and the event and field names change.

**What you will build:**

* Two metered features, input tokens and output tokens, fed by the events the SDK sends.
* A wrapped gateway client that reports usage after every call.
* A usage limit that is checked before every call.

**Before you start:**

* A Stigg server API key for your environment.
* An [OpenRouter](https://openrouter.ai) API key.
* Node.js 22 or later.
* A customer in Stigg with an active subscription. See [Provisioning customers](/guides/quick-start-guides/provisioning-customers).

**1. Create metered features for tokens**

The SDK reports each call as one event named after the gateway method, with the token counts as fields. For OpenRouter through the `OpenAI` client, the event is `openrouter.chat.completions.create`, and the token counts are `prompt_tokens` (input) and `completion_tokens` (output). Create one metered feature per counter you want to meter.

1. In the Stigg app, go to **Product catalog → Features** and click **Create feature**.
2. Name it **Output tokens**, with ID `feature-output-tokens`, and choose **Metered**.
3. Choose **Raw events** as the meter type and configure:
   * **Filter:** `eventName` equals `openrouter.chat.completions.create`.
   * **Aggregation:** `SUM` of `completion_tokens`.
4. Repeat for **Input tokens**, with ID `feature-input-tokens` and `SUM` of `prompt_tokens`.

Each vendor's event and field names are on [its page](/documentation/ai-agents/usage-metering#supported-providers-and-gateways).

You can also create the features by describing them to your AI coding assistant, with the Stigg [AI Terminal](/api-and-sdks/ai-terminal) set up:

In my Stigg environment, create two metered features that use a raw-events meter:

* "Output tokens", ID feature-output-tokens: filter where eventName equals openrouter.chat.completions.create, aggregate SUM of completion\_tokens.
* "Input tokens", ID feature-input-tokens: filter where eventName equals openrouter.chat.completions.create, aggregate SUM of prompt\_tokens.

**2. Add the features to a plan as entitlements**

Add both features to the customer's plan as entitlements, each with a monthly usage limit. For example, 2,000,000 output tokens and 8,000,000 input tokens per month, reset monthly. Customers on the plan can then use tokens up to their limit. See [Adding entitlements to a plan](/documentation/modeling-your-pricing-in-stigg/plans/assigning-features-plans#metered-features).

In my Stigg environment, add feature-output-tokens and feature-input-tokens as entitlements to my plan, with a usage limit of 2,000,000 output tokens and 8,000,000 input tokens, both reset monthly.

**3. Install the SDK**

```bash theme={null}
npm install @stigg/agent-sdk-js openai
```

OpenRouter's API follows OpenAI's format, so you call it with OpenAI's `openai` client.

**4. Configure your API keys**

Add your Stigg server API key and your OpenRouter API key to your environment:

```bash .env theme={null}
STIGG_SERVER_API_KEY=server-...
OPENROUTER_API_KEY=sk-or-...
```

Use a **server** API key, from **Integrations > API keys** in the Stigg app. A client (publishable) key cannot report usage. See [API keys](/documentation/managing-your-account/environments/api-keys).

**5. Wrap your client**

Create the SDK once per process, and wrap the gateway client:

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

export const stigg = new StiggAgentSDK(process.env.STIGG_SERVER_API_KEY, {
  onError: (error, context) => console.error('[stigg]', context.operation, error.message),
});

// No default customer: each call sets its own.
export const llm = stigg.wrap(
  new OpenAI({ baseURL: 'https://openrouter.ai/api/v1', apiKey: process.env.OPENROUTER_API_KEY }),
);
```

Because the wrapped client has no default customer, TypeScript requires every call to set one. That is the safest setup for a server that serves many customers.

**6. Set the request context**

Add a `stiggContext` to the parameters of each call, with the customer the call is for. Everything else stays the same.

```typescript theme={null}
const reply = await llm.chat.completions.create({
  model: 'anthropic/claude-sonnet-5',
  messages: [{ role: 'user', content: question }],
  stiggContext: { customerId: req.user.customerId },
});
```

`stiggContext` associates the request with the customer whose usage it is. It is not included in the request sent to OpenRouter. When the response arrives, the SDK reports the token counts in the background.

> [!NOTE]
>
> For streamed calls, add `stream_options: { include_usage: true }`. Otherwise OpenRouter sends no usage to meter.

**7. Flush before exit**

Reports are sent in the background. In a long-running server you do not need to do anything. In a serverless function, a queue worker or a script, wait for them before the process ends:

```typescript theme={null}
await stigg.flush();
```

**8. Verify the usage**

Make a few calls, then check two places in the Stigg app:

* **Usage events**, in the left navigation, lists every event as it arrives, with the event name `openrouter.chat.completions.create` and the token fields. See [Viewing usage events](/documentation/getting-usage-data-into-stigg/viewing-usage-events).
* The **Entitlement usage** tab of the customer's page shows the usage of each feature, which updates as the events are aggregated. See [Viewing customers entitlement usage](/documentation/managing-customers-and-subscriptions/customers/viewing-customers-entitlement-usage).

If events arrive but the entitlement usage stays at zero, check that the meter's `eventName` filter matches the event name exactly.

**9. Enforce the usage limit**

Set `entitlement` in the `stiggContext` to the feature or credit type to check against. Calls are then denied once the customer reaches the limit, before the request reaches the gateway.

```typescript theme={null}
try {
  const reply = await llm.chat.completions.create({
    model: 'anthropic/claude-sonnet-5',
    messages,
    stiggContext: {
      customerId: req.user.customerId,
      entitlement: { featureId: 'feature-output-tokens' },
    },
  });
  res.json(reply);
} catch (err) {
  const decision = stigg.denial(err);
  if (!decision) throw err;
  res.status(402).json({ message: decision.message, remedy: decision.remedy });
}
```

`decision.remedy` tells you what to offer: `subscribe` when the plan does not include the feature, `top-up` or `raise-budget` when a limit is reached. See [Handling denied access](/documentation/ai-agents/access-enforcement#handling-denied-access).

## Next steps

* Convert tokens into credits that burn down in real time: [Weighted credits](/documentation/ai-agents/usage-metering#weighted-credits).
* Meter what the gateway actually charged instead of tokens: [Meter LLM gateway spend](/guides/ai-agents/meter-llm-gateway-spend).
* Give each agent its own budget: [Set budgets for AI agents](/guides/ai-agents/set-budgets-for-ai-agents).


## Related topics

- [Usage metering](/documentation/ai-agents/usage-metering.md)
- [Mistral](/documentation/ai-agents/providers/mistral.md)
- [DeepSeek](/documentation/ai-agents/providers/deepseek.md)
- [Perplexity](/documentation/ai-agents/providers/perplexity.md)
- [Databricks](/documentation/ai-agents/providers/databricks.md)


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