New in 2.2: Validators, JSONPath and signatures See what's new →

Claude API version 2026-10-21: temperature is gone, and how to test the upgrade

Share

On 21 October 2026 Anthropic published the first new version of the Claude API since 2023-06-01. Every request carries an anthropic-version header, and the new value is 2026-10-21. The new major SDK releases send it by default, so a routine dependency bump is enough to move you onto it.

Most of the changes are small, but one of them breaks a line that appears in a lot of code: temperature is gone. This post lists what changed, according to Anthropic's version history, and gives you a grep, a curl and a small contract test to check your code before you upgrade.

What changed

APIChange on 2026-10-21What to do
Messagestemperature, top_p and top_k are removed. Sending any of them returns 400 on every modelDelete them from request bodies, including default values like temperature: 1
MessagesA document block placed directly in an assistant message returns 400Put documents in user turns or tool results
MessagesA text block with a non-empty citations array in a user message returns 400, directly or inside a search_resultStrip citations before you send earlier model output back as user content
AdminRBAC groups drop roles; members drop group_id; roles drop nameRead role_ids, rbac_group_id, display_name
AdminRate-limit entries drop group_type; API keys drop workspace_idRead group.type and scope.workspace_id
Enterprise AnalyticsUsage and cost reports drop actor.email; summaries drop summariesRead actor.email_address and data

The Admin and Analytics changes are renames. Code that reads a removed field gets undefined rather than an error, which is worse: a dashboard quietly shows blanks. If you have scripts that list API keys or report costs, check those as carefully as your Messages calls.

Why removing temperature hurts most

temperature: 0is one of the most copied lines in LLM code. People add it to make tests "deterministic", wrappers set it by default, and config files pass it through from environment variables. On the new version, all of those requests fail with a 400, even when the value is the default.

If you relied on temperature: 0 to make tests repeatable, it never really did that. Outputs at temperature 0 could still differ between runs. Tests on model output should check structure and properties (valid JSON, required fields, a value in range) rather than exact strings. Our post on testing a model switch shows a golden-set harness that works that way.

Find what will break

Terminal
# Every place your code may send a removed sampling parameter
grep -rnE "\b(temperature|top_p|top_k)\b" --include=*.{py,ts,js,mjs,go,java,cs} src/

# Every place that pins the version header by hand
grep -rn "anthropic-version" src/

Expect some false positives: temperature also shows up in weather APIs and CSS. Check each hit that sits near a Claude call. Also look in config: YAML, .env files and framework settings often hold the parameter without your code ever naming it.

See the 400 for yourself

Send one request with the new header and a temperature. You get a 400 instead of a reply. Change the header back to 2023-06-01 and the same request succeeds:

Terminal
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2026-10-21" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-haiku-5-5",
    "max_tokens": 64,
    "temperature": 0,
    "messages": [{"role": "user", "content": "Say hi"}]
  }'
# Expect: HTTP 400, an invalid_request_error that names the parameter

A contract test for the upgrade

This test sends the same requests on both versions. It costs a few tokens on Haiku and tells you whether the account and region you actually use behave the way the changelog says:

claude-version.test.mjs
// Runs against both versions so you can see exactly what the upgrade changes.
import { test } from "node:test";
import assert from "node:assert/strict";

const call = (version, body) => fetch("https://api.anthropic.com/v1/messages", {
  method: "POST",
  headers: {
    "x-api-key": process.env.ANTHROPIC_API_KEY,
    "anthropic-version": version,
    "content-type": "application/json",
  },
  body: JSON.stringify({ model: "claude-haiku-5-5", max_tokens: 32,
    messages: [{ role: "user", content: "Say hi" }], ...body }),
});

for (const param of [{ temperature: 0 }, { top_p: 0.9 }, { top_k: 40 }]) {
  const name = Object.keys(param)[0];
  test(`2026-10-21 rejects ${name}`, async () => {
    const res = await call("2026-10-21", param);
    assert.equal(res.status, 400);
    const err = await res.json();
    assert.equal(err.type, "error");
    assert.equal(err.error.type, "invalid_request_error");
  });
}

test("2026-10-21 accepts the same request without sampling params", async () => {
  const res = await call("2026-10-21", {});
  assert.equal(res.status, 200);
});

test("2023-06-01 still accepts temperature", async () => {
  const res = await call("2023-06-01", { temperature: 0 });
  assert.equal(res.status, 200);
});

Run it with node --test. When it passes, you know the old version still works for a rollback and the new one rejects exactly what it should. Don't assert the exact error message text. Anthropic reserves the right to change error wording within a version, so assert the status and error.type only.

Migrating without a big bang

  • Pin first. Before upgrading the SDK, set the version header yourself (or pin the old SDK major version) so the upgrade and the API version change happen in separate deploys.
  • Build bodies in one place. A single function that knows which params each version allows turns a production 400 into a failing unit test:
message-body.mjs
// While you migrate: one place that builds request bodies, so the version and the
// params it allows can't drift apart.
const REMOVED_IN_2026_10_21 = ["temperature", "top_p", "top_k"];

export function messageBody(params, version = "2026-10-21") {
  if (version < "2026-10-21") return params;
  const leftovers = REMOVED_IN_2026_10_21.filter((k) => k in params);
  if (leftovers.length) {
    // Fail in your own tests, not in production with a 400.
    throw new Error(`${leftovers.join(", ")} not allowed on ${version}`);
  }
  return params;
}
  • Watch 400s by version. Log the anthropic-version you sent next to each 4xx. A jump in invalid_request_error right after a deploy tells you which service missed a parameter.
  • Check proxies and gateways. If a gateway sits between you and the API and injects defaults like temperature, it breaks your requests on the new version even when your code is clean.

For the full upgrade steps, including Bedrock models older than 4.7, read Anthropic's migration guide. To practise asserting on 400 responses without spending tokens, use our practice endpoints.