Usage & Cost
Every billable Onlist response reports token counts in a standard OpenAI usage object. Cost, however, is handled differently than on OpenRouter: it is out of band, not in the response body. This page covers what the usage object contains, where cost actually lives, and how the prepaid wallet settles each request.
The usage object
On /v1/chat/completions, /v1/completions, /v1/responses, and /v1/messages, the response carries a usage object with the familiar token counts. These three fields are always present:
| Field | Type | Description |
|---|---|---|
prompt_tokens | integer | Tokens in the input (messages, system prompt, tools). |
completion_tokens | integer | Tokens generated in the response. |
total_tokens | integer | prompt_tokens + completion_tokens. |
Two optional breakdown objects appear when the upstream model reports them:
| Field | Description |
|---|---|
prompt_tokens_details.cached_tokens | Prompt tokens served from the upstream prompt cache. |
prompt_tokens_details.audio_tokens | Audio tokens in the input. |
completion_tokens_details.reasoning_tokens | Hidden reasoning tokens (o-series, reasoning models). |
completion_tokens_details.audio_tokens | Audio tokens in the output. |
completion_tokens_details.accepted_prediction_tokens | Predicted-output tokens that matched. |
completion_tokens_details.rejected_prediction_tokens | Predicted-output tokens that were discarded. |
Sample usage JSON
The usage object is part of an otherwise standard chat-completion response body:
{
"id": "chatcmpl-9x2k...",
"object": "chat.completion",
"created": 1748563200,
"model": "gpt-4o-2024-08-06",
"choices": [],
"usage": {
"prompt_tokens": 1024,
"completion_tokens": 256,
"total_tokens": 1280,
"prompt_tokens_details": {
"cached_tokens": 768,
"audio_tokens": 0
},
"completion_tokens_details": {
"reasoning_tokens": 0,
"audio_tokens": 0,
"accepted_prediction_tokens": 0,
"rejected_prediction_tokens": 0
}
}
}
When you stream (stream: true), the usage object arrives in the final chunk before data: [DONE], not on every chunk. Onlist forces stream_options.include_usage to true so that terminal usage chunk is always emitted. See Streaming.
Cost is out of band
If you are coming from OpenRouter, the key difference is that Onlist does not add a cost field to usage. The usage object reports tokens only. Cost is resolved separately, after the request, against the platform settlement record.
usage.cost is not returned in the response body. Unlike OpenRouter, Onlist never injects a per-request cost into usage. Do not parse usage.cost: it will be absent. Read cost out of band from the usage log, as described below.
The reason is the platform model: a request is priced and settled against a specific provider's listing, and that settlement is the source of truth for what you were charged. Keeping cost out of the response body keeps the response byte-faithful to the upstream provider and the OpenAI schema.
How to find cost
Cost is recorded per request in your usage log, next to the provider that served it. One lookup answers both "who served this" and "what did it cost" — there is no correlation step.
In the dashboard
Usage Logs lists one row per call with the provider, the model, tokens in and out, latency and time-to-first-token, and the cost in USD. Opening a row adds the request id, the billing mode, and the unit prices that applied to that call.
Programmatically
Four endpoints, differing in what they authenticate with and what they answer:
| Endpoint | Authenticates with | Answers |
|---|---|---|
GET /api/log/token | the API key itself | that key's latest 1000 entries, one row per call |
GET /api/v1/generation | the API key itself | one call, by request id — what it cost and who served it |
GET /api/v1/key | the API key itself | that key's lifetime, daily, weekly and monthly spend |
GET /api/v1/activity | a management key | last 30 complete UTC days, grouped by day × model × provider |
| Usage Logs page | your browser login | full history, pagination, filtering, per-call details |
Each key sees only its own calls, so issuing a separate key per downstream tenant keeps
their logs apart. The /api/v1/ endpoints are the OpenRouter-compatible surface; see
Management API for the full reference.
curl -sS https://onlist.io/api/log/token \
-H "Authorization: Bearer YOUR_ONLIST_API_KEY"
Each entry carries exactly these fields:
| Field | Description |
|---|---|
cost | What you were charged for the call, in USD |
model_name | The model that served the request |
other.provider_name / other.provider_id | Which upstream provider served it |
prompt_tokens / completion_tokens | Token counts, matching the response usage object |
other.cache_tokens | Prompt tokens served from the upstream cache, when the model reports it |
use_time / other.frt | Total seconds, and time to first token in milliseconds (streaming only) |
request_id / created_at | Request identifier and Unix timestamp |
token_name / type / is_stream | Key name, log type, and whether the call was streamed |
other.error_code / other.status_code | On a failed call, the error code and HTTP status |
other is a JSON object, present only when at least one of its fields applies.
This response is a fixed reporting projection — the fields above and nothing else. Account identifiers, source IPs, internal channel data and free-form diagnostic text are deliberately excluded, because an API key travels further than an account credential: it gets embedded in downstream services, CI configuration and clients. For the complete record, use Usage Logs in the dashboard.
cost is a plain USD amount, the same unit as your balance and the prices on
every model page. There is no conversion step and no rate to look up. If you are
coming from OpenRouter, it is the equivalent of total_cost on a generation.
Charges are metered in whole units of $0.000002, so a call whose true cost falls
below that is billed at $0.000002 rather than rounded away. A cost of exactly
0 means the call genuinely cost nothing: a listing priced at zero, or a call
that produced no tokens.
/api/log/token, /api/status and everything under /api/v1/ are served from
https://onlist.io only. The China acceleration endpoint (https://api.onlist.net)
relays inference traffic and answers 403 for these paths. Point your inference
client at whichever endpoint suits you, and keep reporting calls on the main domain.
Per-call cost by request id
Every Onlist response carries an X-Oneapi-Request-Id header — the same value that
appears as request_id in /api/log/token. Feed it to /api/v1/generation to get one
call's cost, provider, latency and token counts:
curl -sS "https://onlist.io/api/v1/generation?id=THE_REQUEST_ID" \
-H "Authorization: Bearer YOUR_ONLIST_API_KEY"
total_cost (and its alias usage) is the USD amount you were charged — OpenRouter's
total_cost on a generation, same field name and same unit. Full field list in the
Management API.
Route ids explain routing, not cost
Every Onlist response (streaming and non-streaming) also includes an
X-Onlist-Route-Id response header. It is an opaque UUID identifying the routing
decision for that request. On streams it is written before the first SSE byte.
/api/v1/generation takes
If you are looking up what a call cost, use X-Oneapi-Request-Id (see above).
X-Onlist-Route-Id identifies the routing decision, not the log entry, and is the
id used by the routing-explanation endpoints.
X-Onlist-Route-Id is not the body id field. The body id is the upstream
completion id (for example chatcmpl-...); the route id is Onlist's own
identifier for the routing decision.
curl -sS -D - -o /dev/null https://onlist.io/v1/chat/completions \
-H "Authorization: Bearer YOUR_ONLIST_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o",
"messages": [{"role": "user", "content": "Hello"}]
}' | grep -i x-onlist-route-id
To see how a request was routed — which provider was selected, which listing, what the candidate pool looked like, and every failover attempt — open that call in Usage Logs in the dashboard. The route id identifies that record.
Route inspection is a signed-in surface, not a programmatic one: it is served to your browser session, and API keys do not authenticate against it.
Route records explain routing, not cost. They carry no charge, and the usage log has no route-id column to join against. For per-request cost, read the usage log directly: it already names the provider, so nothing needs to be correlated. Reach for route ids when you want to understand why a request went where it did.
For the full list of Onlist response headers and their semantics, see Limits & headers.
Billing model
Onlist billing is prepaid. There is no monthly invoice and no post-paid credit line.
-
Top up your wallet. You add funds to a balance held in your buyer account.
-
Send requests. Before a request is relayed, Onlist estimates its cost and checks your balance. If the balance is below the estimate, the request is rejected with
402 insufficient_balancerather than going through. -
Funds are held in escrow. While a request is in flight, the estimated amount is reserved. Escrow is the platform mechanism that keeps a provider from disappearing with prepaid balances: funds are not released to the provider until the request settles.
-
Each request settles and debits the wallet. On settlement, the actual cost (computed from the
usagetoken counts and the provider's listing price) is debited from your balance, and the entry appears in your usage log with the provider that served it.
Because settlement needs the terminal usage chunk, streaming requests always run with stream_options.include_usage forced to true. You cannot opt out. See Streaming.
Next steps
- Streaming: How usage arrives in the final SSE chunk and why include_usage is forced true.
- Limits & headers: X-Onlist-Route-Id, X-Onlist-Warnings, Retry-After, and rate-limit behavior.
- Errors: The insufficient_balance and quota_exhausted 402 codes, and the full error envelope.
- OpenAPI spec: Exhaustive request and response schemas, including the usage object (YAML download).