Skip to main content

Usage Query API

GET /v1/usage returns your organisation's cost and usage aggregates as JSON rows: live figures for dashboards and meters, and historical breakdowns for pulling into spreadsheets, BI tools, or your own systems.

GET https://api.aispendops.com/v1/usage
Authorization: Bearer aso_agt_...

Authentication uses an access token (aso_agt_…, created under Settings → Access Tokens) with the read:usage scope. Responses are aggregates only, computed from pre-aggregated rollups: fast, cheap, and safe to poll.

Quick examples

Spend this month, as one number:

curl "https://api.aispendops.com/v1/usage?metric=cost_usd&window=month_to_date" \
-H "Authorization: Bearer $ASO_ACCESS_TOKEN"

Spend broken down by your feature dimension for March:

curl "https://api.aispendops.com/v1/usage?metric=cost_usd&dimension=dim&dim_key=feature&window=month&month=2026-03" \
-H "Authorization: Bearer $ASO_ACCESS_TOKEN"

A year of monthly spend per provider, one call:

curl "https://api.aispendops.com/v1/usage?metric=cost_usd&dimension=provider&window=monthly_series&months=12" \
-H "Authorization: Bearer $ASO_ACCESS_TOKEN"

Which models each value of your feature dimension used this month, with cost, request count, and tokens per row (made for BI extracts):

curl "https://api.aispendops.com/v1/usage?metric=cost_usd&dimension=dim_model&dim_key=feature&window=month_to_date&limit=1000" \
-H "Authorization: Bearer $ASO_ACCESS_TOKEN"

Response shape

{
"asOf": "2026-08-08T14:23:11Z",
"rows": [
{ "label": "openai", "value": 2103.40 },
{ "label": "anthropic", "value": 1240.15 }
]
}
  • label: the group (provider name, model, API key id, dimension value), or "total" for single-number queries. API key breakdowns return the key id (aso_k_...), not its display name; resolve names via your own records if you need them.
  • value: USD for cost_usd, a count for requests and tokens.
  • bucket: always present, but only non-null on monthly_series rows, where it carries the month ("2026-03-01").
  • model, requests, tokens: always present, but only non-null on dimension=dim_model rows. There each row is one (dimension value, model) pair: label is the dimension value, model is the model id, value is that pair's cost in USD, and requests and tokens are its request and token counts.

A dim_model row looks like this:

{ "label": "checkout", "model": "claude-sonnet-5", "value": 2.55, "bucket": null, "requests": 103, "tokens": 1030207 }

Parameters

ParameterRequiredDescription
metricYescost_usd, requests, or tokens
windowYesTime window, see below
dimensionNoGroup results: provider, model, api_key, dim, dim_model, or day
dim_keyWith dimension=dim or dim_modelWhich custom dimension to break down by (for example feature, team)
monthWith window=monthCalendar month as YYYY-MM
monthsWith window=monthly_seriesHow many months back, 1 to 24 (default 12)
limitNoRows for breakdowns, 1 to 1000 (default 5), largest first
api_keyNoFilter any cost query to one API key id

Windows

All windows are UTC, matching the dashboards: today is the current UTC day, month_to_date is the current UTC calendar month so far.

windowMeaning
month_to_dateCurrent month so far
last_monthThe previous calendar month
todayCurrent UTC day
yesterdayPrevious UTC day
last_7_days / last_30_daysRolling daily range, used with dimension=day for trends
monthAny specific past month (&month=2026-03)
monthly_seriesMonth-by-month trend, up to 24 months

Supported combinations

Queries are validated against a fixed catalogue; anything outside it returns 400 with code unknown_catalog_tuple. This is what keeps the API fast and safe to poll.

MetricDimensionWindows
cost_usdnone (total)month_to_date, last_month, today, yesterday, month
cost_usdprovidermonth_to_date, today, month, monthly_series
cost_usdmodelmonth_to_date, month
cost_usdapi_keymonth_to_date, today, month
cost_usddimmonth_to_date, month
cost_usddim_modelmonth_to_date, month
cost_usddaylast_7_days, last_30_days
requestsnone (total)today, month_to_date
tokensnone (total)month_to_date

dimension=api_key additionally requires the read:keys scope, because per-key spend reveals how your organisation's keys are used.

Freshness and caching

Results are served from a short server-side cache, so polling does not multiply query cost:

WindowTypical freshness
today totals and breakdownswithin 60 seconds
Month-to-date and monthlywithin 5 minutes
Closed months (immutable)cached up to 1 hour

Poll as often as you like within your rate limit; identical queries inside the cache window return the same snapshot.

Limits and errors

  • Rate limit: 60 requests per minute per token. Exceeding it returns 429 with a Retry-After header.
  • Responses use the standard error shape: { "error": "...", "code": "..." }.
StatusCodeMeaning
400unknown_catalog_tupleThe metric, dimension, window combination is not supported
400Invalid parameter (bad month, months out of range, missing dim_key)
401Missing, invalid, expired, or revoked token
403scope_deniedToken lacks a required scope (for example read:keys)
429Rate limited, honour Retry-After

Notes

  • Figures match the AI SpendOps dashboards: same data, same UTC day and month boundaries.
  • Breakdowns return the top rows by value; raise limit (up to 1000) to fetch a complete breakdown of a low-cardinality dimension.
  • Dimension breakdowns (dim and dim_model) cover only requests tagged with that dimension; untagged traffic is not included. Totals and provider, model, and API key breakdowns cover everything.
  • Costs are USD.