Back to blog

Claude API 400 Error: temperature, top_p, top_k

Claude API 2026-10-21 returns a 400 if you send temperature, top_p or top_k. See what breaks and how to fix it.

0 views 6 min read
Share
Claude API 400 Error: temperature, top_p, top_k

Since October 21, 2026, a Claude API request that includes temperature, top_p or top_k returns a 400 error, even when the value is the default. That only applies to requests sent with API version 2026-10-21, which is the version the new SDK majors send automatically. If your code is pinned to an older SDK, nothing has broken yet. If it isn't, one pip install -U anthropic is enough to find out the hard way. This is the fix, and what I measured when I installed the old and new SDKs side by side.

What changed, according to the migration guide

Anthropic's migration guide for 2026-10-21 lists the breaking changes for the Messages API, token counting and batch requests:

  • A request that sends temperature, top_p or top_k returns 400, "even at the parameter's default value."
  • A document block inside an assistant message returns 400. Documents go in user messages.
  • A non-empty citations array on a text block in a user message returns 400.
  • Without the sampling parameters, requests use the defaults, which includes a temperature of 1.0.

Two details that people skip. On Claude 4.7 and later, the models only ever accepted default values, so for them nothing changes except that sending the default now also errors. On older models such as Sonnet 4.6, lowering temperature for more consistent output stops working, and the guide says no other setting replaces it. If a request truly depends on a low temperature, the guide's advice is to stop sending the parameter or keep that request on 2023-06-01. The guide doesn't quote the exact text of the 400 response, and I did not call the live API, so I'm not going to invent it here.

What the SDKs actually send

I wanted to see which upgrades are risky instead of guessing, so I installed old and new versions of both SDKs and pointed each at a local stub server that records the request. The stub is not the real API. It only shows what leaves your machine.

| SDK | temperature argument | anthropic-version sent | Auth header | |---|---|---|---| | Python anthropic 0.125.0 | accepted | 2023-06-01 | x-api-key | | Python anthropic 1.17.0 | removed | 2023-06-01 | x-api-key | | Python anthropic 2.3.0 | removed | 2026-10-21 | Authorization: Bearer | | @anthropic-ai/sdk 0.137.0 | accepted | 2023-06-01 | x-api-key | | @anthropic-ai/sdk 1.3.0 | removed from types | 2026-10-21 | Authorization: Bearer |

Two things in that table surprised me. The Python SDK dropped the temperature argument already in 1.17.0, while still sending the old API version, so code can break on a minor-looking upgrade before the API change even applies. And the JavaScript SDK removed temperature from its types in 1.3.0 but still sent it at runtime when I forced it through, which means plain JavaScript gets the API's 400 and TypeScript gets a compile error instead.

The naive upgrade: keep your temperature and bump the SDK

Here is the call most existing code looks like:

python
58 tokens
import anthropic
client = anthropic.Anthropic()
client.messages.create(
model="claude-sonnet-4-6",
max_tokens=16,
temperature=0.2,
messages=[{"role": "user", "content": "hi"}],
)

On Python anthropic 2.3.0, and on 1.17.0, it never reaches the network. This is the output I got:

text
14 tokens
TypeError: Messages.create() got an unexpected keyword argument 'temperature'

In TypeScript with @anthropic-ai/sdk 1.3.0, the same call fails at compile time:

text
42 tokens
t.ts(3,70): error TS2769: No overload matches this call.
Object literal may only specify known properties, and 'temperature' does not exist in type 'MessageCreateParamsBase'.

And if you call the API over raw HTTP, for example from a Laravel Http::post() that already sets anthropic-version: 2026-10-21, the SDK isn't there to protect you. The request goes out with temperature in the body and the API answers with the 400 the guide describes. I could not run that last case here, so treat it as what the guide says, not something I observed.

The fix: remove the parameters, then check what leaves your machine

python
39 tokens
client.messages.create(
model="claude-sonnet-4-6",
max_tokens=16,
messages=[{"role": "user", "content": "hi"}],
)

Against my stub, Python 2.3.0 sent exactly this, with no sampling fields in the body:

json
42 tokens
{"anthropic-version": "2026-10-21", "x-api-key": null, "authorization": "Bearer ***", "body_keys": ["max_tokens", "messages", "model"]}

If you use Laravel or any plain HTTP client, the change is two lines. Remove the three parameters from the payload, and only change the anthropic-version header once you've done that. I haven't run this snippet in a Laravel app, so check it in yours:

php
97 tokens
$response = Http::withHeaders([
'x-api-key' => config('services.anthropic.key'),
'anthropic-version' => '2026-10-21',
])->post('https://api.anthropic.com/v1/messages', [
'model' => 'claude-sonnet-4-6',
'max_tokens' => 1024,
'messages' => $messages,
// no temperature, top_p or top_k
]);

Also grep your code for a few places people forget. Wrapper libraries and frameworks may add sampling parameters for you, and the guide doesn't say how each framework behaves, so search your own dependencies, not just your own files:

bash
19 tokens
grep -rnE "temperature|top_p|top_k" app/ src/ config/

Don't forget the auth header

The new SDKs send Authorization: Bearer <key> instead of x-api-key, which my stub run confirmed. The API accepts either header, so your own calls are fine. The trap is a proxy, gateway or logging filter that only looks for x-api-key. If you have one, update it before you upgrade, because a request that suddenly looks unauthenticated is much harder to debug than a 400.

What I'd do

I'd pin the SDK in requirements.txt or package.json today, then migrate on purpose rather than letting an unpinned install do it. Pinning buys you time, but I would not treat 2023-06-01 as a permanent home. The guide I read doesn't announce an end date for it, which is a reason not to panic, not a reason to wait a year. For anything that relied on a low temperature for consistent output on an older model, test the new defaults on real prompts before you ship, because that behaviour change is silent and no 400 will warn you.

Conclusion

Pin your SDK, remove temperature, top_p and top_k from every call, then upgrade and test. If you call the API over raw HTTP, change the header last, after the parameters are gone. For the other half of keeping LLM calls stable, read how to fix invalid JSON from LLM API responses, and if you call an LLM from queued jobs, see fixing OpenAI API timeouts in Laravel jobs. For wiring an LLM into a Laravel app in the first place, start with OpenAI function calling in a Laravel app.

Last checked October 30, 2026. Source: Anthropic's migration guide to API version 2026-10-21. The SDK results above come from my own test with Python anthropic 0.125.0, 1.17.0 and 2.3.0 and @anthropic-ai/sdk 0.137.0 and 1.3.0, sent to a local stub server, not the live API.

Found this useful? Share it.

Share