Skip to main content
Lesson 9 of 12 · Chat Completions: OpenAI Compatibility

Token Usage and reasoning_tokens

Lesson 9 of 12 Updated November 3, 2026 Developer
On this page
  1. Understand token billing
  2. The standard usage object
  3. reasoning_tokens: specific to reasoning models
  4. Cost optimization strategies
  5. Key takeaways

Updated November 3, 2026. Based on the xAI Chat Completions API reference: max_tokens is deprecated in favor of max_completion_tokens, which limits only the visible response. Source: Chat Completions.

Understand token billing

Every request to the Chat Completions API is billed based on the number of tokens consumed. The usage object in the response gives you an exact count, which is essential for monitoring costs, optimizing prompts, and sizing your API budgets.

The standard usage object

Here is the structure of the usage object returned with every response:

{
  "usage": {
    "prompt_tokens": 127,
    "completion_tokens": 342,
    "total_tokens": 469
  }
}

The three basic counters

  • prompt_tokens: number of tokens in your request (system message + history + new message). This is the “input” side of billing.
  • completion_tokens: number of tokens generated by the model in its response. This is the “output” side.
  • total_tokens: sum of the previous two. This is the total billed for this request.

Impact of history on costs

Because the endpoint is stateless, history is sent with every request. This means prompt_tokens increases with each exchange:

Exchange 1 : prompt_tokens = 50   (system + 1 user message)
Exchange 2 : prompt_tokens = 150  (system + 2 user + 1 assistant)
Exchange 3 : prompt_tokens = 300  (system + 3 user + 2 assistant)
Exchange 10: prompt_tokens = 1500 (system + 10 user + 9 assistant)

For long conversations, the cost of history can quickly exceed the cost of the response itself.

reasoning_tokens: specific to reasoning models

Grok models with reasoning capabilities (such as grok-4.20-0309-reasoning) add an extra field to the usage object:

{
  "usage": {
    "prompt_tokens": 127,
    "completion_tokens": 892,
    "total_tokens": 1019,
    "completion_tokens_details": {
      "reasoning_tokens": 550
    }
  }
}

What are reasoning_tokens?

reasoning_tokens represent the tokens used by the model for its internal reasoning — the “thinking” it does before producing its final response. These tokens are generated but are not visible in the content field of the response.

In the example above:

  • The model generated 892 tokens in total (completion_tokens)
  • Including 550 internal reasoning tokens (reasoning_tokens)
  • Leaving 342 visible response tokens (892 - 550)

Why this matters

Reasoning tokens are billed like normal completion tokens. For a reasoning model, the visible response may represent only a fraction of the total cost:

usage = response.usage

visible_tokens = usage.completion_tokens - usage.completion_tokens_details.reasoning_tokens
reasoning_tokens = usage.completion_tokens_details.reasoning_tokens

print(f"Visible tokens: {visible_tokens}")
print(f"Reasoning tokens: {reasoning_tokens}")
print(f"Reasoning ratio: {reasoning_tokens / usage.completion_tokens * 100:.0f}%")

It is not uncommon for reasoning tokens to represent 50 to 80% of completion tokens for complex questions.

Cost optimization strategies

Reduce prompt_tokens

  • Summarize history: instead of sending the entire conversation, summarize older exchanges
  • Limit the system message: a 500-token system prompt is rarely necessary — aim for 100–200 tokens
  • Filter messages: send only exchanges relevant to the current question

Control completion_tokens

  • Use max_completion_tokens (max_tokens is deprecated): it limits the visible response, not reasoning
  • Specify the format: “Answer in 3 sentences” costs less than “Explain in detail”
  • Choose the right model: a non-reasoning model is less expensive if the task does not require complex thinking

Monitor costs in production

import json

def log_usage(response, requete_id: str):
    usage = response.usage
    log = {
        "id": requete_id,
        "prompt": usage.prompt_tokens,
        "completion": usage.completion_tokens,
        "total": usage.total_tokens,
    }
    if hasattr(usage, "completion_tokens_details") and usage.completion_tokens_details:
        log["reasoning"] = usage.completion_tokens_details.reasoning_tokens

    print(json.dumps(log))

Key takeaways

  • The usage object contains prompt_tokens, completion_tokens, and total_tokens
  • prompt_tokens increase with each exchange due to the history sent
  • Reasoning models add reasoning_tokens in completion_tokens_details
  • Reasoning tokens are billed at the output rate but are invisible in the response — their share of the cost depends on the task and effort, so measure it
  • Optimize by summarizing history, adjusting reasoning effort, limiting max_completion_tokens, and choosing the right model