Problem Context
Tokens are the unit of cost, latency, and capacity for every LLM you call. Without token-level telemetry you cannot answer the three questions that come up every week in production: which tenant is most expensive, which feature is regressing on cost, and which prompt is the biggest line item?
- The monthly OpenAI invoice arrived and nobody can decompose it
- You don't know if cached tokens are saving you anything
- One tenant's long context just ate this week's margin and you noticed on day six
What to Count
input_tokensโ what you sent.output_tokensโ what came back.cached_input_tokensโ billed at a discount (Anthropic 90% off, OpenAI ~50% off).reasoning_tokensโ hidden chain-of-thought on o-series / thinking models.tool_call_tokensโ tool schemas in input + tool args in output. Often the silent majority.
Token counts come back in every provider response โ read them from the response, do not estimate from tiktoken after the fact unless you have to.
flowchart LR
R[Response] --> U[usage{ input, output,<br/>cached, reasoning }]
U --> C[cost = ฮฃ tokens ร price by class]
C --> M[Emit metric:<br/>llm.tokens / llm.cost.usd]
M --> D[Dashboards by:<br/>tenant, endpoint, model, prompt_id]
The Cost Formula
function computeCost(usage: Usage, model: ModelPrice): number {
return (
(usage.input_tokens - (usage.cached_input_tokens ?? 0)) * model.input
+ (usage.cached_input_tokens ?? 0) * model.cached_input
+ usage.output_tokens * model.output
+ (usage.reasoning_tokens ?? 0) * model.output // reasoning bills as output
) / 1_000_000;
}Keep a single source-of-truth price table in code, versioned with the date the price went into effect. Vendors change prices quietly; you do not want this on a wiki.
Dimensions That Pay Off
Always emit token + cost metrics with at least these dimensions:
model(with version) โ to spot silent fallback to a pricier model.endpoint/featureโ e.g.support_chat,summary_v2.tenant_id(or hashed) โ for per-customer attribution.prompt_template_id + versionโ to A/B prompt cost changes.cache_hitโ boolean; tracks whether your cache strategy is actually working.
Budgets and Hard Limits
Track per-tenant rolling spend (15-minute window + daily total) in Redis. Three thresholds:
- Soft warn at 80% of daily quota โ email the tenant's admin and your ops channel.
- Throttle at 100% โ switch to a cheaper model or queue requests.
- Hard stop at 150% โ return a typed error explaining the cap; this catches runaway loops fast.
Caching: Half the Cost, If You Set It Up Right
- Put the stable system prompt + few-shot examples + retrieved context at the top. Both Anthropic and OpenAI cache by prefix.
- Anthropic requires explicit
cache_control: { type: "ephemeral" }blocks; OpenAI auto-caches when the prefix > 1024 tokens. - Track
cache_hit_rate = cached_tokens / input_tokensper endpoint โ below 50% on a stable prefix means your prompt is unstable upstream.
Failure Modes
- Reasoning tokens ignored โ o-series / thinking models can burn 5โ20ร tokens you never see in the message. Always add
reasoning_tokensto your cost. - Streamed responses not totaled โ some SDKs only emit usage on the final chunk; make sure your wrapper waits for it.
- Estimating with
length / 4โ wrong by 30โ60% for non-English text and code. - Mixing currencies โ Azure prices in regional pricing; convert once at ingest, store in USD.
Production Checklist
- Read
usagefrom the provider response; do not estimate. - Versioned price table in code; updated within 24h of vendor changes.
- Emit
llm.tokensandllm.cost.usdwith dimensions{model, endpoint, tenant, prompt_id, cache_hit}. - Three-tier budget: warn, throttle, hard-stop.
- Track cache hit rate per endpoint as a first-class KPI.
Key Takeaways
- Token tracking is the foundation of cost control โ you cannot optimise what you cannot decompose.
- Dimensions matter more than the number; per-tenant + per-prompt is the minimum useful split.
- Caching, budgets, and reasoning-token awareness are the three biggest wins on the bill.

