Claude Analytics APIでユーザー別コストを取得する方法
Claude Enterprise Analytics APIのuser_cost_reportは席ユーザー起因のコストだけを支出順に返す。組織合計のcost_reportとの違い、必要なスコープ、group_byの使い方をまとめる。
Claude Enterprise Analytics APIには、コストを返すエンドポイントが2本あります。ユーザーごとの支出を順位付きで返す user_cost_report と、時間バケットで組織全体を返す cost_report です。名前は似ていますが、数字の母集団が違います。メンバー別の請求配賦に使うなら前者、経理の締めに使うなら後者、という分け方になります。
user_cost_reportが返すのは「席ユーザー起因のコスト」だけ
user_cost_report は GET /v1/organizations/analytics/user_cost_report で呼ぶエンドポイントです。指定した期間のコストを米ドルで集計し、ユーザー1人につき1行で、支出の多い順に返します。「誰がいちばん使っているか」を知るためのAPIです。
重要なのは母集団です。公式の説明では、含まれるのは席ユーザーに帰属するコストだけです。APIキーを直接叩いたトラフィックや自動化のコストは、この一覧に入りません。したがって、全ユーザー行の amount を足しても組織の総額にはなりません。
組織合計が必要なときは、バケット型の /v1/organizations/analytics/cost_report を使います。公式もそう案内しています。
2つのエンドポイントの違い
| 観点 | user_cost_report | cost_report |
|---|---|---|
| パス | user_cost_report/v1/organizations/analytics/user_cost_report | cost_report/v1/organizations/analytics/cost_report |
| 返し方 | user_cost_reportユーザー1人につき1行、支出順 | cost_report時間バケットごと、古い順 |
| 母集団 | user_cost_report席ユーザーに帰属するコストのみ | cost_report組織合計(APIキー直叩き・自動化を含む) |
bucket_width | user_cost_report任意。指定すると行が時間で分割される | cost_report既定は 1d |
limit の単位 | user_cost_report行数(1〜1000、既定20) | cost_report時間バケット数(1d は既定7・最大31) |
| 主な用途 | user_cost_report高額利用者の特定、メンバー別の配賦 | cost_report日次・時間別の推移、総額の把握 |
limit の意味が違う点は見落としやすい箇所です。user_cost_report では1ページあたりの行数を指し、cost_report では時間バケットの数を指します。cost_report は bucket_width ごとに既定値と上限が異なり、1h なら既定24・最大168、1m なら既定60・最大256です。
利用条件と認証
どちらのエンドポイントも、利用できるのはClaude Enterpriseプランの組織です。呼び出しには read:analytics スコープを持つAPIキーが必要で、このキーはclaude.aiのOrganization settingsにあるAPIのページで公開APIアクセスを有効にしてから、Analytics APIキーとして作成します。Claude ConsoleのAdmin APIキー(sk-ant-admin01-...)とは別物で、互換性はありません。2種類のキーの使い分けはClaude Analytics API群の使い分けに整理してあります。
プランの種類にも条件があります。コスト系のエンドポイントは使用量ベースのEnterpriseプラン向けで、シート課金型のプランでは、返る数字が利用クレジット(usage credits)の分だけになります。シート課金型の組織で合計が請求額より小さく見えても、APIの不具合とは限りません。
Claude Console側のAPI利用コストを取りたい場合は、こちらの系統ではありません。Usage and Cost APIを扱ったClaude Usage APIとCost APIで利用量とコストを取得するが別の入口です。
最小のリクエスト
必須のクエリパラメータは starting_at だけです。RFC 3339のタイムゾーン付き日時で指定し、直近365日以内、かつ2026年1月1日(UTC)以降でなければなりません。ending_at を省くと、現在時刻と starting_at に31日を足した日時のうち早いほうが使われます。範囲は最大31日です。
curl "https://api.anthropic.com/v1/organizations/analytics/user_cost_report\
?starting_at=2026-09-01T00:00:00Z\
&ending_at=2026-09-15T00:00:00Z\
&limit=100" \
--header "x-api-key: $ANALYTICS_API_KEY" \
--header "anthropic-version: 2023-06-01"anthropic-version ヘッダーは毎回必要です。応答は data の配列で、各行に actor(ユーザー情報)、amount、list_amount、currency、requests などが入ります。group_by[] も bucket_width も付けない場合、ユーザー1人分の行は次のような形です(値は例)。
{
"data": [
{
"actor": {
"type": "user_actor",
"user_id": "user_01AbCdEfGhIjKlMnOpQrSt",
"email": "jane@example.com",
"name": "Jane Smith",
"deleted": false
},
"amount": "41280.000000",
"list_amount": "51600.000000",
"currency": "USD",
"requests": 128
}
],
"data_refreshed_at": "2026-09-15T04:00:00.000Z",
"has_more": true,
"next_page": "<opaque cursor>",
"organization_id": "org_013FP9SaFPBg7Kw7fetjn6cF"
}group_by[] で分けた次元の値(model や product など)は、その行に追加のフィールドとして現れます。has_more がtrueなら、次のページが残っています。
金額は「セント単位の10進文字列」で返る
amount は割引後・クレジット適用前の金額で、単位は小数を含むセントです。たとえば "41280.000000" は412.80ドルにあたります。ドルに直すには、10進数として解釈して100で割ります。金額が数百万ドルを超える可能性がある場合は、二進の浮動小数点でパースしないよう公式が注意しています。Pythonなら Decimal を使うと安全です。
from decimal import Decimal
def cents_to_usd(value: str) -> Decimal:
# "41280.000000"(セント)-> Decimal("412.80")(ドル)
return (Decimal(value) / 100).quantize(Decimal("0.01"))
print(cents_to_usd("41280.000000")) # 412.80list_amount は割引前の定価ベースの金額です。並び順は order_by で amount と list_amount から選べ、既定は amount です。割引の効き具合を見たいときは両方を並べて比べます。
actorで見分けられるユーザーの状態
各行の actor は常に user_actor 型で、user_id、email、name、deleted を持ちます。メンバーが組織から外れた場合やIdP経由でデプロビジョニングされた場合も deleted: true になり、email は残ります。アカウント自体が削除された場合は email がnullになり、name は "Deleted User" と返ります。
退職者の分を集計から外したいときは、exclude_deleted_users=true を付けます。この場合、1ページの行数が limit を下回ることがあるので、has_more と next_page で最後までたどります。
内訳を出す: group_byと絞り込み
ユーザー単位の合計だけでは、何にコストがかかっているのか分かりません。group_by[] を付けると、ユーザーの行が次元ごとに分かれます。指定できる次元は product、model、context_window、inference_geo、speed、cost_type、token_type、rbac_group_id、slack_channel_id、claude_tag_category、claude_tag_user_id の11個です。cost_report と同じ値を受け付けます。
curl -G "https://api.anthropic.com/v1/organizations/analytics/user_cost_report" \
--data-urlencode "starting_at=2026-09-01T00:00:00Z" \
--data-urlencode "group_by[]=product" \
--data-urlencode "group_by[]=model" \
--data-urlencode "limit=200" \
--header "x-api-key: $ANALYTICS_API_KEY" \
--header "anthropic-version: 2023-06-01"リスト型のパラメータは、値ごとにパラメータを繰り返すブラケット記法で渡します。products[]=chat&products[]=claude_code のような形です。
次元によって limit への数え方が違います。product、model、context_window、inference_geo、speed と、bucket_width 指定時の時間バケットは、行数として limit に数えられます。1ユーザーが3モデルを使っていれば3行です。一方、cost_type と token_type は limit に数えられず、data の件数が limit を超えることがあります。cost_type はトークン・ウェブ検索・コード実行の構成要素ごとに1行、token_type はトークン種別ごとに1行を返します。
各値の意味は次のとおりです。
| 次元 | 値 | 意味 |
|---|---|---|
cost_type | 値tokens | 意味トークン課金の分 |
cost_type | 値web_search | 意味ウェブ検索の分 |
cost_type | 値code_execution | 意味コード実行の分 |
token_type | 値uncached_input_tokens | 意味キャッシュを使わない入力トークン |
token_type | 値cache_read_input_tokens | 意味キャッシュから読んだ入力トークン |
token_type | 値cache_creation.ephemeral_5m_input_tokens | 意味5分キャッシュの作成 |
token_type | 値cache_creation.ephemeral_1h_input_tokens | 意味1時間キャッシュの作成 |
token_type | 値output_tokens | 意味出力トークン |
token_type に値が入るのは cost_type が tokens の行だけで、それ以外はnullです。cost_type か token_type で分けた行では、requests はnullになります。リクエスト数が必要なら、分けない応答から読みます。
絞り込みには products[](chat、claude_code、cowork、claude_design、claude_in_chrome、office_agent、claude-tag)、models[]、user_ids[]、rbac_group_ids[] などが使えます。たとえばClaude Codeの利用者だけを対象にする、特定のRBACグループに絞る、といった集計ができます。ユーザー管理側のAPIでメンバーを引く方法はClaude User Management APIでメンバー管理を実装するにあります。
RBACグループ別に集計すると合計が組織総額を超える
group_by[]=rbac_group_id には癖があります。ユーザーが複数のグループに属している場合、そのユーザーの全利用量が、所属する各グループの行にそれぞれ計上されます。グループ行同士は重なるので、合計が組織総額を超えることがあります。どのグループにも属さない日のユーザーは、rbac_group_id がnullの1行にまとまります。
また、Claude Tag(SlackのClaude)の claude_tag_user_id はSlackのユーザーIDで、claude.aiのユーザーIDとは別です。rbac_group_id の次元や rbac_group_ids[] フィルターとは併用できません。特定のユーザーに帰属しないClaude Tagの利用(監視や、Claudeが自発的に応答した分)は、ユーザー別の行に入らないため、ユーザー行の合計がClaude Tag全体より小さくなることがあります。
ページ送りとデータの鮮度
has_more がtrueのあいだは、next_page の値を page パラメータに渡して次のページを取得します。カーソルはそれを発行したクエリに結び付いています。products[]、group_by[]、order_by、期間、フィルターのどれかを途中で変えて古いカーソルを渡すと、400エラーになります。条件を変えたいときは、カーソルなしの最初のページからやり直します。データ更新の後にカーソルが失効することもあり、その場合はHTTP 410が返るので、同じく先頭から取り直します。
最後までたどる最小のループは、次のようになります。410を受けたら先頭からやり直す点だけ押さえてあります(やり直しは3回までで打ち切ります)。
import os
from decimal import Decimal
import requests
URL = "https://api.anthropic.com/v1/organizations/analytics/user_cost_report"
HEADERS = {
"x-api-key": os.environ["ANALYTICS_API_KEY"],
"anthropic-version": "2023-06-01",
}
PARAMS = {"starting_at": "2026-09-01T00:00:00Z", "limit": 1000}
def fetch_all():
rows, page, restarts = [], None, 0
while True:
params = dict(PARAMS, **({"page": page} if page else {}))
res = requests.get(URL, headers=HEADERS, params=params)
if res.status_code == 410: # カーソル失効: 先頭から
restarts += 1
if restarts > 3:
res.raise_for_status()
rows, page = [], None
continue
res.raise_for_status()
body = res.json()
rows += body["data"]
if not body["has_more"]:
return rows
page = body["next_page"]
total_cents = sum(Decimal(r["amount"]) for r in fetch_all())鮮度には3つの注意点があります。
- データは通常4時間ごとに更新され、遅いと24時間かかることがある
- 同じ日付の値でも、遅れて届くイベントや調整のため、最大30日は値が改訂されることがある
- 請求と突き合わせる用途では、30日以上前の日付を対象にする
応答の data_refreshed_at は、そのレスポンスが参照したエクスポートの時刻です。ending_at を省くと、この時刻より後の未完了データが末尾に含まれます。同じ条件で繰り返し取得して結果を安定させたいときは、ending_at に前回返った data_refreshed_at 以前の時刻を指定します。該当期間をカバーするエクスポートがまだなければ、data_refreshed_at はnullで data も空です。
レート制限はAPIキー単位ではなく組織単位で、既定ではAnalytics API全体で毎分60リクエストです。ユーザー別に何度も内訳を取り直す実装では、この上限に当たりやすくなります。
時間で分割する: bucket_width
user_cost_report にも bucket_width(1d、1h、1m)があり、指定すると各行に starting_at と ending_at が入り、1ユーザーが時間バケットごとの複数行に分かれます。このとき ending_at は必須です。1m を選ぶと、範囲は最大24時間に制限されます。省略した場合は、期間全体を1行に集約します。
推移をユーザー別に見たいだけなら、この指定で足ります。ユーザーを分けずに日次の推移と総額が欲しいなら、cost_report のほうが素直です。
使い分けの早見表
| やりたいこと | 使うエンドポイント |
|---|---|
| 今月いちばん使っているユーザーの上位を出す | 使うエンドポイントuser_cost_report |
| ユーザーごとに、どの製品・モデルで使ったかを見る | 使うエンドポイントuser_cost_report + group_by[] |
| 組織全体の日次コストの推移を見る | 使うエンドポイントcost_report |
| APIキー直叩きや自動化を含めた組織総額を出す | 使うエンドポイントcost_report |
| 支出上限の増額を検討するメンバーを探す | 使うエンドポイントuser_cost_report(運用はClaude支出上限APIの記事) |
ユーザー別の合計と組織の総額が一致しないのは、仕様どおりの動作です。差分はAPIキー直叩きや自動化などの、席ユーザーに帰属しないコストに当たります。突き合わせるなら、cost_report の総額から user_cost_report の合計を引いた差が、その帰属先のコストの目安になります。
つまずきやすい点
Claude Console側の使用量・コストを画面で確認したいなら、APIを組む前にClaude Consoleの使用量・コストレポートの見方で足りる場合があります。Enterprise組織のコスト取得でこのAPIを選ぶのは、メンバー単位・グループ単位の集計が必要なときです。
- シート課金型プランでは利用クレジットの分しか反映されない。請求額との差はプランの種類から疑う
- Claude CodeをAmazon Bedrock経由で使っている場合、そのClaude Codeの利用はAnalytics APIに返らない
amountはセント単位の文字列で、浮動小数点でパースしない- カーソルを持ったまま
group_by[]や期間を変えると400になる deleted: trueの行には退職者が含まれるので、配賦の対象から外すかどうかを先に決める
まとめ
メンバー別の請求配賦や高額利用者の特定には user_cost_report、日次の推移やAPIキー直叩きを含む組織総額には cost_report を使います。前者の合計が後者の総額に届かないのは仕様で、差分は席ユーザーに帰属しないコストです。実装では、amount を Decimal で扱うことと、カーソルの失効(410)で先頭からやり直すことの2点を先に入れておくと、集計が崩れにくくなります。