API reference
Usage events
GET /v1/usage: one metered event per request, with cursor pagination and time filters.
Returns one event per metered request on your key's allocation, newest first. These are the same events the supplier sees on their side of the agreement.
/v1/usageRequires the usage:read scope.
Query parameters
| Parameter | Type | Notes |
|---|---|---|
from | ISO 8601 | Only events at or after this time |
to | ISO 8601 | Only events before this time |
limit | integer | Page size. Defaults to 50, capped at 100 |
cursor | string | next_cursor from the previous page |
A from or to that isn't a valid date is ignored.
Example
curl "https://cairva.xyz/v1/usage?from=2026-11-01T00:00:00Z&to=2026-11-02T00:00:00Z&limit=100" \
-H "Authorization: Bearer $CAIRVA_API_KEY"async function allUsage(from, to) {
const events = [];
let cursor = null;
do {
const url = new URL("https://cairva.xyz/v1/usage");
url.search = new URLSearchParams({ from, to, limit: "100", ...(cursor ? { cursor } : {}) });
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.CAIRVA_API_KEY}` },
});
const page = await res.json();
events.push(...page.data);
cursor = page.next_cursor;
} while (cursor);
return events;
}import os
import requests
def all_usage(start, end):
events, cursor = [], None
while True:
params = {"from": start, "to": end, "limit": 100}
if cursor:
params["cursor"] = cursor
page = requests.get(
"https://cairva.xyz/v1/usage",
params=params,
headers={"Authorization": f"Bearer {os.environ['CAIRVA_API_KEY']}"},
).json()
events += page["data"]
cursor = page["next_cursor"]
if not cursor:
return eventsResponse
{
"object": "list",
"data": [
{
"id": "0f6b8a52-3c1e-4e0b-9a77-1d2c3b4a5e6f",
"occurred_at": "2026-11-01T09:14:02.511Z",
"request_id": "7d0c2f4e-6a51-4c43-9f0e-2b8f5a1c9e30",
"upstream_request_id": "chatcmpl-582",
"client_request_id": "ticket-4812",
"input_tokens": 31,
"output_tokens": 18,
"source": "provider_reported",
"finality": "observed"
}
],
"next_cursor": null
}next_cursor is null on the last page.
Fields
| Field | Meaning |
|---|---|
id | The usage event id |
occurred_at | When the usage was booked |
request_id | Matches cairva.request_id on the completion |
upstream_request_id | The provider's id for the request, when it sent one |
client_request_id | The x-request-id header you sent, if any |
input_tokens, output_tokens | Booked token counts |
source | Where the counts came from |
finality | How settled the counts are |
source
| Value | Meaning |
|---|---|
provider_reported | The model provider returned the counts with the response |
reconciliation | Cairva operations booked the counts later from provider metering, with evidence |
sandbox_estimate | A stand-in sandbox route that calls no model estimated the counts |
finality
Events start as observed. Events booked by reconciliation are confirmed. The field also defines corrected and disputed. Nothing sets either today, but handle them so your code doesn't break if it starts to.
What isn't here
A request appears only once its usage is booked. Failed requests never appear and never consume allowance. Requests waiting on usage (usage_source: "pending" on the response) appear after operations reconcile them.