Blog
3 November 2026/20 min read

Claude Usage and Cost Analytics API: track spend per team, API key and user

The Claude Usage and Cost Analytics API is Anthropic's beta Admin API for reporting token usage and cost across your organization, split by model, workspace, API key, service tier and the user or service account behind each request. It has two endpoints, `GET /v1/organizat…

Boulanouar Walid
Author:Boulanouar Walid,Founder & CEO
Claude Usage and Cost Analytics API: track spend per team, API key and user

Book a Free Strategy Call

Skip the read: talk to Walid in 30 min.

Free strategy call. We map your AI engineering team, you keep the notes.

The Claude Usage and Cost Analytics API is Anthropic's beta Admin API for reporting token usage and cost across your organization, split by model, workspace, API key, service tier and the user or service account behind each request. It has two endpoints, GET /v1/organizations/analytics/usage and GET /v1/organizations/analytics/cost, and every call needs the anthropic-beta: analytics-usage-cost-2026-08-24 header.

Anthropic launched it on October 29, 2026. The same release note made the older Usage and Cost API a legacy API, along with the usage and cost report endpoints of the Claude Enterprise Analytics API. All of them remain supported.

If you run Claude in production or across an engineering team, this is the API that answers "which team, key or person is spending our Claude budget?" This guide gives you working queries for each of those questions, a daily cost-per-team report you can schedule, and the limits that will trip you up the first time. Every endpoint, parameter and limit below is quoted from Anthropic's documentation, fetched on November 2, 2026.

What is the Claude Usage and Cost Analytics API?

It reports historical usage, not live usage. For Claude Console organizations, Anthropic says the data "is similar to what the Usage and Cost pages of the Claude Console show", but you can query it, group it and pipe it into your own dashboards and finance reports.

EndpointWhat it returnsorder_by options
GET /v1/organizations/analytics/usageToken counts (uncached input, cache writes, cache reads, output, total), server tool use and request countstotal_tokens (default), output_tokens, uncached_input_tokens, requests, time
GET /v1/organizations/analytics/costCost in USD: amount (after discounts, before credits), list_amount (at list prices), requestsamount (default), list_amount, time

Every row has the same three parts: starting_at and ending_at for the period, a group object holding the dimensions, and a usage or cost object holding the metrics. A dimension is null unless you list it in group_by[], and you can group by up to five dimensions at once.

The dimensions that matter most for spend attribution:

DimensionWhat it tells youApplies to
modelWhich Claude model served the requestAll organizations
workspace_idThe Console workspaceClaude Console only
api_key_idThe key that authenticated the requestClaude Console only
actor, actor_typeThe user, service account, federated cloud identity or API key the request is attributed toAll organizations
productThe surface, such as claude_code, chat or coworkValues listed in the API reference
rbac_group_idThe team a user belongs toOrganizations with teams
service_tier, context_window, inference_geo, speedHow the request was servedservice_tier is Console only

Who can use it, and which key do you need?

You need an organization. The docs are blunt about it: "The Admin API is unavailable for individual accounts." If you're on a personal Console account, set up an organization first in the Console's Organization settings.

Organization typeCredential
Claude ConsoleAn Admin API key, or another Admin API credential such as an OAuth token with the org:admin scope
Claude EnterpriseA key with the read:analytics scope, created in claude.ai by the primary owner once the Analytics API is enabled
Claude Platform on AWSAWS IAM, through the ListAccountUsage and ListAccountCost actions; call each AWS region's endpoint to cover the whole organization
Claude Models on Google Cloud (Anthropic managed)Google Cloud credentials with usageAnalytics.list (usage) and costAnalytics.list (cost)

Anthropic also recommends giving the reporting service its own Admin key, storing it in a secrets manager and rotating it, or better, using a Workload Identity Federation rule with the org:admin scope so there's no long-lived key to leak. On a Claude Console organization, an Admin API key works across the whole Admin API, not just reporting, so treat a cost dashboard's key like a production credential, not a read-only token.

Watch out: the API key your app uses to call Claude won't work here. Console organizations need an Admin credential, and Claude Enterprise needs a read:analytics key. If the job that pulls cost data runs in CI, limit who can read that secret.

How do you get Claude spend per API key?

Group the cost endpoint by api_key_id. This is the fastest way to find the service or integration that is burning budget, because most production systems already use one key per service.

curl --globoff "https://api.anthropic.com/v1/organizations/analytics/cost?\
starting_at=2026-10-01T00:00:00Z&\
ending_at=2026-11-01T00:00:00Z&\
group_by[]=api_key_id&\
group_by[]=model&\
limit=50" \
  -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 amount, largest first, so the first rows are your most expensive key and model combinations for the month. To turn an apikey_... ID into a name, call the List API Keys endpoint or look it up in the Console.

Two details matter here:

  • Requests with no key have api_key_id: null. That covers usage from the Claude Console, from Claude Code after a sign-in without an API key, and from Workload Identity Federation tokens. To include that usage in an api_key_ids[] filter, add an empty entry: api_key_ids[]=.
  • A key filter and an actor filter answer different questions. api_key_ids[]=apikey_... matches every request the key authenticated, whoever it's attributed to. actor_ids[]=apikey_... matches only requests attributed to the key itself.

Free weekly brief

Steal our production automations

The exact n8n flows, Claude Code setups, and prompts we ship for clients, broken down step by step. No spam, unsubscribe anytime.

How do you get Claude spend per user or service account?

Group by actor. Each row's actor is an object whose type tells you what it is: user_actor, service_account_actor, federated_actor or api_actor.

curl --globoff "https://api.anthropic.com/v1/organizations/analytics/cost?\
starting_at=2026-10-01T00:00:00Z&\
ending_at=2026-11-01T00:00:00Z&\
group_by[]=actor&\
actor_types[]=user_actor&\
limit=100" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: analytics-usage-cost-2026-08-24" \
  -H "x-api-key: $ANTHROPIC_ADMIN_KEY"

How attribution works, per the docs:

Credentialactor showsapi_key_id shows
Personal key or service account keyThe linked user or service accountThe key
Legacy workspace keyThe key itselfThe key
Key Claude Code creates for a member at sign-inThat memberThe key (it lives in the Claude Code workspace)
Claude Code sign-in without an API keyThe member who signed innull
Workload Identity Federation tokenThe federated workload's service accountnull

Usage that can't be tied to any identity comes back with actor: null.

If most of your spend lands on api_actor rows, your organization still runs on shared legacy workspace keys and you can't see people behind them. Moving services to service account keys and people to personal keys is the fix, and it's worth doing before you build a dashboard on top.

Per-user Claude Code cost

This is the question most engineering leads start with. Anthropic's FAQ gives the query directly: call the cost endpoint with products[]=claude_code&group_by[]=actor.

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&\
limit=100" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: analytics-usage-cost-2026-08-24" \
  -H "x-api-key: $ANTHROPIC_ADMIN_KEY"

Claude Console organizations can also use the separate Claude Code Analytics API, which reports per-user Claude Code activity alongside estimated cost. If your developers keep hitting limits rather than budgets, our breakdown of Claude Code rate limits covers that side.

How do you track Claude spend per team?

It depends on your organization type, and the two approaches don't add up the same way.

Claude Console organizations: one workspace per team. Group by workspace_id. If each team or product line has its own workspace, workspace cost is team cost. The default workspace has its own real wrkspc_ ID in this API (the legacy API returned null for it). List Workspaces leaves it out unless you pass include_default=true, and Get Workspace returns "name": "Default" for it.

Organizations with teams: rbac_group_id. Group by rbac_group_id to get one row per team. Read the fine print before you send this to finance: a user in several groups "contributes their full usage to each of those groups' rows", so the team rows overlap and their sum can exceed the organization total. A null row holds users in no group that day. For the true total, run the same query without group_by[]. If teams aren't available to your organization, this dimension returns an error instead of empty rows.

Team modelGroup byRows add up to the org total?
One Console workspace per teamworkspace_idYes, each request belongs to one workspace (Console only)
One API key per serviceapi_key_idYes, plus a null row for keyless usage (Console only)
People grouped into teamsrbac_group_idNo, a user in two teams counts in both

Tip: if you're setting up workspaces from scratch, name them after the cost center finance already uses. The API returns IDs, not names, and a lookup table that maps wrkspc_ IDs to cost centers is the piece every dashboard ends up needing.

How do you read the cost numbers?

Amounts are decimal strings in cents with six decimal places. Anthropic's example: "41280.000000" is $412.80 USD. Divide by 100 before you show anyone a dollar figure. Currency is always "USD".

The cost endpoint has two extra dimensions that don't count toward the five-dimension limit:

  • cost_type splits each row into tokens, web_search, code_execution and session_usage (Claude Managed Agents session time).
  • token_type splits token cost into uncached input, 5-minute cache writes, 1-hour cache writes, cache reads and output.

Grouping by model, cost_type and token_type together is the quickest way to see whether prompt caching is paying off: if cache_read_input_tokens cost is small next to uncached_input_tokens on a workload that repeats the same long system prompt, caching is probably not set up or not hitting. The Claude pricing breakdown has the per-token rates for each type if you want to sanity-check the totals. With either of these two dimensions in the request, requests is null on every row, so read request counts from a separate ungrouped call.

One warning from the docs on priority capacity: requests served under a Priority Tier commitment appear on the usage endpoint with service_tier: "priority" but add zero token cost, because the commitment covers them. The commitment fee is billed separately and isn't in this data. If you have a commitment, this data leaves out the commitment fee, so add it yourself when you reconcile against the invoice.

A daily cost-per-team report you can schedule

Here is a small Python script that pulls the last N days of cost grouped by workspace, follows pagination, fills empty days with zeros, prints a CSV and flags any workspace that went over a daily budget. Days that data_refreshed_at hasn't fully covered yet are marked partial and skipped for alerts, so a half-loaded day can't hide an overspend or trigger a false all-clear. It uses plain HTTP so you can read every parameter against the docs.

import collections, datetime as dt, os, sys
import requests

API = "https://api.anthropic.com/v1/organizations/analytics/cost"
HEADERS = {
    "anthropic-version": "2023-06-01",
    "anthropic-beta": "analytics-usage-cost-2026-08-24",
    "x-api-key": os.environ["ANTHROPIC_ADMIN_KEY"],
}

def fetch_rows(starting_at, ending_at):
    params = {
        "starting_at": starting_at, "ending_at": ending_at,
        "time_bucket_width": "1d", "group_by[]": ["workspace_id"],
        "order_by": "time", "order": "asc", "limit": 1000,
    }
    rows, page, restarts = [], None, 0
    while True:
        query = dict(params, page=page) if page else params
        resp = requests.get(API, headers=HEADERS, params=query, timeout=60)
        if resp.status_code == 410 and restarts < 2:  # cursor expired after a data refresh
            rows, page, restarts = [], None, restarts + 1
            continue
        resp.raise_for_status()
        body = resp.json()
        rows.extend(body["data"])
        if not body.get("has_more"):
            return rows, body.get("data_refreshed_at")
        page = body["next_page"]

DAYS, BUDGET_USD = 7, 150.0
today = dt.datetime.now(dt.timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
start = today - dt.timedelta(days=DAYS)
rows, refreshed = fetch_rows(start.strftime("%Y-%m-%dT%H:%M:%SZ"), today.strftime("%Y-%m-%dT%H:%M:%SZ"))

spend = collections.defaultdict(lambda: collections.defaultdict(float))
for row in rows:
    team = row["group"]["workspace_id"] or "(no workspace)"
    spend[team][row["starting_at"][:10]] += float(row["cost"]["amount"]) / 100  # cents to USD

days = [(start + dt.timedelta(days=i)).strftime("%Y-%m-%d") for i in range(DAYS)]
# A day whose bucket ends after data_refreshed_at is still filling in: report it, don't alert on it.
cutoff = dt.datetime.fromisoformat(refreshed.replace("Z", "+00:00")) if refreshed else None
partial = {d for d in days if cutoff is None
           or dt.datetime.fromisoformat(d + "T00:00:00+00:00") + dt.timedelta(days=1) > cutoff}
print(f"data_refreshed_at: {refreshed}, partial days (no alerts): {sorted(partial)}")
print("workspace," + ",".join(days) + ",total_usd")
for team, by_day in sorted(spend.items(), key=lambda kv: -sum(kv[1].values())):
    values = [by_day.get(d, 0.0) for d in days]  # days with no cost have no row
    print(team + "," + ",".join(f"{v:.2f}" for v in values) + f",{sum(values):.2f}")
    for d, v in zip(days, values):
        if d not in partial and v > BUDGET_USD:
            print(f"ALERT {team} spent ${v:.2f} on {d}", file=sys.stderr)

How to run it:

  1. Create an Admin API key for this job only, store it as ANTHROPIC_ADMIN_KEY in your secrets manager, and set an expiry.
  2. Run it once a day from cron, a GitHub Action or your scheduler of choice. Because data can take up to 24 hours to land, yesterday is often still partial in the morning run; the alert for it fires on the next day's run instead.
  3. Send the stderr ALERT lines to Slack or your on-call tool, and load the CSV into the sheet or BI tool finance already reads.
  4. Add a second job on the 8th of each month that pulls the previous month in full. That's the number to reconcile against the invoice (see the freshness section below).

We wrote this against Anthropic's docs as fetched on November 2, 2026, and tested the paging, the 410 restart, the cents-to-dollars conversion, the zero-filling and the partial-day check against mocked responses. We haven't run it against a live organization's data, so run it once by hand and compare with the Console's Cost page before you wire alerts to it.

Bottom line: this API is for attribution and trend alerts, not hard spend caps. Data typically lands within 4 hours and can take up to 24, so a runaway agent loop can burn a day of budget before any report sees it. Put per-request token limits and per-feature budgets in your application for that.

Limits and gotchas

These are the rules from the docs that most often break a first integration:

RuleWhat it means for you
starting_at must be on or after January 1, 2026, and within the last 365 daysNo history before 2026 from this API. Claude Console organizations can still use the legacy Usage and Cost API for older data.
One request spans at most 31 daysSplit a quarter into monthly requests.
ending_at is required when time_bucket_width is setDaily and hourly queries need both ends of the range.
1m buckets span at most 24 hours per requestMinute-level data is for incident forensics, not trends.
limit is 1 to 1,000 rows per page, default 20Set limit=1000 for reports, or you'll page through many small pages.
Buckets with no usage produce no rowsFill missing days with zeros before you chart.
An unknown or misspelled ID or model name matches nothingA typo in models[] returns an empty result, not an error. Check empty results by hand.
A cursor can expire after a data refresh (HTTP 410)Restart pagination from the first page.
Data refreshes typically within 4 hours, up to 24Use data_refreshed_at to tell real zeros from late data.
Values can change until about 7 days after the month endsOctober totals settle around November 7. Close the books after that.

Filters take up to 100 values each. Values for the same filter are combined with OR, and different filters with AND, so models[]=claude-opus-5-5&workspace_ids[]=wrkspc_... means "Opus 5.5 in that workspace".

How is it different from the legacy Usage and Cost API?

The legacy endpoints were /v1/organizations/usage_report/messages and /v1/organizations/cost_report. Your Admin key, the anthropic-version header, the bucket widths and most field names carry over. The changes that matter:

Legacy Usage and Cost APIUsage and Cost Analytics API
Beta headerNoneanalytics-usage-cost-2026-08-24
FreshnessTypically within 5 minutesTypically within 4 hours, up to 24
Response shapeTime buckets, oldest first, including empty onesRows ranked by order_by, no rows for empty combinations
Time bucketsbucket_width, default 1dtime_bucket_width, no default (omit it for whole-range totals)
Per-user groupinggroup_by[]=account_id / service_account_idgroup_by[]=actor or actor_type
Default workspaceworkspace_id: nullIts real wrkspc_ ID
Unknown ID in a filterErrorMatches nothing
Cost amounte.g. "123.45"Six decimals, e.g. "123.450000", plus list_amount
Code executionNot in the usage reportUsage rows with model: null, plus requests counts
Date rangeAny rangeFrom January 1, 2026, last 365 days, 31 days per request

The freshness row is the one to think about. If you built near-real-time spend alerts on the legacy API's 5-minute lag, moving them to the new API makes them hours slower. The legacy API is still supported, so on a Claude Console organization it's reasonable to keep a fast legacy check for alerts and use the new API for attribution by user, team and product.

To reproduce a legacy daily report, request time_bucket_width=1d with the same group_by[], add order_by=time&order=asc, page through with limit=1000, then group rows by starting_at and fill missing days with zeros.

Where this fits in a production Claude setup

Cost attribution is usually the second thing teams ask for, right after the first surprising invoice. The API tells you where the money went. It doesn't stop a misbehaving agent, cap a feature's spend, or tell you whether the output was worth it. Those live in your application: token budgets per request, per-feature cost caps, model routing, and caching.

That's the part we build when we take AI agent development projects to production. And the Admin key behind your cost dashboard is exactly the kind of secret that leaks into repos and agent configs; finding those is part of a Claude Code security audit.

FAQ

Who can use the Claude Usage and Cost Analytics API?

Claude Console and Claude Enterprise organizations can, and so can organizations on Claude Platform on AWS or Claude Models on Google Cloud (Anthropic managed), using cloud IAM credentials. Individual accounts can't, because the Admin API is unavailable for them. Console organizations call it with an Admin API key or an org:admin OAuth token, and Enterprise organizations use a key with the read:analytics scope.

What beta header does the Usage and Cost Analytics API need?

Send anthropic-beta: analytics-usage-cost-2026-08-24 and an anthropic-version header such as 2023-06-01 on every request. Anthropic's SDKs and the ant CLI add both headers for you.

Can I see Claude Code cost per developer?

Yes. Call the cost endpoint with products[]=claude_code&group_by[]=actor, which returns one row per user or identity. Claude Console organizations can also use the Claude Code Analytics API for per-user activity and estimated cost. If you're rolling Claude Code out to a team, our Claude Code for teams guide covers the setup side.

How current is the data?

It typically refreshes within 4 hours of the usage and can take up to 24 hours. Every response carries a data_refreshed_at timestamp, and usage after it is missing or partial. Values can still be revised until about 7 days after the end of the month.

How far back can I query?

From January 1, 2026, and only within the last 365 days, with at most 31 days per request. Claude Console organizations can still pull earlier history from the legacy Usage and Cost API; the legacy Claude Enterprise reports share the same January 1, 2026 floor and 365-day lookback.

Why don't my per-team totals match the organization total?

If you group by rbac_group_id, a user who belongs to several teams counts in full toward each of them, so team rows can add up to more than the organization total. Run the same query without group_by[] for the true total, or group by workspace_id if each team has its own workspace.

Does the cost endpoint include my Priority Tier commitment?

No. Requests covered by a Priority Tier commitment show up as usage with service_tier: "priority" but add zero token cost, and the commitment fee is billed separately. Add that fee yourself when you reconcile against the invoice.

Should I move off the legacy Usage and Cost API now?

Move attribution reports now. On a Claude Console organization, keep anything that depends on the legacy API's 5-minute freshness where it is for the moment. The new API is still in beta, and the legacy endpoints remain supported. Our team helps with this kind of migration as part of Claude Code development work.


Sources: Usage and Cost Analytics API docs, Cost endpoint API reference, Claude Platform release notes, October 29, 2026, Usage and Cost API (legacy), Claude Code Analytics API. All fetched November 2, 2026.

Book a Free Strategy Call

Building this in production?

Walid runs a 30-min call to map your AI engineering team. Free, no slides.

Free weekly brief

Steal our production automations

The exact n8n flows, Claude Code setups, and prompts we ship for clients, broken down step by step. No spam, unsubscribe anytime.

Share this article
About the Author
Boulanouar Walid
Boulanouar Walid
Founder & CEO

Walid founded AY Automate to help businesses ship AI workflows that actually move revenue. He leads strategy and oversees every client engagement end-to-end.

Full Bio →