Usage

List billed usage for an API key or its account, summarize it, and look up one request.

Authentication

These endpoints are server-to-server. Authenticate with Authorization: Bearer <API_KEY> or x-api-key. Keep the key on your backend.

A key can read account usage, not only its own calls. Keys for the same buyer share one wallet, so reconciliation is normally scope=account. scope=key is the subset for the current key. Looking up one request_id is always account-scoped, even when the call was made with another key of the same buyer.

What a row means

Rows come from billed usage only. A call that was rejected, rate-limited, or interrupted before billing has no row. Settlement is asynchronous: LLM usage usually appears within seconds, and a media task appears after the task finishes. A missing row means the call was not billed, or billing has not finished yet. Do not use this API as a failure-rate counter.

charge is the amount posted to the account for that call, in credits. official_price is the list-price total for the call; historical rows without a stored list price return null.

kind is derived from the request id. Media task ids use a tsk prefix (kind is media); LLM calls use an aitk- prefix (kind is llm). kind is an open value: a filter you do not recognize returns no rows, and a response value you do not recognize should be skipped. A media row's request_id is the task id and can be passed to GET /v1/media/tasks/{id}. Token-priced media fills tokens; per-call media reports zeros.

The reconciliation key is request_id (the x-aitoken-request-id returned by the original call, or the media task id).

Amounts are decimal strings with six fractional digits. Token counts are integers. Timestamps are UTC ISO 8601. to is exclusive.

List usage

GET/v1/account/usage

Returns billed rows for the current key or the whole account, newest first.

curl "https://hopsapi.com/v1/account/usage?scope=account&from=2026-09-22T00:00:00Z&to=2026-09-23T00:00:00Z&limit=2" \
  -H "Authorization: Bearer sk-YOUR_KEY"
NameInRequiredDescription
scopequerynokey (default) or account. Any other value is 400.
fromquerynoWindow start, ISO 8601 or unix seconds. Default is 24 hours before to.
toquerynoWindow end, exclusive. ISO 8601 or unix seconds. Default is now.
modelquerynoExact model id.
channelquerynoExact channel id, standard or flex.
kindquerynoExact kind. llm and media match those rows. Any other value matches nothing.
limitquerynoPage size, integer 1–500. Default 100.
cursorquerynoOpaque next_cursor from the previous page.

from must be earlier than to, and the window cannot be longer than 31 days.

{
  "scope": "account",
  "from": "2026-09-22T00:00:00Z",
  "to": "2026-09-23T00:00:00Z",
  "currency": "credits",
  "credits_per_usd": "100",
  "data": [
    {
      "request_id": "aitk-3f2c",
      "kind": "llm",
      "model": "claude-sonnet-5",
      "channel": "flex",
      "channel_name": "Subscription Direct",
      "tokens": {
        "input": 1834,
        "output": 412,
        "cached": 1200,
        "cache_write": 0,
        "cache_write_1h": 0
      },
      "charge": "0.263400",
      "official_price": "0.752571",
      "status": 200,
      "key": { "id": 1042, "name": "prod-us", "key_masked": "sk-abcd…9f3c" },
      "created_at": "2026-09-22T13:05:41Z"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJ0IjoiMjAyNi0wOS0yMlQxMjo1ODowM1oiLCJpIjo3MTIzNDV9"
}

scope=account includes historical rows from deleted or disabled keys. Each row includes key.id, key.name, and key.key_masked.

Cursor pagination

Results are ordered by created_at descending, then id descending. Pass next_cursor as cursor to read the next page. next_cursor is null when there are no further rows. Do not use an offset: the cursor is anchored to a row, so rows inserted after you start paging do not shift later pages.

curl "https://hopsapi.com/v1/account/usage?scope=account&cursor=eyJ0IjoiMjAyNi0wOS0yMlQxMjo1ODowM1oiLCJpIjo3MTIzNDV9" \
  -H "Authorization: Bearer sk-YOUR_KEY"

A modified cursor returns 400.

Summary

GET/v1/account/usage/summary

Aggregates the same billed rows as the list endpoint over the same window and filters.

curl "https://hopsapi.com/v1/account/usage/summary?scope=account&from=2026-09-22T00:00:00Z&to=2026-09-23T00:00:00Z&group_by=model" \
  -H "Authorization: Bearer sk-YOUR_KEY"
NameInRequiredDescription
scopequerynoSame as the list endpoint. Default key.
fromquerynoSame window rules as the list endpoint.
toquerynoSame window rules as the list endpoint.
modelquerynoExact model id.
channelquerynoExact channel id.
kindquerynoExact kind. An unrecognized value matches nothing.
group_byquerynoday (default), model, or key. Any other value is 400.

day buckets are UTC dates (YYYY-MM-DD) and omit empty days. model buckets are (model, channel). key buckets include key. With scope=key that is one bucket for the current key. total is the whole window, not just one bucket. Summing every page of the list endpoint matches total for calls, each token counter, and charge.

{
  "scope": "account",
  "from": "2026-09-22T00:00:00Z",
  "to": "2026-09-23T00:00:00Z",
  "group_by": "model",
  "currency": "credits",
  "credits_per_usd": "100",
  "data": [
    {
      "model": "claude-sonnet-5",
      "channel": "flex",
      "calls": 1830,
      "tokens": {
        "input": 3120044,
        "output": 611203,
        "cached": 1988000,
        "cache_write": 0,
        "cache_write_1h": 0
      },
      "charge": "482.113200"
    }
  ],
  "total": {
    "calls": 2011,
    "tokens": {
      "input": 3200000,
      "output": 640000,
      "cached": 1988000,
      "cache_write": 0,
      "cache_write_1h": 0
    },
    "charge": "540.220100"
  }
}

One request

GET/v1/account/usage/{request_id}

Returns one billed row for the current buyer's account. The body matches one object from the list endpoint.

NameInRequiredDescription
request_idpathyesThe original x-aitoken-request-id, or a media task id.
curl https://hopsapi.com/v1/account/usage/aitk-3f2c \
  -H "Authorization: Bearer sk-YOUR_KEY"

404 means there is no billed row for that id on this account. That includes an unknown id, another buyer's id, and a call whose settlement has not finished. Retry a recent id before treating it as absent. The response does not say which of those cases it is.

Errors

Every response includes x-aitoken-request-id. These endpoints do not use LLM account limits. The three usage routes share one per-key limit of 120 requests per minute. A rejection is 429 with draft-6 RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset. See Rate limits.

HTTP statuserror.typeerror.codeMeaning
400invalid_request_errorVALIDATIONBad scope, window, filter, limit, group_by, or cursor. A window over 31 days says so.
401authentication_errorThe key is missing, invalid, or inactive. The body matches GET /v1/models.
404not_foundusage_not_foundNo billed row for this account. It may not be settled yet.
429rate_limit_errorusage_rate_limitThe key exceeded 120 usage requests in the current minute. Use the RateLimit-* headers.
503internal_errorusage_unavailableUsage could not be read. The body includes error.request_id. An empty data array is not a read failure.
On this page

On this page