◈AI Codex
Claude APIupdate

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.

8 min read·Cost Optimization

Contents

♡Sign in to save

On October 29, 2026 Anthropic released the Usage and Cost Analytics API in beta. It is two read-only endpoints:

  • GET /v1/organizations/analytics/usage returns token counts and request counts.
  • GET /v1/organizations/analytics/cost returns 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 the org:admin scope. Workspace keys do not work.
  • Claude Enterprise organizations: an Analytics API key with the read:analytics scope, created in claude.ai by the primary owner.
  • Claude Models on Google Cloud (Anthropic managed): Google Cloud credentials with the usageAnalytics.list and costAnalytics.list IAM 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_at must 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_type and token_type do 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.

Related tools

Weekly brief

For people actually using Claude at work.

Each week: one thing Claude can do in your work that most people haven't figured out yet — plus the failure modes to avoid. No tutorials. No hype.

No spam. Unsubscribe anytime.

What to read next

All articles →