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.
Find models that support reasoning
Section titled “Find models that support reasoning”GET /v1/models describes reasoning for each model. The call is public, so you can filter it without a key:
curl -s https://kenari.id/v1/models | jq -r '.data[] | select(.reasoning == true) | .id'| Field | Meaning |
|---|---|
reasoning | true when the model can think. |
reasoning_options | The effort levels the model accepts, for example ["low", "medium", "high"]. Absent when the model has no effort levels. |
reasoning_toggle | true 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.
Request reasoning
Section titled “Request reasoning”Each format has its own field. kenari maps all of them to the native reasoning control of the model’s provider.
| Format | How to ask |
|---|---|
| Chat completions | reasoning_effort, or the reasoning object below |
| Messages | thinking: {"type": "enabled", "budget_tokens": N}, or output_config: {"effort": "high"} |
| Responses | reasoning: {"effort": "high"} |
With Chat completions:
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.
The reasoning object
Section titled “The reasoning object”Chat completions also accepts an OpenRouter-style reasoning object, which you can send on every chat model:
| Field | Type | Description |
|---|---|---|
effort | string | One of the levels above. |
enabled | boolean | false turns reasoning off. true without effort or max_tokens turns it on at medium. |
max_tokens | integer | A token budget for the thinking. Models that take a level instead of a budget may ignore it, so prefer effort. |
exclude | boolean | true 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.
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.
Read the reasoning trace
Section titled “Read the reasoning trace”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.
Billing
Section titled “Billing”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.
Small max_tokens and empty answers
Section titled “Small max_tokens and empty answers”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:
| Format | Signal |
|---|---|
| Chat completions | finish_reason is "length" |
| Messages | stop_reason is "max_tokens" |
| Responses | status 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.