Connect your providers
Sending usage data
Divide shared AI spend by customer, feature, user and team, from your application's own record of each call.
A bill says what was spent on each model, each day. It cannot say which of your customers a call was for, because only your application knows that. When many customers share one API key, usage data is how CostReveal divides that spend: your application, or the gateway in front of it, reports each call with who it was for, and CostReveal divides the billed cost in proportion.
It never sits between your application and the provider. Nothing is slowed or blocked, and if it stops arriving, your application carries on exactly as before. What arrives shows under Connections, Usage data at once, and divides the bill within seconds, always within a minute.
What it explains, and what it does not
- Only billed spend is divided. Usage data never adds a cent. Totals stay exactly what the provider billed.
- Only what it covers. If your data reports 60 percent of the tokens a model was billed for, 60 percent of that model's unexplained cost is divided, and the rest stays Not yet explained. The Usage data section of Connections shows the coverage of each model.
- Tags and owners come first. A workspace or resource already owned by a team keeps that owner; usage data divides only what they leave unknown.
- The basis says so. Cost divided this way is marked ALLOCATED, split by measured usage share.
- Web search and code execution fees too. Anthropic bills them apart from tokens and without a model. Web searches are billed by the request, so they are divided by the searches each owner's calls reported, covered as far as those reports cover the searches billed. Code execution is billed by the time its containers ran, which no call states, so it is divided by each owner's share of the code executions reported, covered as far as the usage data covers the day's billed tokens. Neither is ever divided by tokens: a fee no call reports stays Not yet explained.
- Anthropic, for now. Division needs the tokens a bill stated, by kind of token, which CostReveal reads from Anthropic's usage report. OpenAI's costs report states tokens too; it is divided once its line item names are certified against a real organization's report. Other providers follow as their connectors do.
Create a key
Under Settings, API keys, create a key with the scope Send data only. It can send usage data to its project and read nothing, so it is safe to keep in your services.
OpenTelemetry
If your application or gateway emits OpenTelemetry GenAI spans, point its exporter at CostReveal. Both OTLP encodings are accepted, protobuf and JSON, compressed or not:
OTEL_EXPORTER_OTLP_ENDPOINT=<the OpenTelemetry endpoint shown in Connections>
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer%20<your key>
Each span with token usage is one call. CostReveal reads:
| From the span | Attribute |
|---|---|
| Provider | gen_ai.provider.name, or the older gen_ai.system |
| Model | gen_ai.response.model, else gen_ai.request.model |
| Tokens | gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, gen_ai.usage.cache_read.input_tokens, gen_ai.usage.cache_write.input_tokens, and the older names they replaced |
Input tokens include cached tokens, as the GenAI conventions define them. An instrumentation that follows Anthropic's own convention instead, leaving them out, is recognized when a span reports fewer input tokens than cached ones.
Naming who a call was for. Add attributes to the span, or to the resource that emits it, read with your project's tag keys: the first key present names the owner, compared without case or punctuation, and matching the last part of a dotted name. With the default keys, customer, app.customer and metadata.customer all name the customer. OpenTelemetry's user.id always names the user. Send identifiers, never email addresses.
Spans without token usage, such as tool calls, are passed over. No prompt or response content is read.
LiteLLM
Turn on LiteLLM's OpenTelemetry callback in the proxy's config.yaml:
litellm_settings:
callbacks: ['otel']
and set the two variables above. LiteLLM sends request metadata as metadata. attributes, so a request carrying "metadata": {"customer": "acme-42", "feature": "summaries"} is divided by that customer and feature. To have LiteLLM report cached tokens, set OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental.
Portkey
Portkey's export of GenAI traces is experimental, and available on its Enterprise and self hosted gateway. Where you have it, set EXPERIMENTAL_GEN_AI_OTEL_TRACES_ENABLED to true, and its endpoint and headers variables to the endpoint and header above.
Reading a gateway's own logs
For a gateway that cannot send OpenTelemetry, or where it is simpler, CostReveal reads the gateway's own logs instead. Under Connections, Usage data, choose Add a gateway, the gateway, and a key that reads its logs. CostReveal reads with it once before saving it, then encrypts it and never shows it again.
Each gateway is read every 15 minutes, Portkey hourly, starting with the previous month so its bill can be explained. The newest ten minutes are read the next time, once the gateway has written them, and each read goes back over the last hour for logs written late. Each call is counted once, by the gateway's own request ID. What the gateway priced a call at is not used: the provider's bill says what it cost.
| Gateway | What CostReveal reads | The key |
|---|---|---|
| LiteLLM | The proxy's spend logs, from /spend/logs/v2 | A key of a user with the proxy_admin_viewer role |
| Helicone | Your organization's requests, in the US or EU region | A Helicone API key with read access |
| Portkey | Your workspace's logs, through log exports, a day at a time | A workspace API key that can export logs, on Portkey's Enterprise plan |
Who each call was for is read with your project's tag keys, as for OpenTelemetry:
- LiteLLM: the request's
metadata, as LiteLLM keeps it for spend logs, then theuserthe request named and the LiteLLM team's alias. - Helicone: custom properties, sent as
Helicone-Property-headers such asHelicone-Property-Customer: acme-42, then the Helicone user ID. - Portkey: the request's metadata, then its
_user.
A LiteLLM proxy must be reachable at a public https address. Only successful calls are counted.
If a read fails, Usage data says why, every call already counted stays, and reading picks up where it stopped once the cause is fixed. Removing a gateway stops reading it and deletes its key; the calls it brought in stay counted.
The usage events endpoint
Without OpenTelemetry, send each call as an event, up to 1,000 a request, with the same key as a bearer token, to the usage events endpoint shown in Connections:
{
"events": [
{
"id": "req_018f2c",
"timestamp": "2026-09-29T14:03:11Z",
"provider": "anthropic",
"model": "claude-opus-5",
"inputTokens": 5200,
"outputTokens": 800,
"cacheReadTokens": 4000,
"webSearchRequests": 2,
"codeExecutions": 1,
"customer": "acme-42",
"feature": "summaries",
"user": "u_91c",
"team": "search"
}
]
}
id is your own identifier of the call, so a batch sent again is counted once. webSearchRequests is what Anthropic's response states in usage.server_tool_use.web_search_requests, and codeExecutions the number of times the call ran the code execution tool; both are optional. OpenTelemetry's GenAI conventions have no attribute for either yet, so send them as events, or read them from LiteLLM's logs, which state web searches. The answer says how many events were counted, how many were already counted, and why any were refused.
What is refused
A call without a model or provider, with token counts that are not whole numbers, dated more than a day ahead, or more than 62 days ago, is refused and not counted. Connections, Usage data shows what arrived in the last seven days and why anything was refused, and any model your data names that no bill has.
Send each call once, by one route: a call reported both by your application and by your gateway, or both sent by a gateway and read from its logs, is counted twice, which shows as over reported coverage.