Claude Media
Claudeのuser_usage_reportでユーザー別トークン使用量を取得する

Claudeのuser_usage_reportでユーザー別トークン使用量を取得する

Enterprise Analytics APIのuser_usage_reportは、席ユーザー起因のトークン数を順位付きで返す。usage_reportとの違いとorder_by、group_byの使い方を示す。

Claude Enterprise Analytics APIの user_usage_report は、期間内のトークン使用量をユーザー1人につき1行で返し、多い順に並べるエンドポイントです。「誰がいちばんトークンを使っているか」を調べるために作られています。ただし数字の母集団は席ユーザーだけで、組織全体の使用量ではありません。この点を押さえないと、usage_report の合計と食い違って戸惑います。

user_usage_reportは席ユーザー起因のトークンだけを返す

呼び出し先は GET /v1/organizations/analytics/user_usage_report です。公式の説明では、含まれるのは席ユーザーに帰属する使用量だけです。APIキーを直接叩いたトラフィックや自動化の分は入りません。

したがって、全ユーザー行の total_tokens を足しても組織の総量にはなりません。組織全体の合計が必要なときは、バケット型の /v1/organizations/analytics/usage_report を使います。

金額を知りたいなら、同じ構造のuser_cost_reportでユーザー別コストを取る方法が別にあります。トークン数と金額は一致しません。モデルやキャッシュの種類で単価が違うためです。トークンの多さで順位を見たいときはこちら、請求への配賦ならコスト側、と使い分けます。

usage_reportとの違い

観点user_usage_reportusage_report
返し方user_usage_reportユーザー1人につき1行、order_by の指標で順位付けusage_report分・時・日のバケットごと、古い順
母集団user_usage_report席ユーザーに帰属する使用量のみusage_report組織全体(APIキー直叩き・自動化を含む)
bucket_widthuser_usage_report任意。指定すると行が時間で分割されるusage_report既定は 1d
limit の単位user_usage_report行数(1〜1000、既定20)usage_report時間バケット数
主な用途user_usage_report利用の多いユーザーの特定usage_report推移と総量の把握

limit の数え方は注意が必要です。user_usage_report では行数ですが、bucket_width や group_by[] を付けると1人が複数行に分かれます。この場合、limit は「ユーザー × 時間バケット × 次元」の行数を上限にします。20人分を取るつもりで limit=20 と書いても、内訳を付ければ20人は返りません。

利用条件と認証

利用できるのはClaude Enterpriseプランの組織で、read:analytics スコープを持つAPIキーが必要です。このキーは、claude.aiのOrganization settingsにあるAPIのページで公開APIアクセスを有効にしてから、Analytics APIキーとして作ります。

Claude ConsoleのAdmin APIキーとは別物です。Admin APIキーではこのAPIを呼べず、逆も同じです。2種類のAPIの違いはClaude Analytics APIの使い分けにまとめてあります。

プランにも条件があります。使用量とコストのエンドポイントは使用量ベースのEnterpriseプラン向けで、シート課金型のプランでは利用クレジットの分だけが反映されます。シート課金の組織で数字が小さく見えても、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_usage_report\
?starting_at=2026-10-01T00:00:00Z\
&ending_at=2026-10-15T00:00:00Z\
&limit=100" \
  --header "x-api-key: $ANALYTICS_API_KEY" \
  --header "anthropic-version: 2023-06-01"

応答は data の配列です。group_by[] も bucket_width も付けなければ、ユーザー1人が1行になります。公式の応答例に沿うと、1行は次のような形です(値は例)。

{
  "actor": {
    "type": "user_actor",
    "user_id": "user_01AbCdEfGhIjKlMnOpQrSt",
    "email_address": "jane@example.com",
    "name": "Jane Smith",
    "deleted": false
  },
  "cache_creation": {
    "ephemeral_1h_input_tokens": 0,
    "ephemeral_5m_input_tokens": 0
  },
  "cache_read_input_tokens": 3200000,
  "output_tokens": 891000,
  "requests": 128,
  "server_tool_use": { "web_search_requests": 10 },
  "total_tokens": 5377000,
  "uncached_input_tokens": 1284500
}

これに加えて、model や product などの次元は、group_by[] に指定したときだけ値が入ります。指定しなければnullです。トップレベルには data_refreshed_at、has_more、next_page、organization_id が付きます。

行の数字をどう読むか

トークンの種類は4つに分かれます。

フィールド中身
uncached_input_tokens中身キャッシュを使わずに処理した入力トークン
cache_read_input_tokens中身キャッシュから読んだ入力トークン
cache_creation中身キャッシュ作成の入力トークン。5分と1時間で ephemeral_5m_input_tokens / ephemeral_1h_input_tokens に分かれる
output_tokens中身生成された出力トークン

total_tokens は全トークン種別の合計で、既定の並び順の基準でもあります。つまり、キャッシュ読み取りが多いユーザーは、実際の新規入力が少なくても上位に来ます。「新しく処理させた量」で見たいなら、並び順を uncached_input_tokens か output_tokens に替えます。

requests はAPIリクエスト数です。コード実行のような実行スパンは、リクエストではなくスパン数で数えられ、product がnullの行として現れます。server_tool_use.web_search_requests はウェブ検索の回数です。

この応答には、金額も cost_type / token_type の次元もありません。それらはコスト系エンドポイントの機能です。トークンの種類ごとの内訳は、行の中のフィールドとして最初から横に並んで返ります。

actorで見えるユーザーの状態

actor は常に user_actor 型で、user_id、email_address、name、deleted を持ちます。旧来の email は非推奨で、email_address と同じ値です。

メンバーが組織から外れた場合や、IdP経由でデプロビジョニングされた場合は deleted: true になり、email_address は残ります。アカウント自体が削除されると email_address がnullになり、name は "Deleted User" と返ります。システムが発行するサービスアカウントは email_address がnullで、name にサービス名が入ることがあります。

集計から退職者を外すには、exclude_deleted_users=true を付けます。このとき1ページの行数が limit を下回ることがあるので、has_more と next_page で最後までたどります。

order_byで「何の多さ」で並べるかを選ぶ

order_by で選べる指標は4つで、既定は total_tokens です。order は desc(既定)か asc です。

知りたいことorder_by
全体としていちばん使っている人order_bytotal_tokens
新規の入力が多い人order_byuncached_input_tokens
出力を多く生成させている人order_byoutput_tokens
呼び出し回数が多い人order_byrequests

asc で並べれば、使用量の少ないユーザーから順に見られます。ゼロ使用の席が行として返るかどうかは、このエンドポイントの説明に書かれていません。未使用の席を探すなら、席数を返す活動サマリー系のエンドポイントと突き合わせます。

group_byとフィルターで内訳を出す

ユーザーごとの合計だけでは、何に使ったかが分かりません。group_by[] を付けると、各ユーザーの行が次元ごとに分かれます。指定できるのは9つです。

  • product、model、context_window、inference_geo、speed
  • rbac_group_id、slack_channel_id
  • claude_tag_category、claude_tag_user_id
curl -G "https://api.anthropic.com/v1/organizations/analytics/user_usage_report" \
  --data-urlencode "starting_at=2026-10-01T00:00:00Z" \
  --data-urlencode "group_by[]=product" \
  --data-urlencode "group_by[]=model" \
  --data-urlencode "order_by=output_tokens" \
  --data-urlencode "limit=200" \
  --header "x-api-key: $ANALYTICS_API_KEY" \
  --header "anthropic-version: 2023-06-01"

リスト型のパラメータは、値ごとにパラメータを繰り返すブラケット記法で渡します。絞り込みには products[]、models[]、user_ids[]、rbac_group_ids[]、context_windows[]、inference_geos[]、speeds[] が使えます。products[] の値は chat、chat_cowork_unified、claude-tag、claude_code、claude_design、claude_in_chrome、cowork、office_agent です。

chat_cowork_unified を含む製品別の数字は、利用者が移行するにつれて chat や cowork から動きます。その仕組みはchat_cowork_unifiedに数字が移る仕組みで扱っています。製品で絞るときは、ここを products[] に入れ忘れると、移行済みのユーザーの分が抜けます。

時間で分割する: bucket_width

bucket_width(1d、1h、1m)を指定すると、各行に starting_at と ending_at が入り、1人が時間バケットごとの複数行に分かれます。このとき ending_at は必須です。1m を選ぶと、範囲は最大24時間に制限されます。省略すると、期間全体が1行に集約されます。

上位ユーザーを製品別に集計するスクリプト

内訳付きでは1人が複数行になるので、集計はユーザーごとにまとめ直します。次のスクリプトは、product で分けた応答をユーザー単位にまとめ、製品別の total_tokens を並べます。410(カーソル失効)を受けたら先頭から取り直す処理も入れています。

import os
from collections import defaultdict
import requests
 
URL = "https://api.anthropic.com/v1/organizations/analytics/user_usage_report"
HEADERS = {
    "x-api-key": os.environ["ANALYTICS_API_KEY"],
    "anthropic-version": "2023-06-01",
}
PARAMS = {
    "starting_at": "2026-10-01T00:00:00Z",
    "ending_at": "2026-10-15T00:00:00Z",
    "group_by[]": ["product"],
    "exclude_deleted_users": "true",
    "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"]
 
by_user = defaultdict(lambda: defaultdict(int))
for r in fetch_all():
    email = r["actor"]["email_address"] or r["actor"]["user_id"]
    by_user[email][r["product"] or "other"] += r["total_tokens"]
 
top = sorted(by_user.items(), key=lambda kv: sum(kv[1].values()), reverse=True)
for email, products in top[:10]:
    print(email, sum(products.values()), dict(products))

product がnullの行は、製品に帰属できない使用量です。ここでは other にまとめています。公式の説明にも、帰属できない使用量が other として報告されることがある旨があります。

limit は内訳を含めた行数の上限なので、同じユーザーの行が複数に分かれます。上のように全ページを取得してからユーザー単位で合算します。

組織総額との差を確かめる

ユーザー別の合計が組織の使用量に届かないのは仕様どおりです。差を確かめるには、group_by[] なしの usage_report を同じ期間・同じ bucket_width で取り、その合計からユーザー別の合計を引きます。差は、APIキー直叩きや自動化など、席ユーザーに帰属しない分に当たります。

注意点は、期間の揃え方です。どちらも ending_at を省くと、data_refreshed_at より後の未完了データが末尾に含まれます。2つの数字を比べるなら、両方に同じ ending_at を渡し、その値を前回の data_refreshed_at 以前にします。そうしないと、取得した時刻のずれがそのまま差になります。

つまずきやすい点

  • RBACグループで分けると、複数グループに属するユーザーの全使用量が各グループの行に計上される。グループ行の合計は組織総額を超えうる。組織の真の総量は group_by[] なしの実行で取る
  • claude_tag_user_id はSlackのユーザーIDで、claude.aiのユーザーIDではない。rbac_group_id の次元や rbac_group_ids[] フィルターとは併用できない
  • 特定のユーザーに帰属しないClaude Tagの利用(監視や、Claudeが自発的に応答した分)は、ユーザー別の行に入らない。ユーザー行の合計がClaude Tag全体より小さくなる
  • Claude Tagのダイレクトメッセージの利用は、claude-tag ではなくそのユーザーの製品の下に出る。products[]=claude-tag と claude_tag_categories[]=dm を併用すると、DMは除外される
  • カーソルは発行したクエリに結び付く。途中でパラメータを変えると400、データ更新後に失効すると410になる
  • Claude CodeをAmazon Bedrock経由で使っている場合、そのClaude Codeの利用はこのAPIに返らない

データの鮮度とレート制限

データは通常4時間ごとに更新されます。応答の data_refreshed_at は、その応答が参照したエクスポートの時刻で、該当期間をカバーするエクスポートがなければnullで data も空です。

値は、遅れて届くイベントや再調整のために改訂されます。改訂は、使用日が属する暦月の終了から約7日後まで続きます。たとえば10月1日の値は、11月7日ごろまで変わりえます。請求と突き合わせる用途では、終了から7日以上たった月だけを対象にします。

レート制限はキー単位ではなく組織単位で、既定ではAnalytics API全体で毎分60リクエストです。ユーザー別に内訳を何度も取り直す実装では、この上限に当たりやすくなります。

まとめ

user_usage_report は、席ユーザーのトークン使用量を順位付きで返します。見たい指標に合わせて order_by を選び、内訳が要るときは group_by[] を足します。内訳付きでは1人が複数行になるため、全ページを取得してユーザー単位で合算するところまでが実装の範囲です。組織全体の量は usage_report、金額はuser_cost_reportと、母集団と単位で分けて使います。権限や画面での見方はClaude Team/Enterpriseの使用量を確認する方法にあります。

この記事を共有:XはてブLinkedIn