Chat completions

Reasoning

Use reasoning models that think step-by-step before answering.

How reasoning models work

Reasoning models are trained to think step-by-step before responding with an answer. Given an input prompt, they first produce a chain of thought, visible as tokens in a dedicated reasoning_content field, and then output a final answer in the content field. See Handle reasoning tokens.

Supported models

Not every reasoning model thinks on a plain request. Those marked “No” under Thinks by default answer directly until you set reasoning_effort; the others think on every request. Models with adjustable effort accept reasoning_effort to control depth, including none to turn thinking off for a request.

ModelAPI idThinks by defaultAdjustable effortreasoning_tokens in usageContext
DeepSeek V4 Flashdeepseek4-flashNoYesYes262k
DeepSeek V4 Prodeepseek-v4-proNoYesYes1M
GLM 5.3glm5.3YesYesYes262k
GLM 5.3 Flashglm5.3-flashYesYesYes262k
Qwen3.8 27Bqwen3.8-27bNoYesYes262k
Reka Flash 3reka-flash-3YesNoNo64k

reka-edge-2603 is a non-reasoning model. The feed at GET /v1/models is authoritative.

Quickstart

Reasoning models return the chain of thought in a reasoning_content field alongside content in the response, and as delta.reasoning_content on each streaming chunk. Reasoning models produce longer outputs, so you should stream the response:

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.reka.ai/v1",
    api_key=os.environ["REKA_API_KEY"],
)

stream = client.chat.completions.create(
    model="glm5.3-flash",
    messages=[{"role": "user", "content": "Which number is bigger, 9.11 or 9.9?"}],
    stream=True,
)

printed_answer_header = False

for chunk in stream:
    if not chunk.choices:
        continue
    delta = chunk.choices[0].delta

    # The chain of thought streams in reasoning_content.
    reasoning = getattr(delta, "reasoning_content", None)
    if reasoning:
        print(reasoning, end="", flush=True)

    # The final answer arrives in content.
    if delta.content:
        if not printed_answer_header:
            print("\n\n--- Answer ---\n", flush=True)
            printed_answer_header = True
        print(delta.content, end="", flush=True)

Here is what the same request returns without stream, trimmed to the relevant fields. The thinking is in reasoning_content, the answer is in content, and usage.reasoning_tokens reports how many of the completion_tokens were spent thinking (58 of 71 here):

JSON
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "9.9 is bigger than 9.11.",
        "reasoning_content": "Let me compare 9.11 and 9.9. Both have 9 as the integer part, so I need to compare the decimal parts: 0.11 vs 0.9. Since 0.9 = 0.90, and 0.90 > 0.11, we know 9.9 > 9.11."
      }
    }
  ],
  "usage": {
    "prompt_tokens": 18,
    "completion_tokens": 71,
    "total_tokens": 89,
    "completion_tokens_details": { "reasoning_tokens": 58 },
    "reasoning_tokens": 58
  }
}

Reasoning effort

The reasoning_effort parameter controls how much computation the model spends on reasoning, so you can balance accuracy against cost and latency. It accepts none, minimal, low, medium, high, xhigh or max, and is also accepted as reasoning.effort.

  • none: skip thinking entirely. Fastest and cheapest; use it for simple tasks.
  • low / minimal: faster responses for simpler tasks with reduced reasoning depth.
  • medium: balanced performance for most use cases.
  • high / xhigh / max: deepest reasoning for complex problems. Set max_tokens to ~30,000 or more with these settings.

Each reasoning model maps the value onto its own thinking tiers, so adjacent levels may behave identically on a given model. Any other value is a 400 with code invalid_value and param: reasoning_effort.

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.reka.ai/v1",
    api_key=os.environ["REKA_API_KEY"],
)

stream = client.chat.completions.create(
    model="glm5.3-flash",
    messages=[
        {
            "role": "user",
            "content": "Solve: If all roses are flowers and some flowers are red, can we conclude that some roses are red?",
        }
    ],
    reasoning_effort="medium",
    max_tokens=30000,
    stream=True,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

Set reasoning_effort to none to skip thinking on a request:

response = client.chat.completions.create(
    model="glm5.3-flash",
    messages=[{"role": "user", "content": "What is the capital of France?"}],
    reasoning_effort="none",
)

print(response.choices[0].message.content)

reasoning_effort is accepted but has no effect on reka-flash-3 or reka-edge-2603. Reka Flash 3 always reasons and cannot be turned off; use prompting to shorten it, and give itmax_tokens headroom: with a small budget it can spend all of it thinking and return empty content.

Controlling reasoning depth via prompting

You can also influence how much the model thinks by including instructions in your prompt. This is a simple way to reduce token usage and latency when the problem doesn’t warrant deep reasoning, and it is the only control available on Reka Flash 3.

Ask the model to keep its thinking concise:

response = client.chat.completions.create(
    model="glm5.3-flash",
    messages=[
        {
            "role": "user",
            "content": "Please be succinct in your thinking.\n\nWhat is the derivative of x^3 + 2x?",
        }
    ],
    stream=True,
)

You can also suggest an approximate budget for the reasoning process:

response = client.chat.completions.create(
    model="glm5.3-flash",
    messages=[
        {
            "role": "user",
            "content": "Please use around 1000 words to think, but do not literally count each one.\n\nExplain why quicksort has O(n log n) average-case complexity.",
        }
    ],
    stream=True,
)

The model won’t hit an exact word count, but it reliably produces shorter or longer reasoning chains in response to the guidance. Combine it with max_tokens for a hard ceiling on total output.

Handle reasoning tokens

Every reasoning model returns its thinking in a dedicated reasoning_content field on the response message, and as delta.reasoning_content when streaming. glm5.3-flash also echoes it under reasoning, OpenRouter’s name for the same field; read reasoning_content and ignore the duplicate.

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.reka.ai/v1",
    api_key=os.environ["REKA_API_KEY"],
)

response = client.chat.completions.create(
    model="glm5.3-flash",
    messages=[{"role": "user", "content": "Say test 10 times"}],
)

message = response.choices[0].message

print("Reasoning:", message.reasoning_content)
print("Answer:", message.content)
print("Reasoning tokens:", response.usage.completion_tokens_details.reasoning_tokens)

Structured outputs with reasoning models

Reasoning models can return JSON that conforms to a schema, the same way non-reasoning models do. The model still produces its chain of thought in reasoning_content, then writes the structured answer to content.

Example: have GLM 5.3 Flash solve a math problem and return the steps as typed JSON.

import json
import os
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI(
    base_url="https://api.reka.ai/v1",
    api_key=os.environ["REKA_API_KEY"],
)


class Step(BaseModel):
    explanation: str
    output: str


class MathReasoning(BaseModel):
    steps: list[Step]
    final_answer: str


completion = client.chat.completions.create(
    model="glm5.3-flash",
    messages=[
        {
            "role": "system",
            "content": "You are a helpful math tutor. Guide the user through the solution step by step.",
        },
        {"role": "user", "content": "how can I solve 8x + 7 = -23"},
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "math_reasoning",
            "schema": MathReasoning.model_json_schema(),
        },
    },
)

math_reasoning = json.loads(completion.choices[0].message.content)
print(json.dumps(math_reasoning, indent=2))

Example output:

JSON
{
  "steps": [
    {
      "explanation": "To solve 8x + 7 = -23, I need to isolate x.",
      "output": ""
    },
    {
      "explanation": "Subtract 7 from both sides.",
      "output": "8x = -30"
    },
    {
      "explanation": "Divide both sides by 8.",
      "output": "x = -30/8 = -15/4"
    }
  ],
  "final_answer": "x = -15/4"
}

For the full reference, see Structured outputs.

Prompting best practices

Prompt reasoning models differently than standard models:

TipDetails
Don’t add chain-of-thought instructionsThese models already reason step-by-step. Telling them to “think step by step” is unnecessary and can hurt performance.
Avoid few-shot examplesFew-shot prompting can degrade performance. Describe the task and desired output format instead.
Think in goals, not stepsProvide high-level objectives (e.g., “Analyze this data and identify trends”) and let the model determine the methodology. Over-prompting limits reasoning ability.
Structure your promptUse XML tags, markdown formatting, or labeled sections to separate different parts of your prompt.
Set generous max_tokensReasoning tokens can number in the tens of thousands for complex problems. Make sure max_tokens accommodates both reasoning and content; the gateway applies a per-model default budget if you omit it.
Use reasoning_effort, not temperature, to control depthLeave temperature at the model default and tune reasoning_effort for how much thinking you want.

When not to use reasoning

Non-reasoning models, or reasoning_effort: "none", are a better fit when:

  • Latency is critical: real-time voice agents, instant-response chatbots, or other applications that need fast responses.
  • Tasks are straightforward: simple classification, basic text generation, factual lookups, or quick summaries don’t benefit from extended reasoning.
  • Cost is the priority: high-volume pipelines processing many simple queries. Reasoning tokens significantly increase per-query costs.

For these use cases, consider reka-edge-2603, a model that doesn’t think by default such as qwen3.8-27b, or set reasoning_effort: "none" on any reasoning model.

Manage costs and latency

Reasoning tokens can vary from a few hundred for simple problems to tens of thousands for complex challenges. Strategies to keep costs and latency in check:

  • Count reasoning tokens: reasoning output is billed once as completion tokens and reported under usage.reasoning_tokens (also at usage.completion_tokens_details.reasoning_tokens, where the OpenAI SDKs read it). It is a subset of completion_tokens, not billed twice. reka-flash-3 omits the field: its reasoning is billed as ordinary output tokens with no separate breakdown.
  • Use max_tokens: set a token limit to cap total output. This reduces costs but may truncate reasoning on complex problems, and a model that always reasons (Reka Flash 3) can use the whole budget thinking and return empty content. Find the right balance for your use case.
  • Use reasoning effort levels: use reasoning_effort: "none" or "low" for routine tasks and "high" for critical decisions.
  • Prompt for shorter reasoning: include instructions like “Be succinct in your thinking” to reduce reasoning token usage on simpler problems. See Controlling reasoning depth.
  • Stream responses: since reasoning models produce longer outputs, streaming with stream: true provides a better user experience by showing partial results as they arrive.

Next steps