Usage export
Row-level metered usage, one row per request, in JSON or CSV.
GET /v1/usage/export returns the underlying usage rows rather than aggregates
— one row per request, each carrying the price that was stamped on it. It exists
so metering accuracy can be checked outside the product, before any billing
depends on it.
curl "https://api.inference.runatlas.com/v1/usage/export?format=csv&start=2026-09-01&end=2026-10-01" \
-H "Authorization: Bearer $ATLAS_READ_KEY" \
-o atlas-usage-september.csvAuthenticates with a console session or a read-scoped
key. It accepts the same start, end,
model, and api_key_id filters as GET /v1/usage, with
the same 93-day period ceiling.
Two formats, one row definition
?format=csvStreamed as text/csv, walked with an internal cursor and written as it
goes, so a full month does not have to be held in memory at either end. The
filename is set from the organization and period.
CSV does not accept a cursor — it paginates internally and returns the
whole period in one response. Passing one is refused with
cursor_not_supported.
Both formats are generated from a single row definition, so a figure that
reconciles in one reconciles in the other. The columns array in the JSON
response is the CSV header, in the same order.
The columns
| Column | |
|---|---|
occurred_at | ISO 8601 timestamp. |
request_id | The same id returned as x-request-id. |
organization_id | |
api_key_id | Which key made the request. |
model_identifier | e.g. atlas-mid-1. |
model_id, model_version_id | Internal ids; model_version_id pins the exact weights served. |
outcome | How the request ended. |
streamed | Boolean. |
shape | plain, tool_calling, or structured_output. |
http_status, error_code | For failed requests. |
| Column | |
|---|---|
backend_reported_input_tokens | The meter of record. |
backend_reported_output_tokens | The meter of record. |
backend_reported_reasoning_tokens | Counted inside the output figure. Never add them. |
generated_output_tokens | What the model produced. |
delivered_output_tokens | What reached the client. Differs on a disconnect. |
atlas_counted_input_tokens | Atlas's independent count — null unless this row was sampled. |
atlas_counted_output_tokens | Same. |
reconciliation, reconciliation_divergence | The comparison outcome for a sampled row. |
system_fingerprint | The backend's fingerprint, where it reports one. |
| Column | |
|---|---|
input_price_per_mtok, output_price_per_mtok | The price in force when this row was written. |
price_currency | |
input_cost, output_cost, total_cost | Computed from the stamped price above. |
cost_provisional | Always true during the beta. |
| Column | |
|---|---|
time_to_first_token_ms | |
total_duration_ms |
Reconciling it yourself
The prices are on every row precisely so the cost columns can be recomputed rather than believed:
input_cost == backend_reported_input_tokens / 1e6 * input_price_per_mtok
output_cost == backend_reported_output_tokens / 1e6 * output_price_per_mtokThree things to get right when you do:
- Do not add
backend_reported_reasoning_tokensto the output figure. It is already inside it. - Price on
backend_reported_*, not ongenerated_ordelivered_. The generated and delivered figures answer "what happened to the stream", not "what was metered". - Do not reprice with today's published price. Rows written before a price change carry the older price, and that is intentional.
Cursors
A JSON cursor encodes a position — a timestamp and a request id — and nothing
about who is reading. A cursor handed to another account selects a position in
their data, which is to say nothing of yours. An unparseable cursor is refused
with invalid_cursor.
Rows are ordered by occurred_at then request_id, so paging is stable even as
new rows arrive behind you.