Research-backed calculator guide
Claude Usage Analytics Schema: Attribute Cost by Model, Workspace, Key and Service Tier
Build a Claude usage cost attribution schema that joins Anthropic analytics rows to workspaces, API keys, models, service tiers, owners, and finance-ready reconciliation checks.
Quick answer
A Claude usage cost attribution schema should keep the provider’s reporting grain intact before adding business ownership. Use one canonical record for each reporting row and time bucket, then store the provider dimensions separately: workspace_id, api_key_id, model, service_tier, product, and, where available, actor and actor_type. Keep the provider’s metric payload in explicit units: token counts as integers, request counts as integers, and cost amount and list_amount as decimal monetary values with a recorded currency and cost basis. Anthropic’s current Usage and Cost Analytics API returns a time interval, a group object, and either usage or cost; dimensions are null unless they are requested in group_by[], and a query can group by up to five dimensions.[1]
The practical rule is: attribute, do not overwrite. Preserve the raw provider row, map known IDs to internal owners in a separate dimension table, and retain a visible unattributed or unknown_service_account category when identity is missing. Build an ungrouped total for the same organization, date range, filters, and refresh watermark. The sum of the complete grouped ledger should reconcile to that total before you publish showback or chargeback. A schema that silently drops null actors or incomplete pagination can make the dashboard look precise while understating the bill.
This design is for usage-based Claude Platform reporting. It does not turn a subscription price into a per-request invoice, and it does not merge direct Anthropic, Amazon Bedrock, Claude Platform on AWS, or other routes into one total without a provider and billing-basis field. First decide which bill you are reconciling. Then normalize the evidence.
What the schema needs to answer
A useful schema answers four different questions without mixing their meanings:
- What did Anthropic report? Store the endpoint, organization or account scope, query filters, reporting interval, refresh timestamp, raw row, and pagination status.
- What was consumed? Keep input, cached-input, cache-creation, output, tool-use, request, and total-token metrics in their native fields. Do not infer billed tokens by adding fields that the endpoint treats separately.
- What was charged? Store
amount,list_amountwhen returned,cost_type,token_type,description, currency, and the billing basis. A list-price estimate is not the same as an invoice amount. - Who owns the work? Store provider identity and an internal ownership mapping separately. An API key can identify a workload without proving which employee owns it. A service account can belong to a team without representing a human.
The Analytics API is designed for rows that can be grouped and filtered. Its response shape uses starting_at and ending_at for the interval, group for dimensions, and usage or cost for metrics. With a time bucket, empty periods do not generate rows, so a reporting layer must fill missing buckets with zeroes only when it needs a continuous chart; it should not manufacture provider records.[1] The API also ranks rows by a metric by default, which makes pagination and total-count checks part of the ingestion contract rather than optional housekeeping.
A minimal canonical key is:
provider + organization_id + endpoint + starting_at + ending_at + group_hash + metric_kind + page_snapshot
group_hash should be a deterministic hash of the sorted provider dimension names and values, including explicit nulls. This prevents a null API key from colliding with a row where the key was never requested. metric_kind distinguishes a usage row from a cost row. page_snapshot can be the provider’s data_refreshed_at watermark plus the query fingerprint. The purpose is idempotency: rerunning a page should update the same record, not double the spend.
Field-level attribution crosswalk
Keep raw fields as received, then create normalized columns for reporting. The normalized layer should be boring and auditable. Do not rename a provider field in a way that hides whether it is an ID, a label, an estimate, or a monetary amount.
| Schema stage | Example fields | Unit or meaning | Attribution rule and check |
|---|---|---|---|
| Query evidence | endpoint, starting_at, ending_at, filters, group_by, data_refreshed_at | UTC interval and query scope | Hash the complete query. Grouped and ungrouped baselines must use matching scope and refresh evidence. |
| Provider dimensions | workspace_id, api_key_id, model, service_tier, product, actor, actor_type | IDs or enumerated labels | Preserve nulls. Null means missing or not applicable, not “shared team” by default. |
| Usage metrics | uncached_input_tokens, cache_read_input_tokens, cache_creation.*, output_tokens, total_tokens, requests | Integer tokens or requests | Store each metric separately. Check that any derived total follows the provider’s definition. |
| Cost metrics | amount, list_amount, cost_type, token_type, description | Decimal money and classifications | Store currency and cost basis. Do not treat list price as paid amount. |
| Internal ownership | owner_type, owner_id, team_id, cost_center, allocation_status | Internal IDs and controlled values | Map from provider identity or key registry. Never replace the provider row when mapping fails. |
| Lineage and control | raw_payload_uri, ingested_at, page_cursor, row_status, reconciliation_batch_id | Evidence pointers and states | A failed later page marks the batch incomplete; it does not replace the last complete batch. |
For a Claude usage analytics API schema, use a dimension table for mappings such as api_key_id -> application_id -> team_id -> cost_center. Version that mapping with effective timestamps. A key may be rotated, shared during a migration, or reassigned. Joining the latest mapping to all historical rows rewrites history and can move last month’s cost into the wrong team. The fact table should retain the provider ID and the mapping version used at ingestion.
For actor, use an identity table that distinguishes user_actor, service accounts, API keys, and null or unattributed records. Anthropic’s migration guidance says an actor-grouped export can contain an explicit null actor row, and that row belongs in the organization total.[1] Treat “unattributed” as a queue for investigation, not as zero cost. If a directory lookup fails, keep the spend with owner_id = null, allocation_status = unresolved, and the original provider identity.
If Claude traffic runs through a different commercial path, add provider_route and billing_account_id. AWS’s IAM-principal cost allocation feature, for example, exposes the caller ARN in the line_item_iam_principal column of CUR 2.0 and supports principal tags for grouping Bedrock spend.[4] Those fields can enrich an AWS-backed ledger, but they are not interchangeable with Anthropic’s actor or api_key_id. Join on a documented request or account relationship, not on a guessed email address.
Attribution workflow that does not lose cost
Start each collection with a closed UTC period. Capture a total query with no identity grouping and an attribution query grouped by the dimensions needed for reporting. Keep endpoint, dates, filters, credential scope, and refresh watermark comparable. Fetch every page. Save the raw response before transforming it.
Then apply these steps:
- Validate completeness. Require the final page to report no more rows. A first page with a high limit is not evidence of completeness.
- Normalize dimensions. Convert missing values to explicit nulls and standardize IDs without changing their original representation.
- Resolve ownership. Join keys, actors, workspaces, and service accounts to versioned internal mappings. Keep unresolved rows visible.
- Calculate attribution. For provider-reported cost rows, use the provider’s amount. For usage-only rows, apply the dated rate card only when the rate and pricing basis are known. Label the result as an estimate.
- Reconcile. Sum the grouped rows with decimal arithmetic and compare with the ungrouped total. Compare by the same cost type and currency.
- Publish status. Mark the batch
complete,complete_with_unattributed, orincomplete. A complete batch can contain unattributed spend; it cannot contain a failed page.
Anthropic’s legacy Usage and Cost API describes USD cost values as decimal strings in lowest units and says cost reporting has different coverage from usage reporting; it also warns that Priority Tier costs are not included in that legacy cost endpoint.[2] That is why currency, unit_scale, endpoint_version, and cost_type belong in the schema. Do not add a generic “dollars” column and assume every route uses the same meter.
For identity enrichment outside Anthropic, use a stable trace or request correlation field when your gateway emits one. OpenTelemetry semantic conventions exist to standardize attribute names and meanings across telemetry producers and consumers, which makes a trace or span join more defensible than parsing free-text logs.[6] Still, telemetry is evidence of activity, not automatically the billing source. Store the join confidence and keep the provider cost row authoritative for reconciliation.
Worked cost example
Assume a closed UTC day produces three provider cost rows from the same organization and refresh snapshot. These are illustrative inputs, not vendor quotes or live account results. The amounts are in USD, and the example assumes amount is the cost basis being reconciled. No tax, credits, discounts outside the provider amount, or cross-provider spend is included.
| Step | Provider row | Model / workspace / key | Provider amount | Internal mapping | Allocated output |
|---|---|---|---|---|---|
| 1 | Row A | Sonnet / ws_eng / key_build | $18.40 | Engineering / build-service | $18.40 |
| 2 | Row B | Sonnet / ws_eng / key_review | $7.10 | Engineering / review-service | $7.10 |
| 3 | Row C | Opus / ws_shared / null key | $4.50 | Unattributed shared workload | $4.50 |
| 4 | Control total | Ungrouped query, same dates and filters | $30.00 | Expected grouped sum | $30.00 |
The arithmetic is $18.40 + $7.10 + $4.50 = $30.00. The allocation rate for each row is one because the provider already reported cost at the same grain. If Row C later maps to a team, update the ownership dimension and preserve the original row, amount, mapping version, and prior unresolved status. Do not create a second cost row.
If you only have usage rows, the calculation changes. Suppose a dated rate card says a model’s uncached input rate is $3.00 per million tokens and a row contains 2,000,000 uncached input tokens. The estimated component is 2,000,000 / 1,000,000 × $3.00 = $6.00. Add separately priced output, cache-write, cache-read, batch, or tool-use components only when their rates and units are documented for the same model and date. Mark the result estimated, record the rate-card URL and effective date, and never label it as the invoice total. For rate-card comparisons, the Model pricing comparison guide and LLM cost calculator can help keep units explicit.
The calculator is useful at the reconciliation boundary: paste the provider total, internal grouped total, variance, currency, and any known adjustments into the AI API invoice reconciliation calculator. For token-only estimates, use the AI token cost calculator and preserve its assumptions beside the estimate.
Run your scenario
Turn this framework into an editable estimate.
Use dated, source-linked assumptions and see cost per request, successful task and month.
Common schema failures and limitations
Dropping null identities. A null actor, api_key_id, or workspace_id may represent unassigned, default-workspace, playground, or not-applicable usage. It is not permission to discard the row. Report the null category and investigate it separately.
Mixing usage and cost grains. Usage may be grouped by more dimensions or time buckets than cost. A token row and a cost row are not joinable merely because they share a model name. Use a documented allocation key and retain the metric kind.
Treating estimates as invoices. Local CLI estimates and analytics dashboards can be useful for operations, but the provider’s billing output is the reconciliation source. The Claude Console help page describes separate usage and cost views and CSV exports, while noting that its current console reporting does not provide individual-user cost breakdowns in that view.[3] Product surfaces change; date your observation and prefer the API contract for ingestion.
Overwriting attribution. Ownership is a business interpretation. Keep provider_actor, provider_api_key_id, and mapping_version even after assigning a cost center.
Ignoring route boundaries. Bedrock and direct Claude API records may have different IDs, meters, discounts, and invoice systems. If you combine them, require provider_route and a route-specific reconciliation control. AWS session tags can persist through role chaining only when configured as transitive, so an AWS identity join also needs a documented federation setup.[5]
Publishing partial pages. A report that omits page two can be numerically plausible. Make ingestion fail closed: stage a batch, verify pagination, reconcile, then publish.
FAQ
What is a Claude usage cost attribution schema?
It is a versioned data model that stores provider-reported Claude usage or cost rows, their time and grouping dimensions, metric units, billing basis, internal owner mappings, and reconciliation evidence. It is not just a CSV with a team column. The raw provider identity and the internal interpretation must remain distinguishable.
How do I calculate Claude cost by workspace?
Query the Usage and Cost Analytics API with group_by[]=workspace_id, keep the time range and filters fixed, fetch all pages, and sum the returned cost amounts by workspace. Preserve a null workspace as its own category. Compare that grouped sum with an ungrouped organization total before using it for chargeback.[1]
Can I allocate Claude cost by API key?
Yes, when the endpoint returns or supports api_key_id as a grouping or filter dimension. Treat the key as a workload identifier, not automatically as a person. Maintain a dated key registry that maps the key to an application, team, and cost center, and retain a visible unresolved state for keys that are shared or retired.
What should I do with an unattributed actor?
Keep it in the ledger and in the reconciliation total. Add allocation_status = unattributed or unresolved, preserve the row’s provider dimensions, and route it to an ownership workflow. Filtering it out makes the grouped report fail to explain the organization total.
Is the provider amount the same as a list-price estimate?
Not necessarily. A cost response can expose amount fields and cost classifications, while a token-only calculation uses a dated rate-card assumption. Store the amount, currency, scale, cost type, and source separately. If you calculate from tokens, label the output as an estimate and record the pricing assumptions.
Should OpenTelemetry replace the provider usage API?
No. Telemetry is useful for joining a request or workflow to a user, service, trace, or deployment. The provider usage or cost endpoint remains the billing evidence for the reconciliation batch. Use stable trace and span identifiers as enrichment, and record join confidence rather than treating every telemetry event as a charge.
Primary sources checked
Pricing and product behavior can change. These sources support the time-sensitive claims in this guide; verify current provider terms before committing spend.