Usage and cost
Read the usage object on each response: token counts, billable and cached tokens, the shape of the cost object, and how a stream reports usage.
Every chat response reports what the call consumed in a usage object: its
token counts and a cost object. This page documents the shape of both. Cost
amounts and rates are specific to your account and shown in your Cruxy
Console, so the examples here use placeholders where those numbers go. Read
the real values from your own responses.
# usage, with its cost object, is near the end of the response body.
curl https://api.cruxy.ai/api/v1/chat \
-H "Authorization: Bearer $CRUXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "vaani",
"messages": [
{ "role": "user", "content": "Name three monsoon fruits." }
]
}'const res = await fetch('https://api.cruxy.ai/api/v1/chat', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CRUXY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'vaani',
messages: [{ role: 'user', content: 'Name three monsoon fruits.' }],
}),
})
if (!res.ok) throw new Error(`chat failed: ${res.status} ${await res.text()}`)
const { usage, cruxy } = await res.json()
console.log(`request ${cruxy.request_id} on ${usage.cost.tier}`)
console.log(`input ${usage.input_tokens} (billable ${usage.billable_input_tokens})`)
console.log(`output ${usage.output_tokens}`)
// cost is an object, not a number: amount is the one figure, in cost.currency.
console.log(`cost ${usage.cost.amount} ${usage.cost.currency}`)The usage object
A non-streaming POST /api/v1/chat response carries usage next to message.
input_tokens, output_tokens, billable_input_tokens, and cost are always
present.
| Field | Meaning |
|---|---|
input_tokens | The call's input tokens. |
output_tokens | The tokens the model generated. |
billable_input_tokens | The input tokens the call is billed for. |
cache_read_tokens | Input tokens read from the prompt cache. |
cache_write_tokens | Input tokens written to the prompt cache. |
prompt_tokens_details | The same cache split in OpenAI's shape, cached_tokens and cache_write_tokens. Present only when the prompt cache was used. |
cost | What the call cost, as an object; see The cost object. |
The token fields have different names on different endpoints. Each set belongs to a wire contract that has already shipped, so read the one for the endpoint and mode you called:
| Endpoint and mode | Token fields | Cost |
|---|---|---|
POST /api/v1/chat, stream: false | input_tokens, output_tokens, billable_input_tokens, cache_read_tokens, cache_write_tokens | usage.cost |
POST /api/v1/chat, stream: true | prompt_tokens, completion_tokens, total_tokens, plus the three Cruxy counts when they are not zero | usage.cost, on the usage frame |
POST /api/v2/chat, either mode | input_tokens, output_tokens, billable_input_tokens, cache_read_tokens, cache_write_tokens | usage.cost |
POST /api/v1/embeddings and POST /api/v1/rerank | prompt_tokens, total_tokens | cruxy.cost |
POST /v1/chat/completions | prompt_tokens, completion_tokens, total_tokens, and prompt_tokens_details.cached_tokens when the cache was used | Not in the response |
POST /api/v1/moderations | Not in the response | Not in the response |
The cost object
cost is an object, not a number. Read cost.amount when you want one figure.
{
"tier": "vaani",
"amount": "<amount>",
"currency": "<currency>",
"input_unit": "<input rate>",
"output_unit": "<output rate>",
"cache_read_unit": "<cache read rate>"
}Each <…> is a placeholder. In a real response, currency is a string and the
other placeholders are numbers.
| Field | Meaning |
|---|---|
tier | The tier that served the call. |
amount | The modeled cost of the call, in currency. |
currency | The currency of amount. |
input_unit | The input rate amount was computed from, per million tokens. |
output_unit | The output rate, per million tokens. |
cache_read_unit | The rate for input read from the prompt cache, per million tokens: the tier's own cache-read rate where it has one, otherwise input_unit times the cache discount. |
Amounts and rates are specific to your account, and your Cruxy Console shows
them. Take cost.amount from each response rather than computing it from token
counts and a rate you copied, so your numbers match what the gateway records.
Billable and cached tokens
More goes into input_tokens than the messages you send. Each of these is
billed as input:
- The tier's own prompt. The gateway adds a per-tier system prompt and the tier's chat-template scaffolding.
- Standing rules, when they are injected into the turn; see Standing rules.
- Recalled memory, when recall runs. Sending
memory: {"recall": false}lowers input by the size of that block; see Memory. - Attached files, on every request that sends them, including later turns
that replay a message with
file_idsin its history. - Tools. The
toolsarray you send is real input; see Define tools.
billable_input_tokens is the input the call is billed for, and
cache_read_tokens and cache_write_tokens count prompt-cache activity. Input
read from the cache is priced at cost.cache_read_unit. The API does not
document how these counts relate to input_tokens, so read each one as
reported rather than deriving one from another.
Thinking is output. A reasoning tier writes its thinking inside the same
max_tokens budget as the answer, POST /api/v2/chat reports that
output_tokens includes the reasoning, and no response carries a separate
reasoning-token count. See Reasoning effort.
To size a request before you send it, POST /api/v1/tokens/count returns the
input_tokens a chat request would bill for the same tier, messages, and
tools. It calls no model and is not billed. The count is exact for single-turn
and user-only prompts on every tier, and never an undercount: where it can
differ from the real call, it errs high. It takes a tier, not "auto", and
refuses file_ids.
How a stream reports usage
Usage arrives once, at the end of a successful stream:
POST /api/v1/chatsends exactly one usage frame: the frame whosechoicesis an empty array, after the finish frame and beforedata: [DONE]. It always carriescost.billable_input_tokens,cache_read_tokens, andcache_write_tokensare left out when they are zero, andprompt_tokens_detailsappears only when the cache was used, so a missing field means zero. See Streaming.POST /api/v2/chatcarriesusageon its terminalfinishframe, in the same shape as a non-streaming v2 response. Itserrorframe can carryusagetoo.POST /v1/chat/completionssends a usage frame only when the request setsstream_options: {"include_usage": true}, and that usage has no cost.
A stream that fails partway sends no usage frame. The tokens it delivered were real, though, and are billed as a partial usage row.
What is billed
Not billed:
- A non-streaming chat that runs past its tier's deadline:
504upstream_timeout. - A model failure on chat:
502upstream_error, and the503codesupstream_overloaded,model_unavailable,all_models_unavailable,server_shutting_down, andlimits_unavailable. - A request the input moderation gate refuses:
400moderation_rejected. - A request refused for its
Cruxy-Version, for an attached file, or for the memory, rules, or files scope its key lacks: these are answered before anything is billed. - A replay of a stored success under the same
Idempotency-Key. POST /api/v1/tokens/count, and uploads toPOST /api/v1/files.
Billed:
422structured_output_unsatisfied. A retry with the sameIdempotency-Keyand body is not billed a second time.- A stream that ended early, for the tokens it delivered.
- Every case in an eval run, as a chat completion.
When a streamed request is retried under the same Idempotency-Key, it is
billed once if both attempts complete. An attempt that did not complete is
billed for the tokens it delivered, and the retry is billed as well; see
Errors and retries.
Track spend
- Spend caps and credit.
GET /api/v1/limitsreports the caps that bind your key:key_spend_capandworkspace_spend_cap, each withcap,spent,remaining,resets_at, andunit, and a headless key'scredits. See Rate limits and budgets. - Per-request tokens. An organization owner or admin can read any call's
token counts by its
request_id; see Look up a request. - Amounts. Your Cruxy Console shows what your usage costs.
Next steps
- Request IDs and metadata: the
cruxyblock and the headers on each response. - Streaming: the usage frame and the rest of the stream.
- Rate limits and budgets: spend caps, budgets, and live headroom.
- Reasoning effort: keep thinking from spending your
max_tokens.