n4nAI

o3-mini reasoning effort settings: latency at low vs high

Analyzing o3-mini reasoning effort latency at low vs high settings: how hidden reasoning tokens drive TTFB and total time, with code and tradeoffs.

n4n Team4 min read880 words

Audio narration

Coming soon — every post will get a voice note here.

The o3-mini reasoning effort latency gap between low and high configurations is the single most misunderstood performance variable when shipping reasoning-model features. Set the knob to high by default and you silently multiply user wait times; set it to low and you may truncate the model’s ability to solve non-trivial problems. This analysis breaks down where the time goes, how to measure it honestly, and how to architect around the tradeoff.

The thesis: reasoning effort is a latency lever, not a quality toggle

Reasoning models don’t think faster or slower in the human sense. They allocate a budget of hidden internal tokens to decompose the problem before emitting the answer you see. The reasoning_effort parameter (low, medium, high) sets that budget.

Higher effort means more internal tokens, which translates directly into wall-clock time on the provider side. The visible output often looks similar in length, but the machine did more work upstream. Treating high as a generic “better” setting is a mistake that shows up as timeout errors and churned users.

How o3-mini spends time: hidden reasoning tokens

OpenAI’s o3-mini follows the o1-family design: a pre-answer reasoning phase generates tokens that are not streamed to the client. They are billed and metered, but they sit between request receipt and first visible token.

TTFB vs total completion time

Two metrics matter:

  • Time to first byte (TTFB) or time to first visible token: dominated by the reasoning phase.
  • Total completion time: reasoning tokens + generated answer tokens.

At low effort, the reasoning phase is short; TTFB can be near a normal chat model. At high, the model may spend seconds to minutes in internal deliberation before anything renders. For interactive UX, TTFB is usually the deal-breaker.

Measuring o3-mini reasoning effort latency in practice

You cannot eyeball this from a single curl. Write a timed streaming call and capture both the first token and the final usage.

from openai import OpenAI
import time

client = OpenAI()  # or base_url="https://api.n4n.ai/v1" if using gateway

def stream_o3(question: str, effort: str):
    return client.chat.completions.create(
        model="o3-mini",
        messages=[{"role": "user", "content": question}],
        reasoning_effort=effort,
        stream=True,
        stream_options={"include_usage": True},
    )

def measure(question: str, effort: str):
    start = time.perf_counter()
    ttft = None
    completion_tokens = 0
    for chunk in stream_o3(question, effort):
        delta = chunk.choices[0].delta
        if delta.content and ttft is None:
            ttft = time.perf_counter() - start
        if chunk.usage:
            completion_tokens = chunk.usage.completion_tokens
    total = time.perf_counter() - start
    return ttft, total, completion_tokens

# q = "Prove that sqrt(2) is irrational and implement a checker in Python."
# measure(q, "low") vs measure(q, "high")

The completion_tokens field includes reasoning tokens on these models, so the count itself signals how much hidden work happened.

What the numbers actually show

Without quoting private benchmarks: across math and code-gen prompts, high routinely produces several times the completion tokens of low and takes correspondingly longer end-to-end. The multiplier is problem-dependent. Trivial classification sees little delta; multi-step proofs show the largest spread.

An OpenAI-compatible gateway such as n4n.ai passes the reasoning_effort parameter through to the provider and meters per-token usage, so you can attribute o3-mini reasoning effort latency to billed cost without custom instrumentation. When a provider is rate-limited, a gateway with automatic fallback keeps the same reasoning_effort semantics on the failover model, preventing silent quality drops during incidents.

Token accounting and cost coupling

Latency and cost are coupled because reasoning tokens are billed exactly like output tokens. A typical usage blob looks like this:

{
  "usage": {
    "prompt_tokens": 120,
    "completion_tokens": 850,
    "total_tokens": 970
  }
}

If completion_tokens jumps from 200 at low to 900 at high on the same prompt, your latency scaled roughly with that ratio. Don’t separate the two axes in your dashboards.

Tradeoffs: when high effort pays off

The engineering job is to match effort to task class.

Simple classification: low wins

If the prompt is “label this support ticket as billing or technical,” low is correct. The reasoning phase adds nothing but wait. Ship low and cache the result.

Multi-step math or code: high earns its wait

For “derive the closed-form solution to this recurrence and write a test suite,” high reduces hallucinated steps. The latency penalty is justified because the alternative is a wrong answer that costs a human debug cycle.

Medium as a default compromise

If you can’t route per task, medium is a sane default. It bounds worst-case latency while preserving most of the accuracy gain on mid-complexity prompts.

Architecture patterns to mask latency

You rarely need to show a spinner for 40 seconds.

Streaming and incremental UI

Always stream. Even when internal reasoning is hidden, the eventual answer tokens arrive incrementally. Show a “thinking” state backed by TTFB measurement; if TTFB exceeds a threshold, degrade gracefully to async delivery.

// browser: switch UI state on first token
const reader = await fetch("/api/o3", {method:"POST", body: JSON.stringify({q, effort})});
const stream = reader.body!.getReader();
let first = true;
while (true) {
  const {done, value} = await stream.read();
  if (done) break;
  if (first) { setStatus("streaming"); first = false; }
  appendText(decoder.decode(value));
}

Speculative escalation

Send the prompt at low first. If the response confidence is low (e.g., self-check fails, or you detect a fallback pattern), re-issue at high in the background and patch the answer. This caps latency for the common case while preserving correctness for hard cases.

def answer_with_escalation(q):
    low_resp = complete(q, "low")
    if not passes_self_check(low_resp):
        return complete(q, "high")  # async job in real impl
    return low_resp

Caching reasoning context

Repeated subproblems (same system prompt, similar user prefix) can benefit from provider prompt caching. Reasoning tokens are generated fresh each call, but a gateway that forwards provider cache-control hints preserves the prompt-cache discount and trims TTFB on high-effort calls. Set cache_control on static prefixes and measure the delta.

Honest downsides of low effort

Low is not free lunch. On tasks requiring constraint propagation—say, “generate SQL that matches this schema and these three business rules”—low will occasionally skip a rule. You trade latency for silent quality loss. Log a sample of low-effort traces and review them; the break-even point is task-specific.

Decisive takeaway

Default to low for any request that a competent junior engineer could answer in under a minute. Escalate to high only behind an explicit trigger: task complexity, self-check failure, or user opt-in for “deep analysis.” Measure o3-mini reasoning effort latency per route in production, not in a notebook, because the spread is dominated by your prompt distribution, not the model. Build the escalation path before you ship, and the latency cost of reasoning becomes a controllable line item rather than a surprise outage.

Tagso3-minireasoning-effortlatency-benchmarkreasoning-model

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All reasoning model latency overhead posts →