If you're running Claude in production, you eventually need answers to questions like: How many tokens are we burning? Which team is driving spend? Is our prompt caching actually working? Anthropic's Usage & Cost Admin API gives you programmatic access to exactly this data, so you can build dashboards, chargeback reports, and cache-efficiency analyses without scraping the console.

This tutorial walks through connecting to the API, pulling daily usage, and interpreting the token metrics that matter.

Setup and authentication

The Admin API lives under https://api.anthropic.com/v1/organizations and requires an Admin API key (format sk-ant-admin...), which is distinct from your regular message-generation key. Grab one from the Claude Console's admin keys settings, and treat it like a production secret: store it in an environment variable, rotate it regularly, and never commit it.

A thin wrapper keeps auth and error handling in one place:

import os, requests
from typing import Any

class AnthropicAdminAPI:
    def __init__(self, api_key: str | None = None):
        self.api_key = api_key or os.getenv("ANTHROPIC_ADMIN_API_KEY")
        if not self.api_key or not self.api_key.startswith("sk-ant-admin"):
            raise ValueError("Valid Admin API key required.")
        self.base_url = "https://api.anthropic.com/v1/organizations"
        self.headers = {
            "anthropic-version": "2023-06-01",
            "x-api-key": self.api_key,
            "Content-Type": "application/json",
        }

    def _make_request(self, endpoint: str, params: dict[str, Any]) -> dict[str, Any]:
        r = requests.get(f"{self.base_url}/{endpoint}",
                         headers=self.headers, params=params, timeout=30)
        if r.status_code == 401:
            raise ValueError("Invalid key or insufficient permissions")
        r.raise_for_status()
        return r.json()

Handle 401 (bad key or permissions) and 429 (rate limit) explicitly so failures are easy to diagnose.

Querying usage data

There are two main endpoints. The Messages Usage API (usage_report/messages) returns token-level data grouped into time buckets — fixed intervals of aggregated usage. The Cost API returns financial data in USD.

Usage requests take a starting_at, ending_at, a bucket_width (e.g. 1d), and a limit. A subtle but important detail: snap your timestamps to the start of the day so they align with bucket boundaries. Misaligned windows can silently drop or split data.

from datetime import datetime, time, timedelta

def get_daily_usage(client, days_back=7):
    end = datetime.combine(datetime.utcnow(), time.min)
    start = end - timedelta(days=days_back)
    params = {
        "starting_at": start.strftime("%Y-%m-%dT%H:%M:%SZ"),
        "ending_at": end.strftime("%Y-%m-%dT%H:%M:%SZ"),
        "bucket_width": "1d",
        "limit": days_back,
    }
    return client._make_request("usage_report/messages", params)

Understanding the token metrics

Each bucket contains a results list. The key fields:

  • uncached_input_tokens — fresh prompt and system tokens
  • output_tokens — Claude's generated responses
  • cache_creation — tokens written to the cache, split by TTL (ephemeral_5m_input_tokens, ephemeral_1h_input_tokens)
  • cache_read_input_tokens — previously cached tokens reused
  • server_tool_use — server-side tool activity such as web_search_requests

A useful derived metric is cache efficiency: cache reads as a percentage of all input tokens. Higher is better — it means you're reusing cached context instead of paying full price for it.

cache_read = result.get("cache_read_input_tokens", 0)
creation = result.get("cache_creation", {})
created = (creation.get("ephemeral_5m_input_tokens", 0)
           + creation.get("ephemeral_1h_input_tokens", 0))
uncached = result.get("uncached_input_tokens", 0)

total_input = uncached + created + cache_read
efficiency = (cache_read / total_input * 100) if total_input else 0

Tracking cost

The Cost API returns spend in USD with service-type breakdowns, ideal for finance reporting and per-workspace attribution. Two constraints to remember: it only supports a 1d bucket_width, and requests are capped at roughly 31 days per call — page through longer ranges. Note that Priority Tier costs use a separate billing model and will never appear in the cost endpoint, though Priority Tier usage still shows up in the usage endpoint.

Key takeaways

  • Use a dedicated Admin API key (sk-ant-admin...), kept in env vars and rotated regularly.
  • Snap timestamps to day boundaries so they align with usage buckets.
  • Track uncached_input_tokens, output_tokens, cache_creation, and cache_read_input_tokens to understand consumption and cache efficiency.
  • The Cost API only supports 1d buckets and ~31-day windows; page for longer ranges.
  • Priority Tier spend won't appear in the cost endpoint — track its usage separately.

Original tutorial synthesized from the source: Usage Cost Api — Anthropic.