Skip to content

Mapping Temperature and Sampling Parameters Between APIs

10 min read · updated August 11, 2026

Three APIs, one idea, three placements and three ranges. The renames are easy. The trap is assuming that a temperature which produced good output on one provider produces equivalent output on another once you have scaled the number into the accepted range.

Where the parameters live

The first difference is structural: some APIs put sampling parameters at the top level of the request body and one nests them.

// OpenAI Chat Completions — top level, alongside model and messages
{ "model": "...", "messages": [...],
  "temperature": 0.7, "top_p": 1, "stop": ["\n\n"], "max_tokens": 512 }

// Anthropic Messages — top level, alongside model, system and messages
{ "model": "...", "system": "...", "messages": [...],
  "temperature": 0.7, "top_p": 1, "top_k": 40,
  "stop_sequences": ["\n\n"], "max_tokens": 512 }

// Google Gemini generateContent — nested under generationConfig
{ "contents": [...], "systemInstruction": {...},
  "generationConfig": {
    "temperature": 0.7, "topP": 1, "topK": 40,
    "stopSequences": ["\n\n"], "maxOutputTokens": 512
  } }

Three renames are already visible and each has bitten somebody. The stop list is stop, stop_sequences and stopSequences. The output cap is max_tokens, max_tokens and maxOutputTokens — with the further wrinkle that newer OpenAI models take max_completion_tokens instead. And the nucleus parameter is top_p in snake case on two APIs and topP in camel case on the third, which is the kind of difference a case-insensitive search does not find.

The output cap deserves separate attention because it is the one whosemeaning is least stable. On Anthropic’s Messages API it is required rather than optional, so a request ported from an API where it defaults will be rejected outright until you supply one. On models that produce reasoning tokens, the cap may bound reasoning plus visible output together, which means a value that was generous on one model truncates the visible answer on another. This library’s context window versus max tokens page is the treatment of what the number actually bounds.

Ranges and defaults differ

The commonly cited difference is that temperature is accepted over a 0-to-2 range on OpenAI’s chat API and a 0-to-1 range on Anthropic’s Messages API, with Gemini’s accepted range documented per model. That is real and it is what produces the range-validation error when a value ports across. But two subtler facts matter more.

  • Defaults are not identical, and an omitted parameter is not a neutral parameter. Dropping temperature from a request does not mean “no sampling adjustment”; it means whatever that API defaults to. If your old request set a value explicitly and your new one omits it, you have changed the setting even though you deleted a line rather than editing one.
  • Some models reject the parameter entirely. Reasoning models on several providers do not accept a temperature at all, or accept only the default value, and return an error naming the parameter as unsupported for that model. This is a per-model constraint rather than a per-API one, so a capability table keyed on provider will get it wrong.
Every accepted range, default and per-model restriction in this section is a vendor decision that changes without a version bump. Read the range off the provider’s current API reference — OpenAI, Anthropic, Google — rather than from a table anywhere, including this one.

Why rescaling is not translation

Faced with a 0-to-2 value going to a 0-to-1 API, the obvious move is to halve it. It is obvious, it is what most quick adapters do, and it is not a translation. Here is why.

Temperature divides the logits before the softmax. The output distribution therefore depends on the logits, and the logits depend on the model — its vocabulary size, how confidently it was trained to predict, how much of the probability mass typically sits on the top few tokens. Two different models at the same numeric temperature do not produce equally varied output, and two models from the same family at the same temperature often do not either. The number is a knob on the model’s own distribution, not a unit of randomness that means the same thing everywhere.

So a linear rescale gives you an arithmetically defensible number with no behavioural meaning. Worse, it hides the fact that you have changed the sampling behaviour: a request that arrives asking for 1.4 and is silently converted to 0.7 will produce output nobody expected and no log line explains. If a mapping matters to your output quality, the honest procedure is to pick the setting on the target model by looking at its output on your own task, exactly as somebody did for the source model, and record the pair of values as two separate settings.

The same argument applies to top_p, with one difference in its favour: nucleus sampling is defined in terms of cumulative probability mass, so 0.9 does mean “the smallest set of tokens whose probabilities sum to 0.9” on any model. That makes it more portable than temperature. It is still not equivalent across models, because which tokens fall inside that mass depends entirely on the distribution, but the definition at least travels. Setting both at once is its own question, treated in temperature and top_p together.

Parameters with no counterpart

  • top_k. Truncate to the k highest-probability tokens. Present on Anthropic’s Messages API and in Gemini’s generation config; absent from OpenAI’s chat API. There is no client-side equivalent, because the truncation happens inside the sampler. Where it is absent, top_p is the nearest control and is not the same control.
  • Frequency and presence penalties. Additive adjustments that discourage repeated tokens and already-seen tokens respectively — an OpenAI-shaped pair without a direct counterpart on every other API. What they actually do is worth reading before deciding whether the loss matters; often the behaviour they were compensating for is better handled in the prompt.
  • Logit bias. Per-token additive bias, specified by token id. Narrowly supported, and inherently non-portable even where supported, because the token ids belong to a specific tokenizer. A bias map built for one model is meaningless on another.
  • Seed. Best-effort reproducibility. Absent on many APIs; see the fallback when a provider has no seed.
  • Multiple candidates. An n parameter on some APIs, a candidate count on others, absent elsewhere. Shimmable as n separate requests at the same total token cost.
  • Stop-list length. Even where the concept exists on both sides, the maximum number of stop strings differs, and exceeding it is a 400 rather than a truncation of the list. The stop parameter limit covers one such cap.

The policy your adapter needs

You need one decision per parameter, made once, applied everywhere, and visible in the response. The three options are clamp, reject and drop, and the right choice differs by parameter.

Reject is right for an out-of-range temperature. A caller asking for 1.6 on an API that accepts up to 1.0 has an expectation your adapter cannot meet, and clamping to 1.0 satisfies the validator while producing output the caller did not ask for. Fail with a message naming the parameter, the requested value and the accepted range, and let the caller decide.

Clamp is defensible for the output cap, where the semantics are “at most” and a smaller value is a weaker request rather than a different one — but it must be recorded. A request capped from 8,000 to the model’s maximum should come back with that fact on your response object, because the caller’s next question when the answer is short is whether the limit bit.

Drop is right only for parameters whose absence is a loss of a hint rather than a change of contract — and it too must be recorded. The rule that makes all of this work is the same one in the parity gaps page: whatever you do to a caller’s parameters, the fact that you did it belongs in the response, not only in a log.

Put the ranges in the same capability table the rest of the adapter uses, keyed by route rather than by vendor, and have the validation read from it. The alternative — a range check written inline at each call site — is how one service ends up clamping and another rejecting for the same underlying model.