GPT-5 API parameters: fixing unsupported parameter errors
Updated 2026-07-15
Most GPT-5 unsupported_parameter errors trace back to two habits carried over from older chat models: sending max_tokens instead of max_completion_tokens, and still sending presence_penalty, frequency_penalty, logprobs, or seed even at their default value. Convert the token field, strip the legacy list, and keep reasoning_effort and verbosity, since those two are actually built for reasoning models.
Quick answer: the two fixes that clear most GPT-5 parameter errors
If you moved an integration from a classic chat model to a GPT-5-family reasoning model, a parameter list that used to work without complaint can start failing with an unsupported_parameter error, and the prompt itself is rarely the cause. Reasoning models validate the request body more strictly than older chat-completion models did, and a handful of fields that used to ride along harmlessly now get rejected outright or silently ignored depending on the route. Two changes clear almost every case. First, stop sending max_tokens and send max_completion_tokens instead. Reasoning models spend part of their token budget on hidden reasoning before they write a visible answer, and max_completion_tokens is the field built to cap that combined budget; max_tokens was never designed for a model that thinks before it writes. Second, strip presence_penalty, frequency_penalty, logprobs, top_logprobs, seed, and best_of from the payload before it leaves your app, even when your code sets them to an unremarkable default like 0, because a field that is present still counts as present to a strict validator regardless of its value. reasoning_effort and verbosity are worth keeping deliberately rather than stripping, since they are the two controls actually designed for this model family: one governs how hard the model thinks before answering, the other governs how much detail comes back once it does. Everything in this guide applies the same way whether you are calling gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4, gpt-5.4-mini, or the coding-tuned gpt-5.3-codex-spark, since all of them sit behind the same OpenAI-compatible /v1/chat/completions request shape.
Why the error shows up now, not on your old integration
None of this means these fields are useless everywhere. presence_penalty and frequency_penalty still matter for repetition control on classic sampling-based chat models, and stop and n both have real uses, just not as unconditional defaults on a GPT-5-family reasoning request. The practical fix lives at the boundary of your app, not inside the model: normalize the payload once before it goes out, and you stop debugging this error call site by call site every time a new route or fallback model appears.
- SDK and client-library defaults: many OpenAI-compatible client libraries still attach presence_penalty: 0, frequency_penalty: 0, or a numeric max_tokens automatically, even when your own code never set them, and a strict reasoning route can reject an explicitly present field regardless of its value.
- Hidden reasoning tokens: a GPT-5-family model spends part of its budget reasoning before it writes anything visible, so a max_tokens limit sized for a classic chat model can cut the answer off before it starts, or get rejected outright depending on how that specific route validates the field.
- Gateway and fallback routing: the same model name can be served by more than one upstream path depending on load, region, or availability, and not every path validates the exact same parameter set the same way, so a payload that worked yesterday can fail today with nothing changed on your side.
- Copied integration code: a request body written years ago for an older chat model often still carries stop, n, seed, and logprobs that nobody chose deliberately for the new model. They are leftovers from a payload that used to work, not decisions anyone made about this model.
What reasoning_effort actually costs: a worked example
reasoning_effort takes three values, low, medium, and high, and each one changes how many tokens the model spends thinking before it writes the part you actually see. Those reasoning tokens bill the same as ordinary output tokens, so the setting is a cost and latency dial as much as it is a quality one. Leaving it on high everywhere out of caution is the single most common way teams overspend on a GPT-5-family model. The table below prices one short request, roughly 500 input tokens, at each setting on gpt-5.6-terra, priced at $2.00 per million input tokens and $12.00 per million output tokens on the catalog. Treat the token counts as an illustrative planning model, not a measured benchmark: actual reasoning depth depends on the prompt and the task, and a simple classification call at high effort can still finish faster and cheaper than a genuinely hard problem run at low effort.
| reasoning_effort | Approx. tokens billed | Cost on gpt-5.6-terra | Best fit |
|---|---|---|---|
| low | ~650 total (500 in / 150 out) | $0.0028 | Classification, routing, short rewrites |
| medium | ~1,200 total (500 in / 700 out) | $0.0094 | Everyday chat, coding help, support replies |
| high | ~3,000 total (500 in / 2,500 out) | $0.0310 | Hard debugging, multi-step planning, review work |
GPT-5 parameter compatibility, field by field
Once max_completion_tokens and the strip list are handled, the rest of a typical request body falls into a smaller number of buckets: fields to always keep, fields to keep only with case-by-case testing, and fields to treat as greylisted until you have confirmed the exact model tier and route you are calling actually supports them. Use this as a starting whitelist for a production app, then relax it once you have verified a specific field against the model IDs you route to in practice.
| Parameter | Recommended status | Why it matters |
|---|---|---|
| model | Keep | Required on every call |
| messages | Keep | Required for Chat Completions |
| stream | Keep | Standard field, unchanged behavior |
| max_completion_tokens | Prefer | The token budget field written for reasoning models |
| max_tokens | Convert | Legacy budget field; strict reasoning routes may reject or mishandle it |
| reasoning_effort | Keep | Directly controls hidden reasoning depth, cost, and latency |
| verbosity | Keep | Controls answer length and detail inside the token budget |
| temperature / top_p | Keep with caution | Usually accepted, but matters less once reasoning is involved |
| presence_penalty | Strip | Built for repetition control on classic sampling models; low value here |
| frequency_penalty | Strip | Same risk profile as presence_penalty |
| logprobs / top_logprobs | Strip unless required | Often unsupported or costly on reasoning routes |
| seed | Strip | Determinism is not guaranteed across routes regardless |
| best_of | Strip | A completions-era field with no place in a reasoning chat request |
| stop | Greylist | Useful, but confirm it on each model tier before relying on it |
| n | Greylist | Multiplies cost per call and is not universally supported |
| tools / tool_choice | Keep | Required for tool-calling workloads, including coding-tuned models |
| response_format | Keep with validation | Useful for structured output; confirm schema support per model |
How to stop chasing unsupported_parameter errors
The last two items compound well together: a normalizer that strips the legacy list once, plus a routing layer that keeps the request shape identical across tiers, means the only thing that changes when you move traffic from a flagship model to a budget one is the model string itself.
- Start every new integration with the smallest payload that works: model, messages, and max_completion_tokens. Confirm that succeeds before adding anything else to the request.
- Add reasoning_effort deliberately per task, not as a blanket default. Low for classification and routing, medium for everyday chat and coding help, high only for requests that genuinely need deeper reasoning.
- Add verbosity once max_completion_tokens is set, and use it for length and style, not as a substitute for the hard cap.
- Convert max_tokens to max_completion_tokens at the client boundary and delete the original field. Never send both in the same request.
- Strip presence_penalty, frequency_penalty, logprobs, top_logprobs, seed, and best_of by default, and only add one back if a specific route genuinely needs it for a specific task.
- Treat stop and n as greylisted: confirm they work on every model tier you route to, including a coding-tuned model like gpt-5.3-codex-spark, before relying on them in production.
- Route requests through an OpenAI-compatible gateway that normalizes the payload for you. APIsRouter accepts the same normalized body whether the model is gpt-5.6-sol, gpt-5.6-terra, or gpt-5.6-luna, so falling back to a cheaper tier under load does not mean rewriting parameter logic.
Normalize the payload once, not per call site
The safest place to fix this is a single function that runs on every outgoing GPT-5-family request, not a patch applied wherever the error happens to surface next. The normalizer below converts the legacy token field and drops the strip list; keep it in whatever layer sits between your app code and the API client. Test the result with a direct call before wiring it into production. The example below points at an OpenAI-compatible endpoint and requests gpt-5.6-terra with reasoning_effort and verbosity both set explicitly, which is the shape you want the normalizer to produce for every GPT-5-family call.
const GPT5_STRIP_FIELDS = [
'presence_penalty',
'frequency_penalty',
'logprobs',
'top_logprobs',
'seed',
'best_of',
]
function isGpt5Family(model) {
return /^gpt-5(\.|-|$)/.test(model)
}
function normalizeGpt5Payload(body) {
const next = { ...body }
if (next.max_tokens && !next.max_completion_tokens) {
next.max_completion_tokens = next.max_tokens
}
delete next.max_tokens
for (const field of GPT5_STRIP_FIELDS) {
delete next[field]
}
return next
}
function normalizeRequest(body) {
return isGpt5Family(body.model) ? normalizeGpt5Payload(body) : body
}FAQ
Should I send max_tokens or max_completion_tokens to GPT-5 models?
Send max_completion_tokens. Convert max_tokens to it at the client boundary before the request goes out, and never send both fields in the same payload; reasoning models treat max_completion_tokens as the cap on the combined reasoning-plus-answer budget.
Why does my old chat integration suddenly throw an unsupported_parameter error on GPT-5?
Almost always a field your SDK or an older codebase still attaches by default, commonly presence_penalty, frequency_penalty, logprobs, seed, or best_of, even at a harmless-looking value like 0. Strip that list at the boundary and the error usually clears immediately without touching the prompt.
What does reasoning_effort actually control on a GPT-5 request?
How many tokens the model spends reasoning before it writes a visible answer. Low suits classification, routing, and short rewrites; medium fits everyday chat and coding help; high is for multi-step debugging or planning. Reasoning tokens bill as output, so higher settings cost more and add latency, which makes this a per-task dial rather than a global default.
Is verbosity the same thing as max_completion_tokens?
No. max_completion_tokens is a hard cap on the token budget; verbosity is a style and detail preference, low, medium, or high, for how much the answer says within that cap. Use both together: set the cap first, then choose verbosity for how the answer should read inside it.
Do presence_penalty and frequency_penalty ever work on GPT-5 models?
Some OpenAI-compatible routes accept them without error, but they were designed for repetition control on classic sampling-based chat models and add little to a reasoning model's output. Treat them as strip candidates by default and only add one back if a specific route needs it for a specific task.
What is the cheapest way to test a GPT-5 parameter fix before shipping it?
APIsRouter is a pay-as-you-go OpenAI-compatible gateway: top up at /topup with no signup, the key arrives by email, GPT-5-family models price at 20% below official list, and the first top-up adds a 100% balance bonus. That makes testing a normalizer against gpt-5.6-luna or gpt-5.4-mini before a flagship rollout cost a small fraction of a production run.