The Usage and Cost Analytics API: one query for who spent what, on Console and Enterprise
In brief
On October 29, 2026 Anthropic released the Usage and Cost Analytics API in beta. Two endpoints report tokens and dollars for Claude Console and Claude Enterprise organizations, grouped by model, workspace, API key, or the person behind each request. The old usage and cost endpoints are now legacy. What changed, working queries, and the freshness rule that will trip up your dashboards.
Contents
On October 29, 2026 Anthropic released the Usage and Cost Analytics API in beta. It is two read-only endpoints:
GET /v1/organizations/analytics/usagereturns token counts and request counts.GET /v1/organizations/analytics/costreturns cost in US dollars.
The same endpoints, parameters, and response fields work for a Claude Console (API) organization and a Claude Enterprise (claude.ai) organization. Before this, those two kinds of organization had separate reporting APIs with different shapes. Both older APIs, the Console Usage and Cost API (usage_report/messages and cost_report) and the Enterprise Analytics API's four report endpoints, are now marked legacy. They still work.
If you run a cost dashboard, a chargeback spreadsheet, or a finance export off either old API, this is the one you will be migrating to.
What it answers that the old endpoints did not
The old Console endpoints split usage by API key, workspace, and model. The new one adds an actor dimension: the user, service account, or cloud identity behind a request. That matters because most organizations now issue personal keys and service account keys rather than one shared key per workspace, and Claude Code users often sign in without an API key at all. With group_by[]=actor, "which person spent the most on Claude Code last month" is a single request.
It also covers dimensions that did not exist when the old API was designed: Managed Agents (agent_id, session_id, deployment_id), user profiles for platforms that resell API access (user_profile_id), fast mode (speed), data residency (inference_geo), and, for Enterprise, Claude Tag in Slack (slack_channel_id).
Authentication
- Claude Console organizations: an Admin API key (
sk-ant-admin01-...) or an OAuth token with theorg:adminscope. Workspace keys do not work. - Claude Enterprise organizations: an Analytics API key with the
read:analyticsscope, created in claude.ai by the primary owner. - Claude Models on Google Cloud (Anthropic managed): Google Cloud credentials with the
usageAnalytics.listandcostAnalytics.listIAM permissions.
Every request also needs the beta header anthropic-beta: analytics-usage-cost-2026-08-24. The official SDKs send it for you.
A first query
Daily token usage for one week, one row per model per day:
curl --globoff "https://api.anthropic.com/v1/organizations/analytics/usage?\
starting_at=2026-10-01T00:00:00Z&\
ending_at=2026-10-08T00:00:00Z&\
time_bucket_width=1d&\
group_by[]=model" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: analytics-usage-cost-2026-08-24" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
The same thing in Python. The SDK pages through results for you as you loop:
import anthropic
client = anthropic.Anthropic(api_key=ADMIN_KEY)
rows = client.beta.organization.analytics.usage.list(
starting_at="2026-10-01T00:00:00Z",
ending_at="2026-10-08T00:00:00Z",
time_bucket_width="1d",
group_by=["model"],
)
for row in rows:
print(row.starting_at, row.group.model, row.usage.total_tokens)
Each row has three parts: starting_at and ending_at for the period it covers, a group object holding every dimension, and a usage object (or cost on the cost endpoint) holding the numbers. A dimension you did not group by comes back as null. A usage row looks like this, trimmed:
{
"starting_at": "2026-10-03T00:00:00Z",
"ending_at": "2026-10-04T00:00:00Z",
"group": { "model": "claude-sonnet-5-5", "workspace_id": null, "actor": null },
"usage": {
"uncached_input_tokens": 1284500,
"cache_creation": { "ephemeral_1h_input_tokens": 0, "ephemeral_5m_input_tokens": 215300 },
"cache_read_input_tokens": 3200000,
"output_tokens": 891000,
"total_tokens": 5590800,
"requests": 4210
}
}
Three queries worth saving
Per-person Claude Code cost for the month. This is the question most admins have been answering by hand:
curl --globoff "https://api.anthropic.com/v1/organizations/analytics/cost?\
starting_at=2026-10-01T00:00:00Z&\
ending_at=2026-11-01T00:00:00Z&\
products[]=claude_code&\
group_by[]=actor" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: analytics-usage-cost-2026-08-24" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
Rows come back ranked by cost, largest first, so the first page is your top spenders.
Where the money went, by model and line item. On the cost endpoint, cost_type splits tokens from web search, code execution, and Managed Agents session time, and token_type splits input, output, and cache reads and writes:
rows = client.beta.organization.analytics.cost.list(
starting_at="2026-10-01T00:00:00Z",
ending_at="2026-11-01T00:00:00Z",
group_by=["model", "cost_type", "token_type"],
)
for row in rows:
print(row.group.model, row.group.cost_type, row.group.token_type, row.cost.amount)
If cache writes show up as a large line for a model and cache reads do not, your prompt caching is creating entries that nothing reuses. Claude cost optimization covers what to change.
A daily time series for a dashboard. Set time_bucket_width=1d and order_by=time&order=asc. Rows then come back day by day, oldest first. Days with no usage produce no row, so fill the gaps with zeros on your side.
Reading the cost numbers
Each cost row has amount (after discounts, before credits), list_amount (at list price), currency (always "USD"), and requests. Amounts are decimal strings in cents, with six decimal places: "41280.000000" is $412.80. Divide by 100 before you put it in a spreadsheet, or your finance partner will see a bill a hundred times too big.
The gap between list_amount and amount is your discount. If you have a negotiated rate, that gap is the number to show when someone asks whether the contract is paying off.
The freshness rule
The old Console endpoints showed usage within about 5 minutes. The new API refreshes within about 4 hours, and up to 24 hours. Every response carries a data_refreshed_at timestamp; anything after it is missing or partial.
Numbers can also change after the fact. Values keep being revised until about 7 days after the end of the calendar month, so October figures can move until roughly November 7. Two practical consequences:
- Do not build a real-time spend alarm on this API. For a same-day alert, the OpenTelemetry export or spend limits in the admin console are the right instruments.
- For a month-end report that finance will reconcile against an invoice, run it after the 7th of the following month.
Limits to plan around
starting_atmust be on or after January 1, 2026, and within the last 365 days.- One request covers at most 31 days. With
time_bucket_width=1m, at most 24 hours. group_by[]takes up to five dimensions.cost_typeandtoken_typedo not count toward the five.- Filters take up to 100 values each. An unknown ID or a misspelled model name matches nothing rather than returning an error, so a typo shows up as an empty report.
Migrating from the legacy endpoints
Your Admin API key, starting_at/ending_at, the bucket values (1m, 1h, 1d), and the token and cost field names carry over. The changes that break code:
| Legacy | New |
|---|---|
bucket_width, defaults to 1d |
time_bucket_width, no default. Omit it for whole-range totals |
data is a list of time buckets, each with a results list |
data is a flat list of rows ranked by order_by |
| Empty buckets included | Combinations with no usage have no row |
limit counts buckets |
limit counts rows (1 to 1,000, default 20) |
account_ids[], group_by[]=account_id |
actor_ids[], group_by[]=actor |
group_by[]=description on cost |
Group by model, cost_type, token_type and other structured dimensions |
Default workspace is workspace_id: null |
Default workspace has its real wrkspc_ ID |
Cost amount like "123.45" |
"123.450000", plus list_amount |
To rebuild the old daily report exactly, request time_bucket_width=1d with the same group_by[], page through with limit=1000, group rows by starting_at, and fill missing days with zeros.
Enterprise organizations moving off usage_report and cost_report gain one thing in particular: the old endpoints returned only the top 100 groups in each bucket. The new ones return every combination.
Who should switch now
If you are writing a new integration, start here. If a working legacy integration feeds a dashboard that needs data within minutes, keep it until you have a different source for the real-time view. If you report spend by person, by agent, or across both Console and Enterprise, switch now: the legacy endpoints cannot answer those questions.
For what to do with the numbers once you have them, Claude Enterprise cost controls covers which instrument answers which question, and agent operator cost control covers budgets for agents specifically.