The Claude Code Analytics API is GET https://api.anthropic.com/v1/organizations/usage_report/claude_code. It takes an Admin API key in x-api-key, a single UTC day in starting_at, and returns one record per user per day: sessions, lines added and removed, commits, pull requests, per-tool accept and reject counts, and token and estimated-cost figures broken down by model. Cost is in cents. Pagination is cursor-based on has_more and next_page. It is free to use, and as of August 2026 it covers Claude Code on the Claude API only.
- One endpoint:
/v1/organizations/usage_report/claude_code, one UTC day per call. - Needs an Admin API key (
sk-ant-admin01-...), not a normal key. estimated_cost.amountis in cents. 141 means $1.41.- Per-tool accepted and rejected counts give you a real quality signal.
- Two other Anthropic analytics APIs exist. Picking the wrong one wastes a day.
Which analytics API you actually want
Anthropic ships three admin-side reporting APIs with overlapping names, and they take different key types created by different roles in different consoles. Get this wrong and your key is rejected with no useful hint about why.
The three APIs, and which one answers which question. Verified August 2026.
| API | Key type | Created by | Covers |
|---|---|---|---|
| Claude Code Analytics API | Admin API key (sk-ant-admin01-...) | Org admin, in the Claude Console | Daily per-user Claude Code metrics |
| Claude Enterprise Analytics API | Analytics API key (read:analytics) | Primary owner, in claude.ai | Org-wide engagement across every Claude surface |
| Usage and Cost API | Admin API key | Org admin, in the Claude Console | API token consumption and billed cost |
Authenticating
Create the Admin API key at Claude Console, Settings, Admin keys. The Admin API is unavailable for individual accounts, so if the page is missing you need to set up an organisation first under Settings, Organization.
curl "https://api.anthropic.com/v1/organizations/usage_report/claude_code?starting_at=2026-08-06&limit=1000" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "User-Agent: acme-analytics/1.0.0 (https://acme.example.com)"
Every request parameter. There are only three.
| Parameter | Required | Notes |
|---|---|---|
starting_at | Yes | YYYY-MM-DD, UTC. Returns that single day only, not a range |
limit | No | Records per page. Default 20, maximum 1000 |
page | No | Opaque cursor from the previous response |
What comes back
One record per actor per day. The shape below is complete, not trimmed.
{
"data": [
{
"date": "2026-08-06T00:00:00Z",
"actor": {
"type": "user_actor",
"email_address": "developer@example.com"
},
"organization_id": "dc9f6c26-b22c-4831-8d01-0446bada88f1",
"customer_type": "api",
"terminal_type": "vscode",
"core_metrics": {
"num_sessions": 5,
"lines_of_code": { "added": 1543, "removed": 892 },
"commits_by_claude_code": 12,
"pull_requests_by_claude_code": 2
},
"tool_actions": {
"edit_tool": { "accepted": 45, "rejected": 5 },
"multi_edit_tool": { "accepted": 12, "rejected": 2 },
"write_tool": { "accepted": 8, "rejected": 1 },
"notebook_edit_tool": { "accepted": 3, "rejected": 0 }
},
"model_breakdown": [
{
"model": "claude-opus-5",
"tokens": {
"input": 100000, "output": 35000,
"cache_read": 10000, "cache_creation": 5000
},
"estimated_cost": { "currency": "USD", "amount": 141 }
}
]
}
],
"has_more": false,
"next_page": null
}
The fields that carry the most information, and how to read them.
| Field | Reading |
|---|---|
actor.type | user_actor carries email_address (OAuth sign-in). api_actor carries api_key_name |
customer_type | api for pay-as-you-go, subscription for Pro and Team seats |
terminal_type | Where they work: vscode, iTerm.app, tmux, and so on |
tool_actions | Accept and reject counts per edit tool. The only quality signal in the payload |
estimated_cost.amount | Cents. Divide by 100 |
model_breakdown[].tokens | input, output, cache_read, cache_creation |
A collector that does not silently lose people
Two failure modes account for almost every wrong number from this endpoint: ignoring pagination, and pulling a day that is not finished aggregating. Both are silent.
Pull yesterday, not today
Metrics appear within about an hour of a session ending, and only data older than an hour is included in a response so that pagination stays stable. A collector that runs at 00:05 UTC for the day just ended will under-report. Run it a few hours into the next day.
Follow the cursor until has_more is false
limit default 20 -> a 40-person team is 2 pages
limit max 1000 -> most orgs are 1 page
The default of 20 is the trap. A loop that reads data once and stops reports the first twenty people in your organisation and looks entirely plausible.
Store the raw records
Keep the JSON, aggregate later. Field sets on this endpoint have grown twice since launch, and re-deriving a metric from raw records you already have costs nothing.
#!/usr/bin/env bash
set -euo pipefail
DAY=$(date -u -d yesterday +%F 2>/dev/null || date -u -v-1d +%F)
BASE="https://api.anthropic.com/v1/organizations/usage_report/claude_code"
URL="$BASE?starting_at=$DAY&limit=1000"
while [ -n "$URL" ]; do
RESP=$(curl -sS --fail-with-body "$URL" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY")
printf '%s' "$RESP" | jq -c '.data[]' >> "claude-code-$DAY.jsonl"
NEXT=$(printf '%s' "$RESP" | jq -r 'if .has_more then .next_page else "" end')
if [ -n "$NEXT" ]; then
URL="$BASE?starting_at=$DAY&limit=1000&page=$NEXT"
else
URL=""
fi
done
# tool acceptance rate for the day, across the org
jq -s '[.[].tool_actions | to_entries[].value]
| (map(.accepted) | add) as $a
| (map(.rejected) | add) as $r
| { accepted: $a, rejected: $r, rate: ($a / ($a + $r)) }' \
"claude-code-$DAY.jsonl"
# spend in dollars per person, descending
jq -r '[.actor.email_address // .actor.api_key_name,
([.model_breakdown[].estimated_cost.amount] | add / 100)]
| @tsv' "claude-code-$DAY.jsonl" | sort -k2 -rn | head
What it cannot tell you
Questions this endpoint answers well, and badly.
| Question | Answer | Why |
|---|---|---|
| Who is actually using Claude Code? | Yes | One record per actor per day |
| Is adoption growing? | Yes | Pull a day at a time and trend it |
| Are people accepting the edits? | Yes | tool_actions gives accept and reject |
| What does each engineer cost? | Estimate only | estimated_cost is list-rate, not your invoice |
| Which repository is expensive? | No | There is no repo dimension anywhere in the payload |
| Which session went wrong? | No | The grain is a day, not a session |
| How close is someone to a limit? | No | Quota lives in rate-limit response headers |
| What about our Bedrock traffic? | No | Claude API only, as of August 2026 |
Using it responsibly
This endpoint attributes activity to named people by email address. That makes it the piece of AI tooling telemetry most likely to be misused, and the damage is not recoverable once done.
- Lines of code is not productivity. An agent that writes 4,000 lines where 200 were needed scores best and has made things worse.
- Low usage is not a defect. Some of the strongest engineers use agents sparingly and deliberately.
- Announce collection first. Discovering a per-person AI dashboard by accident destroys trust that took years to build.
- Report team aggregates. Leadership questions are answered at team level; individual league tables only produce compliance.
- Acceptance rate is a tool signal, not a person signal. A falling org-wide rate usually means a model or convention change, not worse engineers.
Questions people ask
An Anthropic Admin API endpoint at /v1/organizations/usage_report/claude_code that returns daily per-user Claude Code records for an organisation: sessions, lines added and removed, commits, pull requests, per-tool accept and reject counts, and token and estimated-cost figures by model.
An Admin API key beginning sk-ant-admin01-, created by an organisation admin at Claude Console, Settings, Admin keys, and passed in the x-api-key header alongside anthropic-version: 2023-06-01. Standard API keys are rejected, and the Admin API is unavailable for individual accounts.
Yes. As of August 2026 it is free to use for every organisation with access to the Admin API. Calls do not consume tokens.
No, in cents. An estimated_cost.amount of 141 means $1.41. The separate Claude Enterprise Analytics API returns amounts as decimal strings in cents, so "41280.000000" means $412.80.
No. starting_at takes a single UTC day and the response covers that day only. To build a range, loop day by day and store the raw records.
Almost always pagination. The limit parameter defaults to 20 records per page; follow next_page until has_more is false, or pass limit=1000. Data younger than about an hour is also excluded so pagination stays stable.
It reports customer_type of subscription for Pro and Team seat usage on the Claude API. On a Claude Enterprise plan, claude.ai users are reported by the separate Claude Enterprise Analytics API, which needs an Analytics API key with the read:analytics scope created by the primary owner in claude.ai.
No. The grain is day by actor, with a model breakdown. Repository attribution only exists locally, in the session transcripts, which record the working directory of every request.
Sources
Every figure above was read from these pages on August 2026. Vendors reprice without notice; if you find a stale number, tell us.