Zum Hauptinhalt springen

Die Usage-&-Cost-Analytics-API: jeden Dollar zuordnen

Experte
What you'll learn
  • Was usage_report und cost_report abgelöst hat und warum die neue Form aus Zeilen statt aus Zeit-Buckets besteht
  • Der Zeilenvertrag: starting_at / ending_at, group und usage oder cost — und warum eine Dimension null ist, solange du nicht danach gruppierst
  • group_by[] nimmt fünf Dimensionen — außer den zwei am Cost-Endpunkt, die nicht zählen und limit nicht beachten
  • Das Akteursmodell: Benutzer, Servicekonto, föderierte Cloud-Identität oder der API-Schlüssel selbst — und warum api_key_ids[] und actor_ids[] unterschiedliche Fragen beantworten
  • Die Cent-Falle: amount ist ein Dezimalstring in CENT, also ist "41280.000000" gleich 412,80 $ und ein naives Dashboard liegt um den Faktor 100 daneben
  • Regeln zur Datenaktualität, die ein Dashboard für den laufenden Tag ungültig machen, und der Trick für Ergebnisse, die sich zwischen Aufrufen nicht ändern
  • Das Ein-Zeilen-Rezept für die Claude-Code-Kosten pro Benutzer

Zwei Endpunkte, eine Aufgabe: Sie sagen dir, wohin deine Claude-Ausgaben tatsächlich geflossen sind.

  • GET /v1/organizations/analytics/usage — Token-Zahlen und Anfragezahlen.
  • GET /v1/organizations/analytics/cost — Kosten in USD.

Für eine Claude-Console-Organisation entspricht das grob dem, was die Seiten Usage und Cost der Console zeigen, mit den Dimensionen, die du wählst, statt der von ihnen gewählten. Der Grund, warum es darauf ankommt, ist die Frage, die niemand aus der Console beantworten kann: Welcher meiner Kunden, Agenten oder API-Schlüssel ist für die Zeile verantwortlich, die gestiegen ist?

Das ist Reporting, keine Durchsetzung

Nichts hiervon stoppt eine Rechnung. Dafür siehe Harte Ausgabenlimits — und beachte, dass beides einander ergänzt: Limits entscheiden, ob die Ausgabe stattfindet, diese API sagt dir, wer sie verursacht hat.

Zugangsdaten, nach Organisationstyp​

OrganisationWas zu verwenden ist
Claude ConsoleEin Admin-API-Schlüssel oder ein OAuth-Bearer-Token mit dem Scope org:admin (gesendet als authorization: Bearer, nicht x-api-key)
Claude EnterpriseEin Schlüssel mit dem Scope read:analytics, vom primären Owner in claude.ai erstellt, nachdem die Analytics-API für die Organisation aktiviert wurde

Gib dem Collector-Dienst einen eigenen Schlüssel, bewahre ihn in einem Secrets-Manager auf und setze ein Ablaufdatum für den Schlüssel, damit die Rotation erzwungen statt nur erinnert wird. Wo verfügbar, ist eine Workload-Identity-Federation-Regel mit org:admin eindeutig besser — kurzlebige Tokens, kein langlebiges Geheimnis, das abfließen könnte. Dieselbe Überlegung gilt für die Admin-API allgemein.

Schnellstart​

Tägliche Token-Nutzung nach Modell, eine Woche

curl --globoff "https://api.anthropic.com/v1/organizations/analytics/usage?\
starting_at=2026-09-01T00:00:00Z&\
ending_at=2026-09-08T00:00:00Z&\
time_bucket_width=1d&\
group_by[]=model&\
limit=100" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: analytics-usage-cost-2026-08-24" \
-H "x-api-key: $ANTHROPIC_API_KEY"

--globoff ist wichtig: Ohne es versucht curl, das [] in group_by[] als Glob-Bereich zu interpretieren.

Der Zeilenvertrag​

Das ist der Teil, an dem Leute beim Wechsel von den alten Endpunkten stolpern, denn die Antwort ist absichtlich anders geformt.

Guided walkthrough1 of 6
  1. starting_at und ending_at geben den von der Zeile abgedeckten Zeitraum an, group enthält jede Dimension, und usage (am Cost-Endpunkt cost) enthält die Metriken. Dimensionen und Metriken stehen in getrennten Objekten — sie liegen nicht nebeneinander wie im Legacy-Report.

order_by am Usage-Endpunkt akzeptiert total_tokens (Standard), output_tokens, uncached_input_tokens, requests oder time. Am Cost-Endpunkt: amount (Standard), list_amount oder time.

Standardmäßig nach Rang geordnet ist der stille Dashboard-Fehler

Wenn du einen Bereich anforderst und die Zeilen in der Reihenfolge ihres Eintreffens aufträgst, erhältst du ein nach Größe sortiertes Balkendiagramm, das wie eine Zeitreihe aussieht. Chronologische Zeilen bekommst du nur, wenn du beides setzt: time_bucket_width und order_by=time.

Nicht jede Dimension gilt für dich​

model und actor gelten für jede Organisation. api_key_id gibt es nur in der Console. slack_channel_id gibt es nur in Enterprise. Eine Dimension, die nicht zutrifft, ist in jeder Zeile null, und ihr Filter trifft nichts — stillschweigend. Die einzige Ausnahme ist rbac_group_id, das stattdessen einen Fehler liefert, wenn Teams für deine Organisation nicht verfügbar sind.

Und eine Verhaltensänderung, die man sich an die Wand hängen sollte: In der Legacy-API lieferte ein unbekannter API-Schlüssel oder eine unbekannte Workspace-ID einen Fehler. Hier trifft er nichts, einschließlich eines falsch geschriebenen models[]-Werts. Ein leeres Ergebnis ist jetzt die normale Antwort auf einen Tippfehler.

Akteure: wer die Kosten zugeordnet bekommt​

Die Dimension actor gibt an, wem eine Anfrage zugeordnet wird — einem Benutzer, einem Servicekonto, einer föderierten Cloud-Identität (einem AWS- oder Google-Cloud-Principal, bei Organisationen, die Claude über ihre Cloud-Plattform erreichen) oder, wenn nichts davon dahintersteht, dem API-Schlüssel selbst. Gruppierst du nach actor, ist das actor jeder Zeile ein Objekt, dessen type angibt, um welche Art es sich handelt. Nutzung, die keiner Identität zuzuordnen ist, hat actor: null.

Die Zuordnungsregeln:

  • Persönliche Schlüssel und Servicekonto-Schlüssel handeln als der Benutzer oder das Servicekonto, mit dem sie verknüpft sind: Die Identität steht in actor, der Schlüssel in api_key_id.
  • Legacy-Workspace-Schlüssel sind nicht mit einer Identität verknüpft, daher erscheint der Schlüssel sowohl als actor als auch als api_key_id.
  • Ein Schlüssel, den Claude Code beim Anmelden für ein Mitglied erstellt, liegt im Claude-Code-Workspace, und seine Nutzung wird diesem Mitglied zugeordnet.
  • Anfragen ganz ohne API-Schlüssel — aus der Console, aus Claude Code nach einer Anmeldung ohne API-Schlüssel oder mit einem Workload-Identity-Federation-Token — haben api_key_id: null. Der actor ist weiterhin das angemeldete Mitglied oder, bei einer föderierten Workload, deren Servicekonto.
Zwei Filter, zwei verschiedene Fragen

api_key_ids[]=apikey_… trifft jede Anfrage, die dieser Schlüssel authentifiziert hat, egal, wem sie zugeordnet ist. actor_ids[]=apikey_… trifft nur Anfragen, die dem Schlüssel selbst zugeordnet sind. Sie sind nicht austauschbar, und wer sie verwechselt, bekommt ein Audit mit Lücken. Um Nutzung ohne Schlüssel in einen api_key_ids[]-Filter aufzunehmen, füge einen leeren Eintrag hinzu: api_key_ids[]=.

Der Cost-Endpunkt und die Faktor-100-Falle​

Der Cost-Endpunkt nimmt dieselben Filter, Dimensionen und Zeit-Buckets wie /usage. Das cost-Objekt jeder Zeile enthält:

FeldBedeutung
amountKosten nach Rabatten, vor Anrechnung von Guthaben
list_amountKosten zu Listenpreisen
currencyImmer "USD"
requestsAnzahl der Anfragen
amount ist in CENT, nicht in Dollar

currency sagt "USD", aber die Zahl ist ein Dezimalstring in Cent mit sechs Nachkommastellen. "41280.000000" sind 412,80 $, nicht 41.280 $. Der Legacy-Kostenreport lieferte dollarähnliche Strings wie "123.45"; derselbe Wert ist hier "12345.000000". Teile durch 100, bevor du irgendetwas darstellst, und schreibe den Test — das ist ein Fehler um den Faktor 100, der das Code-Review passiert, weil das Feld amount heißt und das Währungsfeld Dollar sagt.

Zwei Dimensionen gibt es nur am Cost-Endpunkt, und beide brechen die üblichen Regeln zu deinen Gunsten: group_by[]=cost_type teilt jede Zeile nach Kostenart auf, und group_by[]=token_type teilt die Token-Kosten nach Token-Art auf. Keine von beiden zählt zum Limit von fünf Dimensionen, und keine beachtet limit — daher kann eine Seite legitim mehr Zeilen als limit enthalten.

Wohin das Geld ging: Kosten nach Modell, Kostenart und Token-Art für einen Monat

curl --globoff "https://api.anthropic.com/v1/organizations/analytics/cost?\
starting_at=2026-09-01T00:00:00Z&\
ending_at=2026-10-01T00:00:00Z&\
group_by[]=model&\
group_by[]=cost_type&\
group_by[]=token_type" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: analytics-usage-cost-2026-08-24" \
-H "x-api-key: $ANTHROPIC_API_KEY"

Diese eine Abfrage ist bei jeder Organisation die erste, die man ausführen sollte, denn sie beantwortet die Frage, bei der die meisten Teams raten: Wie viel der Rechnung ist gar keine Inferenz. Die Aufteilung nach cost_type trennt Token-Ausgaben von serverseitigen Tools; die Aufteilung nach token_type trennt Cache-Lesezugriffe von nicht gecachtem Input und Output. Dominieren Cache-Lesezugriffe deine Input-Zeile, sind Prompt Caching und Cache-Diagnose die nächste Station; dominieren Output-Token, ist es Token-Verbrauch senken.

Claude-Code-Kosten pro Benutzer, in einer Abfrage

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_API_KEY"

Console-Organisationen haben dafür eine zweite Option — die dedizierte Claude Code Analytics API, die die Claude-Code-Aktivität pro Benutzer zusammen mit den geschätzten Kosten meldet.

Datumsbereiche und die Rundung, die niemand erwartet​

  • Zeitstempel sind RFC 3339 mit UTC-Offset (2026-09-01T00:00:00Z). starting_at ist inklusiv, ending_at ist exklusiv.
  • starting_at muss am oder nach dem 1. Januar 2026 liegen und innerhalb der letzten 365 Tage.
  • Eine Anfrage umfasst höchstens 31 Tage. ending_at ist standardmäßig 31 Tage nach starting_at oder jetzt, je nachdem, was früher liegt. Teile längere Bereiche in mehrere Anfragen auf.
  • ending_at ist Pflicht, sobald time_bucket_width gesetzt ist, und bei time_bucket_width=1m beträgt die Spanne höchstens 24 Stunden.
  • Beide Enden werden abgerundet auf eine Bucket-Grenze — oder auf die volle Stunde, wenn time_bucket_width nicht gesetzt ist. Bei 1h-Buckets wird starting_at=2026-09-01T10:43:00Z zu 10:00:00Z und ending_at=2026-09-01T15:43:00Z zu 15:00:00Z, sodass die Antwort 10:00–15:00 UTC abdeckt. Eine naiv geschriebene Abfrage „letzte 90 Minuten“ liefert eine Stunde.

Datenaktualität bringt Dashboards zum Scheitern​

Jede Antwort trägt einen Zeitstempel data_refreshed_at. Nutzung nach diesem Zeitpunkt fehlt oder ist unvollständig. Gibt es für keinen Teil des Bereichs schon Daten, ist data_refreshed_at gleich null und data leer.

EigenschaftWert
Typische Verzögerung der AktualisierungInnerhalb von 4 Stunden nach der zugrunde liegenden Nutzung
Schlimmster FallBis zu 24 Stunden
RevisionsfensterWerte können sich ändern, wenn verspätete Ereignisse und Abstimmungen eintreffen, bis etwa 7 Tage nach Ende des Kalendermonats
PaginierungJede Seite einer paginierten Abfrage liest denselben Snapshot, daher ist data_refreshed_at auf allen Seiten identisch

In der Praxis: Die Zahlen vom 1. Oktober können sich noch bis etwa zum 7. November ändern.

Das ist ein echter Rückschritt gegenüber der Legacy-API, bei der Daten typischerweise innerhalb von 5 Minuten erschienen, und es verändert, was du bauen kannst. Daraus folgen direkt zwei Regeln:

  • Für Ergebnisse, die sich zwischen Aufrufen nicht ändern, setze ending_at auf oder vor ein data_refreshed_at, das du bereits gesehen hast.
  • Für endgültige Summen frage nur Monate ab, die mindestens 7 Tage zurückliegen.
Baue darauf keine Echtzeit-Alarme

Ein Alarm bei Kostenspitzen mit 4–24 Stunden Verzögerung ist kein Alarm. Nutze die API für Zuordnung, Chargeback und Trend-Reporting; nutze Ausgabenlimits für den Teil, der in Minuten reagieren muss, und OpenTelemetry-Tracing für nahezu Echtzeit-Einblick pro Anfrage — und bedenke, dass Traces Best-Effort und at-least-once sind, sodass die Analytics-API, nicht deine Traces, das Hauptbuch ist.

Migration von usage_report und cost_report​

Was übernommen wird: dein Admin-API-Schlüssel, der Header anthropic-version, starting_at / ending_at, die Bucket-Breiten (1m, 1h, 1d), die Klammernotation, die Paginierungsfeldnamen sowie die Token- und Kostenfeldnamen. Was sich ändert:

Legacy-Usage-&-Cost-APIUsage-&-Cost-Analytics-API
Kein Beta-Headeranthropic-beta: analytics-usage-cost-2026-08-24 bei jeder Anfrage
Daten innerhalb von ~5 MinutenTypisch 4 Stunden, bis zu 24, revidiert bis ~7 Tage nach Monatsende
data sind Zeit-Buckets (älteste zuerst, leere eingeschlossen), jeder mit einer results-Liste, die Dimensionen und Metriken mischtdata sind nach order_by geordnete Zeilen, jede mit eigenem starting_at / ending_at, Dimensionen in group, Metriken in usage oder cost. Keine Zeile für eine leere Kombination
bucket_width, Standard 1dtime_bucket_width, kein Standard — übergib 1d, um tägliche Zeilen zu behalten, lass es weg für Summen über den gesamten Bereich
limit zählt Zeit-Buckets pro Seitelimit zählt Zeilen pro Seite (1–1.000, Standard 20), sodass die Seitenzahl davon abhängt, wie viele Kombinationen Daten haben
Beliebiger Datumsbereich, bucketweise vorwärts paginiertstarting_at ≥ 1. Jan. 2026 und innerhalb von 365 Tagen; ≤ 31 Tage pro Anfrage; ending_at bei Buckets Pflicht; ≤ 24 h bei 1m
account_ids[], service_account_ids[], group_by[]=account_id, group_by[]=service_account_idactor_ids[] mit denselben getaggten IDs und group_by[]=actor oder actor_type; lies actor.user_id oder actor.service_account_id
context_window[]context_windows[] (jetzt im Plural)
group_by[]=description im Cost-Report, mit einem description-StringEntfallen. Gruppiere stattdessen nach strukturierten Dimensionen — model, cost_type, token_type, context_window, service_tier, inference_geo, speed, die alle in eine Anfrage passen
Unbekannter API-Schlüssel oder unbekannte Workspace-ID → FehlerTrifft nichts. Ebenso ein falsch geschriebener models[]-Wert
workspace_id: null bedeutete den Standard-WorkspaceDer Standard-Workspace hat seine echte wrkspc_-ID
inference_geo: "not_available", service_tier: "standard" für Nutzung ohne beidesnull für beides. inference_geos[]=not_available trifft weiterhin die Zeilen ohne Geo
Der Kosten-service_tier war standard oder batch; amount wie "123.45"Die tatsächliche Stufe; amount mit sechs Nachkommastellen ("123.450000"), plus list_amount
Codeausführung fehlt im Usage-Report; keine AnfragezahlenCodeausführung erscheint als Nutzungszeilen mit model: null; requests an beiden Endpunkten und total_tokens in Usage-Zeilen

Um den täglichen Legacy-Report exakt nachzubilden: Fordere time_bucket_width=1d mit denselben group_by[] an, paginiere mit limit=1000, füge order_by=time&order=asc für chronologische Tage hinzu, gruppiere die Zeilen dann nach starting_at und fülle die fehlenden Tage selbst mit Nullen auf.

Der Standard-Workspace ist nicht mehr null — passe deinen Gruppierungscode an

Legacy-Konsumenten behandelten workspace_id: null oft als Sonderfall „Default Workspace“. Diese Zuordnung ist jetzt falsch, und ein null bedeutet hier stattdessen „nicht danach gruppiert“. Der Standard-Workspace hat eine normale wrkspc_-ID — aber List Workspaces lässt ihn aus, sofern du nicht include_default=true übergibst, sodass eine unbekannte ID in deinen Zeilen in deiner Workspace-Tabelle vielleicht nicht auftaucht. Löse sie mit Get Workspace auf: Der Standard-Workspace liefert "name": "Default".

Jenseits der Claude API​

PlattformVerfügbarkeitWas anders ist
Claude Platform on AWSBeide EndpunkteVerwendet die Basis-URL und Authentifizierung der Plattform statt eines Admin-API-Schlüssels; AWS IAM autorisiert über die Aktionen ListAccountUsage und ListAccountCost. Jede Anfrage umfasst nur die Workspaces in der AWS-Region dieses Endpunkts — rufe jede Region auf, um die Organisation abzudecken. user_profile_id, claude_tag_category, claude_tag_user_id und die Managed-Agents-Dimensionen (agent_id, environment_id, deployment_id, session_id) sind in jeder Zeile null
Claude Models on Google Cloud (von Anthropic verwaltet)Beide EndpunkteAufgerufen über die Basis-URL auf Organisationsebene mit Google-Cloud-Zugangsdaten; /usage braucht die IAM-Berechtigung usageAnalytics.list, /cost braucht costAnalytics.list

Die Dimension user_profile_id ist auf der Claude API selbst die interessante: In Kombination mit Benutzerprofilen macht sie diese API zu einem Chargeback pro Endkunde für eine Plattform oder einen Reseller — dieselbe Zuordnung, die den Wirkungsradius deiner Durchsetzung begrenzt, sagt dir auch, was dich jeder Kunde kostet.

Häufige Fehler​

FehlerWas tatsächlich passiert
amount als Dollar darstellenJede Zahl ist um den Faktor 100 zu groß. Es sind Cent mit sechs Nachkommastellen
Zeilen in Antwortreihenfolge als Zeitreihe auftragenZeilen sind über den gesamten Bereich nach order_by geordnet. Du brauchst time_bucket_width und order_by=time
--globoff bei curl weglassencurl behandelt [] in group_by[] als Glob, und die Anfrage geht nicht wie geschrieben hinaus
Mit Nullen aufgefüllte Buckets erwartenBuckets und Kombinationen ohne Daten erzeugen keine Zeilen. Fülle Lücken clientseitig
Einen Alarm für Kostenspitzen darauf bauenDie Daten hinken 4–24 Stunden hinterher und werden eine Woche nach Monatsende revidiert
Monat-bis-heute-Summen vergleichen, die an verschiedenen Tagen abgerufen wurdenWerte werden bis ~7 Tage nach Monatsende revidiert. Fixiere ending_at auf ein data_refreshed_at, das du schon gesehen hast
Ein leeres Ergebnis als „keine Nutzung“ wertenEine unbekannte Workspace-ID, API-Schlüssel-ID oder ein falsch geschriebener Modellname trifft jetzt nichts, statt einen Fehler zu liefern
Die Zuordnung workspace_id: null → „Default“ beibehaltenDer Standard-Workspace hat jetzt eine echte wrkspc_-ID; null bedeutet „nicht nach dieser Dimension gruppiert“
Annehmen, dass limit die Zeilen pro Seite begrenztcost_type und token_type ignorieren limit vollständig
account_ids[] / service_account_ids[] weiterverwendenErsetzt durch actor_ids[] mit group_by[]=actor oder actor_type

Quiz​

Check yourself

0/3
  1. Eine Kostenzeile kommt mit cost.amount = "41280.000000" und cost.currency = "USD" zurück. Was sollte dein Dashboard anzeigen?
  2. Du fragst 7 Tage Nutzung mit group_by[]=model an und trägst die Zeilen in der Reihenfolge ihres Eintreffens auf, in der Erwartung eines täglichen Liniendiagramms. Du erhältst etwas, das nicht chronologisch ist. Was fehlt?
  3. Das Finanzteam ruft die Kosten für Oktober bis heute am 2. Oktober, erneut am 20. Oktober und noch einmal am 10. November ab — und erhält drei verschiedene Summen für den 1. Oktober. Welche Erklärung entspricht dem dokumentierten Verhalten?
Key takeaways
  • Zwei Endpunkte — /v1/organizations/analytics/usage und /cost — die beide anthropic-beta: analytics-usage-cost-2026-08-24 verlangen.
  • Teile amount durch 100. Es sind Cent mit sechs Nachkommastellen, obwohl currency USD anzeigt.
  • Eine Dimension ist null, solange du nicht danach gruppierst, und eine Kombination ohne Daten erzeugt überhaupt keine Zeile — also clientseitig mit Nullen auffüllen.
  • Standardmäßig nach Rang geordnet: Eine echte Zeitreihe braucht time_bucket_width UND order_by=time.
  • Aktualität bedeutet 4–24 Stunden mit Revisionen bis ~7 Tage nach Monatsende. Das ist Infrastruktur für Zuordnung und Chargeback, nicht für Alarme.
  • api_key_ids[] (was der Schlüssel authentifiziert hat) und actor_ids[] (was dem Schlüssel zugeordnet ist) beantworten verschiedene Fragen — wähle bewusst.
  • Tippfehler führen nicht mehr zu Fehlern. Ein unbekannter Workspace, Schlüssel oder Modellname trifft stillschweigend nichts.

Weiter​