Get Usage
Retrieve request-level usage history for an account and everything beneath it, newest first. This is the endpoint to reconcile against an invoice.
https://console-api.siraya.ai/extapi/v1/usage
Query Parameters
2026-09-01 is accepted and read as UTC midnight.
GET /accounts — is used. An id
outside your subtree returns 404.
import requests
url = "https://console-api.siraya.ai/extapi/v1/usage"
headers = {"Authorization": "Bearer <CONSOLE_TOKEN>"}
params = {
"start": "2026-09-01",
"end": "2026-09-08",
"page_size": 20,
}
response = requests.get(url, headers=headers, params=params)
body = response.json()["data"]
for record in body["data"]:
print(record["request_id"], record["model"], record["cost"])
Example Response
{
"isSuccess": true,
"data": {
"data": [
{
"request_id": "req_01J8XK2P9V",
"timestamp": "2026-09-08T02:24:07Z",
"account_id": "11111111-2222-3333-4444-555555555555",
"model": "gpt-5.4",
"usage": {
"prompt_tokens": 10,
"completion_tokens": 20,
"reasoning_tokens": 5,
"cache_read_tokens": 3,
"cache_write_tokens": 2,
"total_tokens": 35
},
"cost": 0.000091,
"currency": "USD",
"status": "success",
"performance": {
"latency_ms": 1200,
"ttft_ms": 300
}
}
],
"page": 1,
"page_size": 20,
"has_more": true
}
}
Response fields
Each record in data.data carries:
| Field | Type | Meaning |
|---|---|---|
request_id |
string | The request's identifier. Pass it to Get a single record |
timestamp |
string | When the request was made, ISO-8601 in UTC |
account_id |
string | The account that made the request — see Sub-account attribution |
model |
string | The model code the request ran against |
cost |
number | The billed amount, after discounts, to 6 decimal places — see How to read cost |
currency |
string | The currency cost is expressed in |
status |
string | The outcome of the request. A request that completed reports success |
usage |
object | Token counts, below |
performance |
object | Timings, below |
usage
| Field | Meaning |
|---|---|
prompt_tokens |
Tokens in the request |
completion_tokens |
Tokens generated in the reply |
reasoning_tokens |
Tokens spent on reasoning, reported separately so they are not counted twice |
cache_read_tokens |
Prompt tokens served from cache |
cache_write_tokens |
Prompt tokens written to cache |
total_tokens |
Sum of the above excluding reasoning_tokens — see How to read total_tokens |
Field names compared with inference responses
This record uses different names from the usage object returned by /v1/chat/completions:
| This record | Chat Completions usage |
|---|---|
prompt_tokens |
prompt_tokens |
completion_tokens |
completion_tokens |
reasoning_tokens |
completion_tokens_details.reasoning_tokens |
cache_read_tokens |
prompt_tokens_details.cached_tokens |
cache_write_tokens |
prompt_tokens_details.cache_creation_tokens |
total_tokens (excludes reasoning_tokens, see below) |
total_tokens (prompt_tokens + completion_tokens) |
In a Chat Completions response, prompt_tokens already includes cached and cache-written tokens, and completion_tokens already includes reasoning tokens. Do not add the cache counts to it when reconciling by hand; use cost here for the billed amount.
performance
| Field | Meaning |
|---|---|
latency_ms |
Total time to complete the request, in milliseconds |
ttft_ms |
Time to first token, in milliseconds |
Limits on the window
| Limit | Value | What to do instead |
|---|---|---|
| Maximum time range | 31 days | Page through the month in several calls |
| Maximum offset | 10,000 records | Narrow the window with start / end rather than paging deeper |
Both are enforced with 400. The 31-day cap covers the monthly reconciliation
this endpoint exists for.
Paginate with has_more
There is no total. An exact count means a full scan of the matching set on
every page, for a number that cannot change between pages of the same filter.
Page until has_more is false.
How to read cost
cost is the billed figure — after discounts — rounded to 6 decimal places,
matching the billing standard so this endpoint reconciles against the invoice.
It is deliberately not the pre-discount figure.
How to read total_tokens
total_tokens excludes reasoning_tokens, matching the console dashboard.
Reasoning is reported on its own field so it can be reconciled without being
counted twice.
Sub-account attribution
account_id on each record is the account that made the request. For a
limit_quota sub-account that is the sub-account itself, not the root that
funds it. One request produces exactly one record.
Get a single record
https://console-api.siraya.ai/extapi/v1/usage/{request_id}
Returns the same record shape, not wrapped in a list.
curl https://console-api.siraya.ai/extapi/v1/usage/req_01J8XK2P9V \
-H "Authorization: Bearer <CONSOLE_TOKEN>"
A request_id outside your account subtree returns 404, not 403 — so the
endpoint cannot be used to confirm that another tenant's request exists.
Errors
| Status | When |
|---|---|
| 400 | Unparseable start / end, end at or before start, range over 31 days, or offset past the cap |
| 401 | Token missing, malformed, revoked, expired or unknown |
| 403 | The issuing user lacks res.request-logs, or their membership is inactive |
| 404 | request_id or account_id not found within your subtree |
| 429 | Per-user rate limit, returned with error.code RATE_LIMIT_EXCEEDED |