Claude API Usage Tracking Per Customer: A Practical Guide
If you're building a product on top of Claude and charging customers, at some point you need to answer a simple question: how much is each customer actually costing me, and are they within their plan limits? Anthropic's API gives you aggregate token usage in your console, but it doesn't know about your customers, your tiers, or your billing periods. That mapping is entirely on you to build.
This article covers the practical ways to implement per-customer usage tracking for the Claude API — what to log, how to attribute costs, how to enforce quotas, and where a managed layer like SubToAPI removes most of the plumbing.
Why Anthropic's dashboard isn't enough
The Anthropic console shows you total requests and token counts for your organization. That's useful for your own cost forecasting, but it's a single number — it doesn't break down by end user, tenant, or API key. If you're running a multi-tenant SaaS where different customers make Claude calls through your backend, you need your own attribution layer sitting between your app and the Anthropic API.
There are three common reasons teams need this:
- Usage-based billing — charging customers based on tokens or requests consumed.
- Plan enforcement — giving free-tier customers a hard cap and paid customers a higher one.
- Cost visibility — knowing which customer or feature is driving your Claude spend before it shows up as a surprise on the invoice.
What to log on every request
Whatever approach you take, capture this data for every call, ideally in a structured table rather than raw logs:
- Customer or tenant ID
- Timestamp
- Model used (Opus, Sonnet, Haiku — pricing differs significantly)
- Input tokens and output tokens (from the API response
usageobject) - Whether the request used tools, streaming, or extended context
- Request status (success, error, rate-limited)
The usage field in every Claude API response gives you input_tokens and output_tokens directly — you don't need to estimate with a tokenizer. Store both, since output tokens are usually priced higher and dominate cost for long-form responses.
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 1024,
messages: [{ role: "user", content: prompt }],
}),
});
const data = await response.json();
await db.usageEvents.insert({
customerId,
model: data.model,
inputTokens: data.usage.input_tokens,
outputTokens: data.usage.output_tokens,
createdAt: new Date(),
});
Once you have this table, per-customer usage is just a GROUP BY customer_id query over your chosen billing window.
Building attribution into your architecture
The cleanest pattern is to route every Claude call through a single internal function or service that wraps the API call and writes the usage event atomically. Avoid calling Claude directly from multiple places in your codebase — you'll inevitably miss logging in one path and end up with under-billed customers.
A common structure:
- Your app calls an internal
callClaude(customerId, params)function. - That function makes the actual API request, applying your system prompt and any per-customer overrides.
- On response, it writes the usage event before returning the result to the caller.
- A separate job aggregates events into daily or monthly rollups per customer for billing and dashboards.
This gives you a single choke point for logging, rate limiting, and quota checks, which is much easier to reason about than scattering API calls across your codebase.
Enforcing quotas before the request
Tracking after the fact is necessary for billing, but if you want to stop a customer from blowing past their plan limit, you need a check before the request goes out — otherwise you're always one request behind.
async function callClaude(customerId, params) {
const usage = await getMonthlyUsage(customerId);
const plan = await getPlan(customerId);
if (usage.totalTokens >= plan.tokenLimit) {
throw new Error("Usage limit exceeded for this billing period");
}
// proceed with the API call
}
For high-throughput products, run this check against a cache (Redis counter incremented per request) rather than querying your primary database on every call — a database round trip per request adds latency you don't want on the hot path.
Where per-key tracking simplifies things
If each of your customers or applications gets its own API key, usage tracking becomes much simpler — you can attribute cost by key instead of building a separate tagging system inside your database.
This is one of the reasons teams put a layer like SubToAPI in front of their Claude access: it issues distinct sub_live_... keys per application or team, and every request made with a key is already tagged with usage metadata — streaming responses, tool calls, and token counts included — without you writing custom logging middleware. If you're already tracking usage per customer manually, moving key issuance to /docs/quickstart can remove a chunk of the bookkeeping, since the per-key breakdown is available directly in the dashboard instead of being reconstructed from logs.
For teams with multiple seats, plans like Solo (€9), Team (€19/seat), and Scale (€49/seat) map naturally onto per-customer or per-team key structures — see /pricing for details.
Handling streaming and tool use in your logs
Two things trip people up when they add usage tracking after the fact:
- Streaming responses (see /docs/streaming) don't give you a single
usageobject up front — token counts arrive incrementally or in the finalmessage_stopevent. Make sure your logging code reads from the terminal event, not the first chunk. - Tool use (see /docs/tools) can trigger multiple round trips per user turn — a tool call, a tool result, and a follow-up completion. If you're billing per "interaction" rather than per raw API call, decide up front whether a multi-step tool exchange counts as one billed unit or several, and log a session ID that ties the steps together.
Getting this wrong doesn't break functionality, but it does produce billing numbers that don't match what actually happened — which is the kind of bug that only surfaces when a customer disputes an invoice.
Getting started quickly
If you're weighing whether to build this attribution layer yourself or use a hosted one, the honest answer depends on scale: a single-tenant internal tool can get away with a simple logging wrapper and a cron job. A multi-tenant SaaS billing customers directly benefits from key-level separation from day one, since retrofitting per-customer attribution onto a shared API key is painful. You can try a free trial at /signup to see the dashboard-based approach before committing.
questions
Do I need to track tokens or requests for accurate billing? Tokens. Request counts don't reflect cost — a single request with a 50,000-token document costs far more than dozens of short ones. Always log input_tokens and output_tokens from the API response.
Can I track usage without giving each customer a separate API key? Yes, by tagging every internal call with a customer ID and logging usage in your own database. It works, but it puts the correctness burden entirely on your application code rather than the infrastructure.
How often should I aggregate usage data for billing? Daily rollups are enough for most SaaS billing cycles. Real-time counters (Redis or similar) are only necessary if you're enforcing hard quotas mid-request rather than just reporting usage after the fact.