> ## 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 client, meter input and output tokens per customer, and gate calls on a usage limit.

In this guide you bill customers for the tokens their requests consume. By the end, every call your application makes to Anthropic or OpenAI is reported to Stigg against the right customer, priced by a plan, and denied when the customer reaches their monthly limit.

**What you will build:**

* Two metered features, input tokens and output tokens, fed by the events the SDK sends.
* A wrapped LLM 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.
* Node.js 22 or later and an Anthropic or OpenAI API key.
* A customer in Stigg with an active subscription. See [Provisioning customers](/guides/quick-start-guides/provisioning-customers).

<Steps>
  <Step title="Create metered features for tokens">
    The SDK reports each call as one event named after the vendor method, with the token counts as fields. Create one metered feature per counter you want to bill.

    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 `anthropic.messages.create` (or `openai.chat.completions.create`).
       * **Aggregation:** `SUM` of `output_tokens` (or `completion_tokens` for OpenAI).
    4. Repeat for **Input tokens** with `input_tokens` (or `prompt_tokens`).

    See [Supported providers](/documentation/ai-agents/llm-wrappers/supported-providers#at-a-glance) for the event and field names of every vendor.
  </Step>

  <Step title="Price the features in a plan">
    Add both features to the customer's plan, with the price per unit and the monthly limit you want. For example, a limit of 2,000,000 output tokens per month, and a per-unit price for overage. See [Metered features](/documentation/modeling-your-pricing-in-stigg/features/creating-features/metered-features).
  </Step>

  <Step title="Install the SDK">
    ```bash theme={null}
    npm install @stigg/agent-sdk-js @anthropic-ai/sdk
    ```

    ```bash .env theme={null}
    STIGG_SERVER_API_KEY=server-...
    ANTHROPIC_API_KEY=sk-ant-...
    ```
  </Step>

  <Step title="Wrap your client">
    Create the SDK once per process, and wrap the client you already use.

    <CodeGroup>
      ```typescript Anthropic theme={null}
      import Anthropic from '@anthropic-ai/sdk';
      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 names its own.
      export const anthropic = stigg.wrap(new Anthropic());
      ```

      ```typescript OpenAI 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),
      });

      export const openai = stigg.wrap(new OpenAI());
      ```
    </CodeGroup>

    Because the wrapped client has no default customer, TypeScript requires every gated call to name one. That is the safest setup for a multi-tenant server.
  </Step>

  <Step title="Name the customer on each call">
    Add a `stiggContext` to the parameters of each call. Everything else stays the same.

    <CodeGroup>
      ```typescript Anthropic theme={null}
      const reply = await anthropic.messages.create({
        model: 'claude-opus-5',
        max_tokens: 1024,
        messages: [{ role: 'user', content: question }],
        stiggContext: { customerId: req.user.customerId },
      });
      ```

      ```typescript OpenAI theme={null}
      const reply = await openai.chat.completions.create({
        model: 'gpt-5',
        messages: [{ role: 'user', content: question }],
        stiggContext: { customerId: req.user.customerId },
      });
      ```
    </CodeGroup>

    `stiggContext` is removed before the request is sent, so the provider never sees it. When the response arrives, the SDK reports the token counts in the background.

    With no `entitlement` in the context, calls are only metered, never blocked. To block them once the customer reaches a limit, see the **Gate calls on the limit** step below.

    <Note>
      For streamed OpenAI calls, add `stream_options: { include_usage: true }`. Otherwise OpenAI sends no usage to meter.
    </Note>
  </Step>

  <Step title="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();
    ```
  </Step>

  <Step title="Verify the usage">
    Make a few calls, then open the customer in the Stigg app. Usage events appear under **Usage events** with the vendor event name and the token fields, and the feature usage on the customer's page updates as the events are aggregated.

    If events arrive but usage stays at zero, check that the meter's `eventName` filter matches exactly. See [Viewing usage events](/documentation/getting-usage-data-into-stigg/viewing-usage-events).
  </Step>

  <Step title="Gate calls on the limit">
    Set `entitlement` to the feature to check against, and calls are denied once the customer reaches the limit, before the request reaches the provider.

    ```typescript theme={null}
    try {
      const reply = await anthropic.messages.create({
        model: 'claude-opus-5',
        max_tokens: 1024,
        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.
  </Step>
</Steps>

## Next steps

* Convert tokens into credits that drain in real time: [Weighted credits](/documentation/ai-agents/llm-wrappers/metering#weighted-credits).
* Bill real gateway cost instead of tokens: [Bill LLM gateway spend](/guides/ai-agents/bill-llm-gateway-spend).
* Give each agent its own budget: [Set budgets for AI agents](/guides/ai-agents/set-budgets-for-ai-agents).


## Related topics

- [Metering usage](/documentation/ai-agents/llm-wrappers/metering.md)
- [Meter usage from your own gateway](/guides/ai-agents/meter-usage-from-your-own-gateway.md)
- [DeepSeek](/documentation/ai-agents/llm-wrappers/providers/deepseek.md)
- [Mistral](/documentation/ai-agents/llm-wrappers/providers/mistral.md)
- [Gateways](/documentation/ai-agents/llm-wrappers/gateways.md)


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