Skip to content
kenari.

Reasoning

Some models think before they answer. They produce a reasoning trace first, then the final text. kenari gives you one set of parameters for this on every chat format, returns the trace next to the answer, and bills the thinking as Output tokens. More thinking usually means better answers on hard problems. It can also mean a slower reply and more tokens, which cost money wherever Output tokens are billed.

GET /v1/models describes reasoning for each model. The call is public, so you can filter it without a key:

Terminal window
curl -s https://kenari.id/v1/models | jq -r '.data[] | select(.reasoning == true) | .id'
FieldMeaning
reasoningtrue when the model can think.
reasoning_optionsThe effort levels the model accepts, for example ["low", "medium", "high"]. Absent when the model has no effort levels.
reasoning_toggletrue when you can switch reasoning on or off per request.

The levels are none, minimal, low, medium, high, xhigh and max. none turns reasoning off, and minimal is treated as low. If you ask for a level the model does not list, kenari uses the highest listed level at or below it. If the model lists no level at or below the one you asked for, the level you asked for is sent unchanged. On a model that does not support reasoning, the reasoning fields are ignored.

Each format has its own field. kenari maps all of them to the native reasoning control of the model’s provider.

FormatHow to ask
Chat completionsreasoning_effort, or the reasoning object below
Messagesthinking: {"type": "enabled", "budget_tokens": N}, or output_config: {"effort": "high"}
Responsesreasoning: {"effort": "high"}

With Chat completions:

Terminal window
curl https://kenari.id/v1/chat/completions \
-H "Authorization: Bearer $KENARI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "step-3-7-flash:free",
"reasoning_effort": "medium",
"messages": [
{"role": "user", "content": "What is 17 * 24? Show the steps."}
]
}'

With Messages. Point the Anthropic SDK at https://kenari.id, without /v1:

import os
import anthropic
client = anthropic.Anthropic(
base_url="https://kenari.id",
api_key=os.environ["KENARI_API_KEY"],
)
message = client.messages.create(
model="step-3-7-flash:free",
max_tokens=4096,
thinking={"type": "enabled", "budget_tokens": 2048},
messages=[{"role": "user", "content": "What is 17 * 24? Show the steps."}],
)
for block in message.content:
if block.type == "thinking":
print("Reasoning:", block.thinking)
elif block.type == "text":
print("Answer:", block.text)

thinking: {"type": "disabled"} turns reasoning off on Messages only for models served through an Anthropic-style API. For other models it removes the effort setting, and the model’s default applies. To turn reasoning off on a model that supports it (reasoning_toggle is true), use reasoning: {"enabled": false} on Chat completions. A budget must be at least 1,024 and below max_tokens. At a max_tokens of 1,024 or less, models served through an Anthropic-style API get no thinking at all.

Chat completions also accepts an OpenRouter-style reasoning object, which you can send on every chat model:

FieldTypeDescription
effortstringOne of the levels above.
enabledbooleanfalse turns reasoning off. true without effort or max_tokens turns it on at medium.
max_tokensintegerA token budget for the thinking. Models that take a level instead of a budget may ignore it, so prefer effort.
excludebooleantrue hides the trace from the response. The thinking is still generated and billed.

When a request has both reasoning and reasoning_effort, the reasoning object wins.

Terminal window
curl https://kenari.id/v1/chat/completions \
-H "Authorization: Bearer $KENARI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "step-3-7-flash:free",
"reasoning": {"enabled": false},
"messages": [
{"role": "user", "content": "In one word, what is the capital of Indonesia?"}
]
}'

kenari turns enabled: false into the control the provider needs, so your code does not change per model. A model without reasoning_toggle set to true may keep thinking.

Where the trace appears depends on the format.

On Chat completions it is in choices[].message.reasoning. The same text is also in reasoning_content, for tools that read that name.

{
"choices": [
{
"message": {
"role": "assistant",
"reasoning": "17 * 24 = 17 * 20 + 17 * 4 = 340 + 68 ...",
"reasoning_content": "17 * 24 = 17 * 20 + 17 * 4 = 340 + 68 ...",
"content": "17 * 24 = 408."
},
"finish_reason": "stop"
}
]
}

On Messages it is a thinking block before the text block:

{
"content": [
{"type": "thinking", "thinking": "17 * 24 = 17 * 20 + 17 * 4 ..."},
{"type": "text", "text": "17 * 24 = 408."}
]
}

On Responses it is a reasoning item at the start of output, with the text in summary[].text. In a stream the trace arrives before the answer. See Streaming for the events of each format.

kenari does not shorten or filter the trace. It is the text the model produced. Some models return no trace, even when they think.

Reasoning tokens are billed as Output tokens, at the same rate as answer tokens. A higher effort level or a larger budget can mean more tokens, and so a higher charge on models that bill them. :free models cost nothing. exclude: true changes nothing about the bill, because the thinking still happens. To spend less, lower effort or turn reasoning off. See How billing works.

Thinking uses part of max_tokens. If max_tokens is small, the thinking can use all of it before the answer starts, and the response has no answer text. kenari reports this as a cut-off, not as a normal stop:

FormatSignal
Chat completionsfinish_reason is "length"
Messagesstop_reason is "max_tokens"
Responsesstatus is "incomplete" with incomplete_details: {"reason": "max_output_tokens"}

The thinking tokens are still billed. Give a reasoning model enough max_tokens for the thinking and the answer together, or lower the effort.