Read API
Your own numbers, as JSON
The CLI already answers these questions on your machine, and its MCP server already lets an agent ask them. This is for when the thing asking is somewhere else: a dashboard, a cron job, a status page, an agent that is not on your laptop.
Patron Read-only, scoped to your own account. Create a token in settings.
Authentication
A bearer token in the Authorization header. Tokens are shown once, when you create them, and stored hashed: there is no endpoint that can hand a secret back, because a store that can reveal a token is a store worth stealing. Revoking one takes effect on the next request.
curl -H "Authorization: Bearer tsk_…" \ https://tokenstat.ai/api/v1/usage
Session cookies are refused on these routes, so a logged-in browser tab cannot be tricked into making an authenticated call on your behalf. A read token cannot sync, and the CLI's sync token cannot read.
Usage rows
GET/api/v1/usage
Counters, summed and grouped however you ask. Everything the hosted side knows comes from the CLI's sync payload, so this is the same shape: tokens, cache reads and writes, and a request count.
| Parameter | Meaning |
|---|---|
group_by | day (default), model, source, project, or machine. |
from | YYYY-MM-DD, inclusive. Clamped to your plan's history window. |
to | YYYY-MM-DD, inclusive. |
format | json (default) or csv. |
{
"group_by": "day",
"window": { "from": null, "to": null },
"rows": [
{
"day": "2026-07-28",
"input": 1300,
"output": 600,
"cache_read": 4000,
"cache_write": 200,
"tokens": 6100,
"events": 4
}
]
}project is the opaque HMAC your machine computed, or null for unattributed work. The server has never been able to turn it back into a path, which is the point, so it is handed back exactly as it arrived. events counts requests, not sessions.
Profile document
GET/api/v1/profile
The same document your public page renders from, whether or not the page is published: totals, cache split, per-model and per-harness breakdowns, the daily series, the activity timeline, streaks, busiest day, peak hour, and list-rate cost. Use this when you want what the page shows without re-deriving it from rows.
{
"handle": "yourname",
"since": "2026-06-17",
"updated": "2026-07-30",
"totals": { "tokens": 1234567, "events": 4321, "activeDays": 44 },
"cache": { "read": 0.83, "write": 0.05, "output": 0.10, "fresh": 0.03 },
"models": [ { "id": "claude-sonnet-4-5", "tokens": 987654 } ],
"sources": [ { "id": "claude_code", "tokens": 876543 } ],
"daily": [ { "d": "2026-07-30", "tokens": 23500000 } ]
}Shown abridged. Fields are added over time and never repurposed: if a key means something different, it gets a different name. Returns 404 no_data until a machine has synced.
Sync schema
GET/api/v1/schema
No token, no account, no tier. This is the schema the server accepts from the CLI, published so the privacy claim is checkable rather than merely stated: unknown fields are rejected, not ignored, so a future version cannot quietly start accepting a file path without the document changing. It also carries the per-plan sync intervals.
curl https://tokenstat.ai/api/v1/schema
Errors
Every error is JSON with an error code and a message written for a person. The code is the part to branch on.
| Status | Code | Means |
|---|---|---|
| 400 | invalid_request | A parameter is not one of the accepted values. |
| 401 | unauthorized | Missing, malformed, unknown, or revoked token. |
| 402 | upgrade_required | Valid token, but the plan does not include the read API. |
| 403 | forbidden | The token is real but lacks the data:read scope. |
| 404 | no_data | Nothing has been synced to this account yet. |
| 429 | rate_limited | Over 60 requests in a minute. Retry-After says when. |
402 rather than 403 for a lapsed plan is deliberate, and the token is not deleted: it starts working again on renewal, because silently destroying credentials over a billing state would be a rude way to lose someone's cron job.
Limits, and what this is not
- 60 requests a minute per token. Generous for a dashboard, tight enough that nothing here is worth scraping through.
- Your own data only. There is no endpoint that reads another account, public profile or not.
- Read-only. Nothing here can write, and nothing here can sync. Uploading is the CLI's job and needs a different token entirely.
- The history window is your plan's window. Asking for an earlier
fromgets you the earliest date your plan covers, not an error. - No webhooks and no streaming. If you want a push instead of a pull, say so and it may happen; guessing at it now would be inventing work.
The CLI is GPL-3.0 and works offline with no account at all. If everything you want is on one machine, tokenstat export and the MCP server already do this locally, for free, and you should use those instead.