Supported providers and gateways
stigg.wrap(client) recognizes each of these from the client object itself, so you do not name the vendor. Each vendor’s page shows how to wrap it and exactly what is reported.
LLM providers
Anthropic
Messages API, streaming included, with the full prompt-caching breakdown
OpenAI
Chat Completions and Responses API, streaming included
Google Gemini
One-shot calls and chats, streaming included, with thinking tokens reported separately
Mistral
Chat, fill-in-the-middle and agent calls, streaming included
Groq
Groq’s own client or the OpenAI client, reported under Groq’s own event name
xAI
Grok models, through the OpenAI client
DeepSeek
DeepSeek’s own models, through the OpenAI client
Perplexity
Models with built-in web search, through the OpenAI client
Together AI
A large catalog of open-source models, through the OpenAI client
Fireworks AI
Open-source and fine-tuned models, through the OpenAI client
Ollama
Models you host yourself: meter and limit your own capacity
vLLM
Your self-hosted model server, through the OpenAI client
TypeSafe
Jev, a model that answers typed questions about your data (yes or no, choice or score) in one call
LLM gateways
OpenRouter
Reports the credits actually charged and the model that actually served, through the OpenRouter or OpenAI client
LiteLLM Proxy
Self-hosted or cloud gateway: reports the cost LiteLLM computed for each call
Amazon Bedrock
The Converse API, for every model Bedrock serves
Cloudflare
AI Gateway and Workers AI, with cache hits left unbilled and the model that served
Databricks
Model serving endpoints, with the model that actually answered each call
Bring your own vendor
Any OpenAI-compatible API
Point the OpenAI client at any endpoint in OpenAI’s format and it is metered with no extra code
Custom adapter
Any other model or API, including your own: describe where its usage is and the SDK does the rest
How usage is reported
- Same names as the vendor. Each metric keeps the name the vendor uses, such as
input_tokensfor Anthropic,prompt_tokensfor OpenAI orpromptTokenCountfor Gemini. You can compare the numbers in Stigg directly with the vendor’s own usage reports, such as its usage dashboard. - A total, and its parts. Every vendor also reports a total: the vendor’s own where it publishes one, such as
total_tokensortotalTokenCount, and otherwise the sum of the parts, added up by the SDK (Anthropic, Ollama and TypeSafe). The total includes the other counters, so price the total or the parts, never both. - What a gateway charged, where it says. OpenRouter and LiteLLM Proxy also report
cost, what the gateway actually charged for the call, and themodelthat served it. See Charging by tokens or by cost. - Every usage field, not just the named metrics. Each event also includes every usage field the vendor returned, with a
raw.prefix (raw.<field name>). If a vendor adds a field the SDK does not report as a metric yet, you can build a meter on it without waiting for an SDK update. - No prompts or responses. Only usage numbers are sent to Stigg. The text of your prompts and the model’s responses never leaves your application.
Ingest modes
The SDK reports usage in one of Stigg’s two standard ways, chosen by the ingest mode:
For how the two differ, including how quickly limits are enforced, see Supported usage sources.
You can set the mode for the whole SDK, for one wrapped client, or for a single call. A more specific setting takes precedence:
- Usage reported to a feature in
syncmode must be a whole number. Stigg’s usage API accepts only whole numbers, so a fractional value is not reported and the failure is passed toonError. A credit type accepts fractional amounts in either mode, and so does an event inasyncmode. - Credits you calculate in code are deducted directly. Usually the SDK reports raw metrics, such as token counts, and Stigg converts them into credits using your credit consumption settings. If you convert usage into credits yourself with a usage mapper, the SDK deducts the result straight from the credit balance, in either mode, so it is not converted twice. It recognizes these credits by name: the mapper’s output must use the ID of the credit type the call is checked against. For example, a mapper that outputs
{ credits: 3 }for a call checked against{ currencyId: 'credits' }deducts 3 credits. The ID must match exactly: a mapper that outputs{ data_credit: 3 }for a call checked against{ currencyId: 'credits' }reports a feature nameddata_credit, which needs a meter like any other feature. - In
asyncmode, deducted credits can take a moment to show. The deduction is applied to the balance asynchronously, so a check made right after a call may not include it yet. Usesyncwhen each call must be deducted before the next check.
Configuring meters for async events
Inasync mode each call becomes one event:
- The event name is the vendor call that produced it, for example
anthropic.messages.create. Each vendor’s event names are listed on its page. Usage from a gateway you operate is named<vendor>.proxied, or theeventyou pass toingestFromHeaders(). - Every metric is a field on the event, under the names on the provider’s page.
stiggContext.eventName, for example eventName: { name: 'llm.call' }, to put several vendors behind one meter.
To meter a metric, create a metered feature with a raw-events meter that filters on the event name and sums the field. For example, output tokens from Anthropic:
dimensions hold:
- The metrics, under the names on the provider’s page. Here
costis in millionths of an OpenRouter credit. - Your own
dimensions, and the agent ID under your agent type’s attribution key (hereagentId) when you set one. Use them as meter filters or for governance attribution. - For gateways, the
modelthat served the call. - Every usage field the vendor returned, with a
raw.prefix. Nested fields are flattened into single values.
Usage mappers
By default the SDK reports exactly the usage the vendor returned, under the vendor’s own metric names, so the numbers in Stigg match the vendor’s own usage reports. This needs no configuration. Each vendor’s metrics are listed on its page. To report something else, setstiggContext.usageMapper to one of the mappers below, or to your own. Set it on wrap() as the client’s default, or pass it with each call to override the default for that call.
Weighted credits
Converts each call’s usage into credits in your code, using a rate table, and deducts them straight from the credit balance. Usually you do not need this. The standard way to charge credits for tokens is in Stigg: create a metered feature for each token metric and set its credit cost in the credit type’s Credit consumption settings. See Credits. Use weighted credits instead when:- You want the rates in code, for example to charge different rates per model by passing a different mapper in each call’s
stiggContext, instead of creating a feature for each case in Stigg. - You want to skip meters. Credits calculated in code are deducted straight from the balance, with no metered feature or meter to configure. With
syncmode, the next check already includes them.
weightedMetrics() turns a rate table into a mapper that reports one number, typically to a credit type or a single metered feature:
- The table is typed against the recipe’s own metric names, so a typo does not compile.
- A metric left out of the table is free.
- The total is not rounded. A credit type accepts fractional amounts, so a call worth a fifth of a credit costs a fifth of a credit. When the target is a metered feature in
syncmode, which accepts only whole numbers, choose rates and aperthat divide evenly, or round in your own mapper. - A call that measured nothing reports nothing.
unitsPerTokens('credits', 1_000) reports one unit per 1,000 tokens of any kind. It does not round either.
A call attributed to an agent is the exception: Governance counts agent and team usage in whole units, so the SDK rounds the amount up to the next whole unit and logs that it did. See Usage governance.
wrap(), like any other stiggContext field. This keeps the wrapped client generic, for example to charge a different rate per model:
Normalized metrics across vendors
Vendors disagree on whether cached tokens are already counted inside the input total: OpenAI and Gemini include them, while Anthropic and Amazon Bedrock count cache reads and writes separately. Each vendor’s page says which applies under What is reported. To price every vendor with one set of features, usenormalizedMetrics. It reports four non-overlapping counters under SDK-owned names: stigg_input_tokens, stigg_output_tokens, stigg_cache_read_tokens and stigg_cache_creation_tokens.
Your own mapper
When none of the built-in mappers fits, write your own. A mapper is a plain function: it receives the usage the recipe measured for a call, and returns what to report, as a{ featureOrCurrencyId: value } record. Each recipe exports a type for what it receives:
usageMapper:
sync mode must be a whole number.
The mapper also receives the rest of the measurement: usage (the four normalized token counters), dimensions, and estimated. estimated is true when a count is the SDK’s estimate rather than the vendor’s. This happens when a streamed Anthropic call is stopped before it finishes: Anthropic reports the output count only at the end of the stream, so the SDK estimates the output from the text received so far, at about four characters per token. To meter only counts the vendor reported, return {} when estimated is set:
Metering through a gateway
An LLM gateway sits between your code and the LLM providers. It lets you:- Work with one API across many LLM providers, often with failover, caching and routing between models.
- Set up one commercial agreement, with the gateway, while still using models from many providers.
Charging by tokens or by cost
An OpenRouter or LiteLLM Proxy call reports two kinds of numbers: the token counts, andcost, what the gateway charged for the call. You choose which one usage is measured in:
- Tokens, to treat calls the same way whether they go straight to a provider or through a gateway. Meter the token metrics.
cost, to follow what the gateway actually charged, for example to pass it on with your margin. Meter thecostmetric.
Optional: convert cost into credits in your code
Optional: convert cost into credits in your code
Instead of mapping a feature to credits in Stigg, the SDK can convert usage into credits before it is reported, and deduct them straight from the credit balance. Use this only when the conversion has to live in code. See Weighted credits.
Routing drift
Themodel dimension records the model that served, not the one you asked for. Filter or group a meter by it in Stigg to see how often your traffic falls back, and how that changes your cost.
A gateway you operate
A proxy you run is a separate process. It sees requests, not the customer or agent behind them, and the customer’s budget lives in your application. So the work splits across the boundary:- Your application checks the budget in-process, as usual, and sends attribution with the request as
x-stigg-*headers. - Your proxy forwards the request, reads usage off the provider’s response, and reports it with
stigg.ingestFromHeaders().
In your application
stigg.headers(context).
In your proxy
vendor is one of anthropic, openai (Chat Completions), openai-responses, gemini or typesafe. It is named rather than guessed because several vendors share field names (input_tokens appears in both Anthropic and OpenAI Responses usage), and a wrong guess would report to the wrong meter. If you omit it, pass a metrics mapper so the SDK knows what to report.
Idempotency
Every report carries an idempotency key, so a retried report is not counted twice:- When the vendor returns a request ID that is stable across its own retries (for example an OpenRouter generation ID), that ID is used.
- Otherwise the SDK generates one per call.
- Set
stiggContext.idempotencyKeyon a call to choose your own, for example when you resume a stream and report the same turn again.
sync mode each metric is keyed separately as <key>:<metric>.
Errors and flushing
Reporting never breaks the call. If a report fails, the response is still returned and the failure is passed toonError:
onError logs a warning, so failures are visible before you wire it up.
Reports are sent in the background. In serverless functions, request-scoped workers and CLIs, wait for them to be sent before the process exits:
flush() returns a summary of what was reported: the number of checks and ingests, failures, and the totals per metric.
Troubleshooting
The credit balance does not go down
A credit balance only goes down when the feature you report is mapped to that credit type in Stigg. If it is not, every call succeeds and every report is accepted, but the balance never changes. Insync mode the SDK detects this from Stigg’s response and logs: