$ cat ./docs/usage.md

Usage API

Query token counts and cost for the API key making the request. One key, its own data — no cross-key access. Use this to drive your own billing dashboards, monthly reconciliation, or cost alerts.

Esta documentación está en inglés. Para una guía de inicio rápido en español, consulta:Guía en español — Claude API en España y Latinoamérica

GET /v1/usage returns aggregated token and cost figures for the API key in the Authorization header. Group by day or hour; optionally split by model. No scope to read another key's data — for that, log in to the dashboard.

#Authentication

Pass your Brievio API key as a Bearer token, the same key whose usage you want to read. The endpoint returns only the usage that was billed against this specific key — there is no parameter to request another key's data.

//
Treat one project per key. The recommended pattern is to mint a separate API key for each application you bill — Brievio dashboards and this endpoint then naturally roll up "per project". Manage keys at /app/keys.

#Request

ParamRequiredDefaultNotes
start_dateyesYYYY-MM-DD, UTC. Inclusive.
end_dateyesYYYY-MM-DD, UTC. Inclusive. Must be ≤ today.
bucketnodayOne of day, hour.
group_byno(none)Comma-separated. Supported: model. Omit to get one row per bucket.
//
Window limits. bucket=day accepts up to 90 days; bucket=hour accepts up to 7 days. Beyond that the request returns 400 invalid_request_error — split into multiple calls.
//
Time zone. Buckets are aligned to UTC days/hours. If you need wall-clock days in another tz, query daily and fold the edges on the client. A future tz= param will do this server-side without changing any existing field.

#Examples

Daily totals across the last 30 days, all models rolled up:

usage_30d.sh
# Daily totals for the last 30 days
curl https://api.brievio.com/v1/usage \
  -G \
  -H "Authorization: Bearer $BRIEVIO_API_KEY" \
  --data-urlencode "start_date=2026-04-28" \
  --data-urlencode "end_date=2026-05-27"

One row per (day, model):

usage_by_model.sh
# Per-model daily breakdown — one row per (day, model)
curl https://api.brievio.com/v1/usage \
  -G \
  -H "Authorization: Bearer $BRIEVIO_API_KEY" \
  --data-urlencode "start_date=2026-05-01" \
  --data-urlencode "end_date=2026-05-27" \
  --data-urlencode "group_by=model"

Python — sum the window into a single number:

usage_query.py
import os, datetime as dt, requests

today = dt.date.today()
start = today - dt.timedelta(days=29)  # last 30 days inclusive

r = requests.get(
    "https://api.brievio.com/v1/usage",
    headers={"Authorization": f"Bearer {os.environ['BRIEVIO_API_KEY']}"},
    params={
        "start_date": start.isoformat(),
        "end_date":   today.isoformat(),
        "group_by":   "model",
    },
    timeout=30,
)
r.raise_for_status()
body = r.json()

# body["data"] is one row per (day, model). Sum cost across the window:
total_usd = sum(float(row["cost_usd"]) for row in body["data"])
print(f"Spent ${total_usd:.4f} on key {body['api_key']['name']}")

#Response

200 OKapplication/json with a list of rows. Buckets with zero activity are omitted; the client fills gaps if it needs a contiguous series.

response.json
{
  "object": "list",
  "api_key": {
    "id": "k_abc123",
    "name": "production",
    "prefix": "sk-br-aZ8x"
  },
  "start_date": "2026-05-01",
  "end_date":   "2026-05-27",
  "bucket": "day",
  "data": [
    {
      "bucket_start": "2026-05-01T00:00:00.000Z",
      "model": "claude-sonnet-4-6",
      "requests": 1284,
      "errors": 7,
      "input_tokens": 1532890,
      "output_tokens": 245100,
      "cached_input_tokens": 980000,
      "cache_write_tokens": 12000,
      "cost_usd": "3.452100"
    },
    {
      "bucket_start": "2026-05-01T00:00:00.000Z",
      "model": "gpt-5",
      "requests": 88,
      "errors": 0,
      "input_tokens": 42000,
      "output_tokens": 12300,
      "cached_input_tokens": 0,
      "cache_write_tokens": 0,
      "cost_usd": "0.210400"
    }
  ]
}
FieldTypeMeaning
bucket_startstring (ISO 8601, UTC)Start of the day or hour this row aggregates.
modelstringModel slug. Present only when group_by=model.
requestsnumberSuccessful calls in this bucket.
errorsnumberCalls that ended in error/timeout/cancelled. Not billed.
input_tokensnumberTotal input tokens (includes cached + cache-write subsets).
output_tokensnumberTotal output tokens.
cached_input_tokensnumberSubset of input_tokens served from the upstream prompt cache.
cache_write_tokensnumberTokens written to cache this bucket (Anthropic only; 0 elsewhere).
cost_usdstringWhat you were charged in USD for this bucket, 6-decimal precision. String to preserve precision; parseFloat() safely.
//
Why cost is a string. Tiny per-call costs (a few hundred-thousandths of a cent for cache-heavy traffic) round to zero in IEEE-754 if you stringify them naively. Keeping the wire format as a string with 6 decimals lets your accounting code parse it with arbitrary precision (e.g. Decimal()).

#Client-side caching

The endpoint sets Cache-Control: private, max-age=60. Most clients (browsers, requests with a session, any HTTP cache on your side) will return the same response for the same parameters within a 60-second window — fine for dashboards that poll. To force a fresh read, vary the query string (e.g. append a &_=tsbuster) or pass Cache-Control: no-cache on your side.

#Errors

StatusCodeWhen
401authentication_errorMissing, malformed, or revoked Bearer key.
400invalid_request_errorBad start_date/end_date format, end before start, future date, span over the bucket limit, or an unsupported group_by value.
500internal_errorDB unreachable or unexpected failure.

#Caveats

  • Deleted keys. When you revoke an API key, its past usage rows lose their api_key_id (set to null) so the historical totals can no longer be retrieved via this endpoint. The data still exists for your account aggregate view in the dashboard. If you need an immutable per-key audit trail, fetch and store the data before revoking.
  • Near-real-time. Rows land within a few seconds of the underlying call completing. There is no guaranteed staleness bound — if you need exact alignment with your invoice, query for yesterday once a day rather than minute-by-minute.
  • Cost is billed cost. The cost_usd here matches what was debited from your wallet ledger. It already reflects the max(catalog, upstream × markup) floor described in Billing. There is no separate "wholesale" figure in this response.

#Where to go next