$ 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.
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.
#Request
| Param | Required | Default | Notes |
|---|---|---|---|
start_date | yes | — | YYYY-MM-DD, UTC. Inclusive. |
end_date | yes | — | YYYY-MM-DD, UTC. Inclusive. Must be ≤ today. |
bucket | no | day | One of day, hour. |
group_by | no | (none) | Comma-separated. Supported: model. Omit to get one row per bucket. |
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.tz= param will do this server-side without changing any existing field.#Examples
Daily totals across the last 30 days, all models rolled up:
# 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):
# 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:
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 OK — application/json with a list of rows. Buckets with zero activity are omitted; the client fills gaps if it needs a contiguous series.
{
"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"
}
]
}| Field | Type | Meaning |
|---|---|---|
bucket_start | string (ISO 8601, UTC) | Start of the day or hour this row aggregates. |
model | string | Model slug. Present only when group_by=model. |
requests | number | Successful calls in this bucket. |
errors | number | Calls that ended in error/timeout/cancelled. Not billed. |
input_tokens | number | Total input tokens (includes cached + cache-write subsets). |
output_tokens | number | Total output tokens. |
cached_input_tokens | number | Subset of input_tokens served from the upstream prompt cache. |
cache_write_tokens | number | Tokens written to cache this bucket (Anthropic only; 0 elsewhere). |
cost_usd | string | What you were charged in USD for this bucket, 6-decimal precision. String to preserve precision; parseFloat() safely. |
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
| Status | Code | When |
|---|---|---|
401 | authentication_error | Missing, malformed, or revoked Bearer key. |
400 | invalid_request_error | Bad start_date/end_date format, end before start, future date, span over the bucket limit, or an unsupported group_by value. |
500 | internal_error | DB 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_usdhere matches what was debited from your wallet ledger. It already reflects themax(catalog, upstream × markup)floor described in Billing. There is no separate "wholesale" figure in this response.
#Where to go next
- Billing & the ledger — how each call's
cost_usdis computed before it lands here. - Authentication — minting new API keys (one per project).
- Dashboard usage view — same data with charts, per-call drill-down, and account-wide roll-up across all your keys.