Skip to content

Reasoning

A reasoning model writes out its reasoning before it answers. These models list reasoning in capabilities on GET /v1/models. Reasoning tokens are billed as output tokens, and usage counts them separately as reasoning_tokens.

The reasoning comes back beside content under two names, reasoning_content and reasoning, which always hold the same text. Engines and client libraries disagree on the name, so every model returns both. Read whichever one your client expects.

"message": {
"role": "assistant",
"content": "391",
"reasoning_content": "17 × 23 = 17 × 20 + 17 × 3 = 340 + 51 = 391.",
"reasoning": "17 × 23 = 17 × 20 + 17 × 3 = 340 + 51 = 391."
}

When streaming, the reasoning arrives first as deltas carrying both fields, then the answer as delta.content:

data: {"choices":[{"index":0,"delta":{"reasoning_content":"17 × 23","reasoning":"17 × 23"},"finish_reason":null}],...}
data: {"choices":[{"index":0,"delta":{"content":"391"},"finish_reason":null}],...}
stream = client.chat.completions.create(
model="moonshotai/kimi-k3",
messages=[{"role": "user", "content": "What is 17 × 23?"}],
stream=True,
)
for chunk in stream:
if not chunk.choices:
continue
delta = chunk.choices[0].delta
reasoning = getattr(delta, "reasoning_content", None)
if reasoning:
print(reasoning, end="")
if delta.content:
print(delta.content, end="")

You don’t need to send the reasoning back in later turns. If an assistant turn you send fills both fields, reasoning_content is the one used.

The reasoning is an output item of type reasoning, ahead of the message. Its text is in content[].text (type reasoning_text) when the model returns it; summary[].text holds a summary instead, when one is produced. Usage counts it in output_tokens_details.reasoning_tokens.

Ask for the reasoning with thinking: { "type": "enabled" }, or "adaptive", which is accepted identically. It comes back as a thinking content block ahead of the text; when streaming, it arrives in content_block_delta events whose delta.type is thinking_delta. Without it you get the answer alone, unless the model put its whole reply inside the reasoning; that turn comes back as an ordinary text block. See Messages.

Send a level in reasoning_effort on Chat Completions, reasoning.effort on Responses, and reasoning_effort or output_config.effort on Messages. Each model lists the levels it takes in reasoning_efforts on GET /v1/models. The applied level comes back in X-Reasoning-Effort. How each endpoint validates the level is described on its own page.