# Usage events

> GET /v1/usage: one metered event per request, with cursor pagination and time filters.

Source: https://cairva.xyz/docs/usage

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.

```endpoint
GET /v1/usage
```

Requires 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

```bash curl
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"
```

```javascript JavaScript
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;
}
```

```python Python
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 events
```

## Response

```json
{
  "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.
