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:

FieldTypeDescription
prompt_tokensintegerTokens in the input (messages, system prompt, tools).
completion_tokensintegerTokens generated in the response.
total_tokensintegerprompt_tokens + completion_tokens.

Two optional breakdown objects appear when the upstream model reports them:

FieldDescription
prompt_tokens_details.cached_tokensPrompt tokens served from the upstream prompt cache.
prompt_tokens_details.audio_tokensAudio tokens in the input.
completion_tokens_details.reasoning_tokensHidden reasoning tokens (o-series, reasoning models).
completion_tokens_details.audio_tokensAudio tokens in the output.
completion_tokens_details.accepted_prediction_tokensPredicted-output tokens that matched.
completion_tokens_details.rejected_prediction_tokensPredicted-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
    }
  }
}
Note

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.

Caution

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:

EndpointAuthenticates withAnswers
GET /api/log/tokenthe API key itselfthat key's latest 1000 entries, one row per call
GET /api/v1/generationthe API key itselfone call, by request id — what it cost and who served it
GET /api/v1/keythe API key itselfthat key's lifetime, daily, weekly and monthly spend
GET /api/v1/activitya management keylast 30 complete UTC days, grouped by day × model × provider
Usage Logs pageyour browser loginfull 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:

FieldDescription
costWhat you were charged for the call, in USD
model_nameThe model that served the request
other.provider_name / other.provider_idWhich upstream provider served it
prompt_tokens / completion_tokensToken counts, matching the response usage object
other.cache_tokensPrompt tokens served from the upstream cache, when the model reports it
use_time / other.frtTotal seconds, and time to first token in milliseconds (streaming only)
request_id / created_atRequest identifier and Unix timestamp
token_name / type / is_streamKey name, log type, and whether the call was streamed
other.error_code / other.status_codeOn a failed call, the error code and HTTP status

other is a JSON object, present only when at least one of its fields applies.

Note

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.

Caution

/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.

Note

/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.

Note

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.

Important

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.

  1. Top up your wallet. You add funds to a balance held in your buyer account.

  2. 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_balance rather than going through.

  3. 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.

  4. Each request settles and debits the wallet. On settlement, the actual cost (computed from the usage token 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.

Caution

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).