The first thing that broke when I moved a summarization pipeline from Gemini 2.5 to Gemini 3 wasn't a prompt or a schema. It was a number. I had spent an afternoon tuning thinking_budget down to a value that gave me acceptable latency without the model going off the rails, and Gemini 3 simply doesn't take that number the way I expected anymore. The replacement, thinking_level, is a smaller and blunter control — and that turns out to be the point.
If you're migrating, or starting fresh on Gemini 3, here's the mental model that saved me a lot of guessing.
From a token count to a dial
Gemini 2.5 asked you to predict the future. thinking_budget was a token allowance for the model's internal reasoning, and to set it well you had to estimate how many tokens a given task deserved — a number that depends on the input, which you don't have until runtime. In practice most teams either left it at the default or picked a round number and hoped.
Gemini 3 abstracts that into thinking_level: instead of a token count, you pick how hard the model should think on a relative scale. It's the difference between telling a contractor "spend exactly $4,200 on this" and telling them "this is a rough job" versus "this one has to be perfect." The model decides how many reasoning tokens that actually costs for the input in front of it.
The values, from least to most reasoning:
minimal — close to no thinking; built for throughput over reasoning. Supported on the Flash-tier models (Gemini 3 Flash, 3.1 Flash-Lite).
low — minimizes latency and cost. Good for instruction following, chat, structured extraction, summarization.
medium — a balance, available on the Flash models for when low is too shallow but you still care about latency.
high — maximizes reasoning depth. This is where you want to be for code generation, multi-step logic, math, and anything agentic.
Defaults are not uniform, and this bites people. Gemini 3.1 Pro and 3 Flash default to high; Gemini 3.1 Flash-Lite defaults to minimal. So the same code, pointed at two different models, can quietly ship very different reasoning behavior. Set the level explicitly if you care.
Setting it is unremarkable, which is how it should be:
from google import genai
client = genai.Client()
response = client.models.generate_content(
model="gemini-3-pro",
contents="Extract the invoice total and due date as JSON.",
generation_config={"thinking_level": "low"},
)
The REST shape is the same field under generation_config:
{
"generation_config": { "thinking_level": "low" }
}
The 400 that catches every migration
There is one hard rule worth tattooing on the migration checklist: you cannot send thinking_level and thinking_budget in the same request. Do it and you get a 400. Both parameters still exist — thinking_budget is kept for backward compatibility — but they are mutually exclusive per request, not a fallback pair.
This is easy to trip over if your config is assembled from layers: a base config that still carries a thinking_budget from your 2.5 days, and a Gemini 3 call site that adds thinking_level. Nothing looks wrong until the merged payload has both. When you migrate, delete thinking_budget outright rather than leaving it "just in case."
Two settings that quietly interact with it
Temperature. My instinct with a reasoning model was to lower temperature for determinism. Don't. Google's guidance for all Gemini 3 models is to keep temperature at its default of 1.0. Pushing it lower can cause looping or degraded output, especially on the exact multi-step reasoning tasks where you'd reach for high in the first place. The thinking machinery is tuned around that default; treat it as load-bearing.
Thought signatures. Gemini 3 returns encrypted thoughtSignature tokens representing the model's internal reasoning, and it expects you to pass them back on subsequent turns. Validation is strict — for function calling, a missing signature is a 400, not a soft degradation. And this holds even at minimal: a low or minimal thinking level does not exempt you from returning signatures. If you're building agents or multi-turn tool use, plumbing these through is not optional, and it's the part most likely to surprise you if you assumed "less thinking" meant "less state to carry."
The shift is subtle but real: thinking_level isn't just a cost knob, it's coupled to how much reasoning context the model hands back to you. Turning it down saves tokens; it does not free you from carrying the model's thoughts across turns.
How I actually pick a level
I stopped agonizing over this once I mapped it to a single question — does correctness on this call depend on chained reasoning?
- No — extraction, classification, reformatting, retrieval-augmented answers where the model is mostly transcribing grounded facts:
low (or minimal on Flash for high-volume paths). These are latency- and cost-sensitive and gain little from deep thinking.
- Somewhat — drafting, tool selection with a couple of steps, moderately structured analysis:
medium where the model offers it, otherwise high.
- Yes — code generation, debugging, math, planning, security-style "scan this for problems" work:
high, and stop trying to shave it. This is what you're paying Gemini 3 for.
Community measurements put the reasoning-token gap between low and high at close to an order of magnitude, so the lever has real financial weight on a busy endpoint — but I'd rather find the floor empirically than start low and chase mysterious quality regressions. Start at the default for the task tier, then step down while watching an eval set, not up while watching a bill.
The takeaway: treat thinking_level as a per-route decision, not a global default. Wire it into your config alongside the model name, delete every lingering thinking_budget, leave temperature at 1.0, and pick the level from whether the route's correctness rides on multi-step reasoning. Then let an eval — not a hunch — tell you how far down you can turn the dial.
Sources: Gemini 3 Developer Guide — Google AI for Developers, New Gemini API updates for Gemini 3 — Google Developers Blog