Anthropic API Version 2026-10-21: Every Breaking Change and How to Upgrade the SDKs
2026-10-21, the first new anthropic-version since 2023-06-01, together with new SDK majors (Python 2.0.0, TypeScript 1.0.0, Go 2.0.0, Java 3.0.0, C# 13.0.0, PHP 1.0.0, Ruby 2.0.0, ant CLI 2.0.0) that send it by default. If your Claude calls started returning 400 after a dependency bump, the usual causes are temperature, top_p or top_k in the request (rejected even at default values), document blocks inside assistant messages, or non-empty citations on text blocks in user messages. Remove those, update proxies that read X-Api-Key (the SDKs now send Authorization: Bearer), and keep only workloads that truly depend on a non-default temperature on anthropic-version: 2023-06-01.For three years, every request to the Claude API carried the same header: anthropic-version: 2023-06-01. That changed on October 21, 2026. Anthropic shipped API version 2026-10-21 and, on the same day, a new major release of every official SDK and the ant CLI. Those releases send anthropic-version: 2026-10-21 on every request by default, so a routine pip install -U or Dependabot merge can turn working code into a stream of 400 errors without a single line of your code changing.
Some of the breakage comes from the API version itself (rejected parameters and message shapes). Some comes from the SDKs, which changed independently of the API version: a role-discriminated MessageParam type, a new auth header, a one-credential rule, removed Text Completions, and a license change from MIT to Apache 2.0.
This guide is organized by symptom. Start with the table, jump to the section that matches your error, then use the per-SDK notes and rollout checklist to finish the upgrade. Everything below is taken from Anthropic's official migration guide, the API versions page, and the platform release notes, as of October 2026.
Symptom-to-fix table
| Symptom | Cause | Fix |
|---|---|---|
400 on every Messages call that sets temperature, top_p or top_k, even temperature=1.0 | Sampling parameters are removed in 2026-10-21 | Delete the parameters. If a workload depends on a non-default value, keep it on 2023-06-01 (previous SDK major in its own service, or raw HTTP) |
| 400 when replaying a conversation that contains a PDF or text document | A document block sits directly in an assistant message | Move the document into a user message |
| 400 when sending back retrieved snippets or search results | A text block in a user message carries a non-empty citations array (including inside search_result blocks) | Strip citations from user-side text blocks |
client.completions is undefined / method not found (TypeScript, Go, Java, Ruby, CLI) | Text Completions removed from those SDKs and the CLI | Port to the Messages API |
| 401 or 403 from your own gateway, proxy, or test stub | SDKs now send the key as Authorization: Bearer <key> instead of X-Api-Key | Have the proxy read Authorization, or set X-Api-Key yourself |
TypeError when calling MessageParam(...) in Python | MessageParam is now a union of role-specific types | Write messages as dicts |
ValueError / TypeError constructing the client | Both an API key and an auth token were passed; a client now holds one credential | Pass exactly one credential |
Admin or Analytics dashboards show blanks or KeyError | Renamed response fields (roles, group_id, name, and others) | Read the new field names |
flag provided but not defined from ant | --api-key and --auth-token flags removed in CLI 2.0.0 | Use ANTHROPIC_API_KEY, --api-key-stdin, or ant auth login |
What actually changed on October 21, 2026
Three of the four October 21 release-note items matter to most teams: the new API version, new SDK and CLI majors that send it by default, and SDK breaking changes that don't depend on the API version, including a license change from MIT to Apache 2.0. (The fourth adds enforcement_status and enforcement_actions fields to user profiles.) The SDK versions that are the first to send 2026-10-21:
| Package | First version on 2026-10-21 | Notes |
|---|---|---|
Python anthropic | 2.0.0 | MessageParam becomes a union; one credential per client |
TypeScript @anthropic-ai/sdk | 1.0.0 | Upgrade Bedrock, Vertex, Foundry, AWS and Google Cloud packages to v1 at the same time |
Go anthropic-sdk-go | 2.0.0 | New module path github.com/anthropics/anthropic-sdk-go/v2; Go 1.25 or later required |
Java com.anthropic:anthropic-java | 3.0.0 | MessageParam.builder() replaced by ofUser / ofAssistant / ofSystem |
C# Anthropic | 13.0.0 | IChatClient adapters ignore sampling options |
PHP anthropic-ai/sdk | 1.0.0 | Use composer require; composer update stays inside your constraint |
Ruby anthropic | 2.0.0 | Text Completions removed; compaction_control: removed |
ant CLI | 2.0.0 | Go install path gains /v2 |
Starting with these versions, the SDKs and CLI are licensed under the Apache License 2.0 instead of MIT. Earlier versions remain MIT. No code change is needed, but if legal or security tracks dependency licenses, update those entries.
One platform caveat: the new SDKs' Amazon Bedrock and Google Cloud clients also send 2026-10-21, so every breaking change below applies there too. On Bedrock, 2026-10-21 is available only for Claude 4.7 and later models. If you call an older model on Bedrock, Anthropic's guidance is to stay on your current SDK version (or keep sending your current version header) until you have moved that traffic to a 4.7-or-later model.
Fix 1: temperature, top_p and top_k return 400
This is the change most teams will hit first. Under 2026-10-21, a Messages, token-counting (POST /v1/messages/count_tokens) or Message Batches request that includes temperature, top_p, or top_k returns a 400 error on every model, even when the value equals the default. The new SDK majors also remove the three parameters from their types, so in typed code the failure often shows up at compile or lint time instead of at runtime.
Leaving the parameters out gives you the defaults, including temperature 1.0, which is the same sampling a 2023-06-01 request gets when it omits them. For Claude 4.7 and later models (including Claude Opus 5.5, Claude Sonnet 5.5 and Claude Haiku 5.5), which already accept only default sampling values, nothing about output behavior changes. On older models that accepted other values, such as Claude Sonnet 4.6, a request that lowered temperature for more consistent output loses that effect, and Anthropic states plainly that no other setting replaces it.
Python: before and after
# Before: anthropic 1.x, sends anthropic-version 2023-06-01
import anthropic
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
temperature=1.0, # accepted on 2023-06-01, rejected with 400 on 2026-10-21
messages=[{"role": "user", "content": "Summarize this incident report."}],
)
# After: anthropic 2.0.0, sends anthropic-version 2026-10-21
import anthropic
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Summarize this incident report."}],
)
print(message.content[0].text)TypeScript: before and after
// Before: @anthropic-ai/sdk 0.x
const message = await client.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
temperature: 1.0, // 400 under 2026-10-21; also gone from the v1 types
messages: [{ role: "user", content: "Summarize this incident report." }],
});
// After: @anthropic-ai/sdk 1.0.0
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const message = await client.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Summarize this incident report." }],
});Watch for indirect callers. Framework adapters, eval harnesses, and shared config objects often inject temperature: 0 for "deterministic" runs. The C# SDK's IChatClient adapters now silently ignore ChatOptions.Temperature, TopK and TopP; other wrappers may not be as forgiving.
Fix 2: document blocks in assistant messages
Under 2026-10-21, a request that places a document block directly in an assistant message returns 400. This usually appears in apps that synthesize conversation history, for example by inserting a fake assistant turn that "contains" a file. Send documents in user messages instead.
Documents nested inside another block are still accepted. A web fetch tool result that Claude returned, which contains a document, can be sent back unchanged.
Fix 3: citations on text blocks in user messages
A text block with a non-empty citations array inside a user message now returns 400, including text blocks inside search_result blocks. RAG pipelines that copy Claude's cited answer into the next user turn, or that pre-populate citations on search results, are the typical culprits.
Citations on text blocks in assistant messages (Claude's earlier replies that you send back) are still accepted, and so is the "citations": {"enabled": true} setting on documents and search results. Only the user-side citation arrays have to go. A small sanitizer before each request is enough:
def strip_user_citations(messages: list[dict]) -> list[dict]:
"""Remove citations arrays from text blocks in user messages."""
for message in messages:
if message["role"] != "user" or isinstance(message["content"], str):
continue
for block in message["content"]:
if block.get("type") == "text":
block.pop("citations", None)
elif block.get("type") == "search_result":
for inner in block.get("content", []):
if inner.get("type") == "text":
inner.pop("citations", None)
return messagesFix 4: Text Completions is gone from four SDKs and the CLI
The legacy Text Completions API is removed from the TypeScript, Go, Java and Ruby SDKs and from the ant CLI. Anthropic notes that newer models and features don't support it. If you still have client.completions.create(...) calls with \n\nHuman: / \n\nAssistant: prompts, port them to the Messages API: move the human turns into messages with role: "user", move any preamble into system, and replace max_tokens_to_sample with max_tokens.
If the code that still uses Completions also targets an old model, check whether that model is approaching retirement. Claude Sonnet 4.5, for example, retires on the Claude API on November 30, 2026, so plan the model move and the Messages port together if you need to do both.
Fix 5: API keys are sent as Authorization: Bearer
The new SDKs and the CLI send your API key as Authorization: Bearer <key> instead of X-Api-Key: <key>. The Claude API accepts a key in either header, so direct calls keep working. What breaks is anything in between that reads or rewrites X-Api-Key: an internal LLM gateway, a reverse proxy that injects the key, a log scrubber, a middleware that rate-limits by key, or a test stub that asserts on headers.
The preferred fix is to update the proxy to read Authorization. If you can't touch the proxy yet, set the old header yourself. In TypeScript:
import Anthropic from "@anthropic-ai/sdk";
const apiKey = process.env.ANTHROPIC_API_KEY!;
const client = new Anthropic({
apiKey,
// Send the key the old way and stop the Bearer header from going out.
defaultHeaders: { "X-Api-Key": apiKey, Authorization: null },
});In Python, the migration guide shows passing an auth function to DefaultHttpxClient that moves the key from Authorization back to X-Api-Key on every request, including retries. A few related details are easy to miss:
- Per-request key overrides changed. In Python,
extra_headers={"X-Api-Key": other_key}no longer replaces the client's key: both keys go out. Useextra_headers={"Authorization": f"Bearer {other_key}"}. The TypeScript equivalent isheaders: { Authorization: `Bearer ${otherKey}` }. - Not every platform client switched. The Foundry clients (
AnthropicFoundryin Python,@anthropic-ai/foundry-sdkin TypeScript) still sendx-api-key. The AWS clients now use Bearer, and the Bedrock Mantle clients already did. - A client holds one credential. Because the key and the auth token now share a header, passing both raises an error:
ValueErrorin Python,TypeErrorin TypeScript,IllegalStateExceptionin Java,ArgumentExceptionin C#. With nothing passed in code, the client takes the first credential it finds, starting withANTHROPIC_API_KEYthenANTHROPIC_AUTH_TOKEN, and logs a warning once when both are set.
SDK type changes: MessageParam is a role-discriminated union
Across all SDKs, MessageParam is now a union of UserMessageParam, AssistantMessageParam and SystemMessageParam (and BetaMessageParam splits the same way). Requests carry the same fields and values as before (in Go and Java, only the key order in a beta system message changes); this is mainly a type-level change, but it does produce runtime errors in a few cases.
Python
Dict literals such as {"role": "user", "content": "Hello"} need no change. Calling MessageParam(...) or subclassing it now raises TypeError:
# Before (anthropic 1.x)
message = MessageParam(role="user", content="Hello")
# After (anthropic 2.0.0)
message: MessageParam = {"role": "user", "content": "Hello"}Type checkers also flag a dict whose role is a variable spanning more than one role, because it matches no single member of the union. Build one dict per role:
from typing import Literal
from anthropic.types import MessageParam
def to_message(role: Literal["user", "assistant"], text: str) -> MessageParam:
if role == "user":
return {"role": "user", "content": text}
return {"role": "assistant", "content": text}TypeScript
Literals that name their role type-check as before. Two patterns break: code that spreads a message of any role while replacing content, and interfaces that extend MessageParam (an interface can't extend a union):
// Before
interface StoredMessage extends Anthropic.MessageParam {
id: string;
}
const copy: Anthropic.MessageParam = { ...message, content: blocks };
// After
type StoredMessage = Anthropic.MessageParam & { id: string };
const copy: Anthropic.MessageParam =
message.role === "system" ? message : { ...message, content: blocks };Go, Java, C#, PHP, Ruby in brief
- Go:
MessageParamhasOfUser,OfAssistantandOfSystemfields;MessageParamRoleconstants are gone.NewUserMessage,NewAssistantMessageandToParamkeep working. Change every import to the/v2module path and rungo get github.com/anthropics/anthropic-sdk-go/v2. - Java:
MessageParam.builder()is gone; useMessageParam.ofUser("..."). Builder methods such asaddUserMessageOfBlockParamsbecomeaddUserMessageOfContentBlockParams. - C#: build
new UserMessageParam { Content = "..." }instead of settingRole = Role.User; theRoleenums are gone. - PHP:
roleis now the first positional argument ofMessageParam::with(), so positional calls in the old order fail;instanceof MessageParamis always false. - Ruby:
message.is_a?(Anthropic::MessageParam)returns false; usemessages.grep(Anthropic::MessageParam)andmessage.role.
Other SDK and CLI changes worth scanning
- Models methods: non-beta
client.models.list()/retrieve()no longer acceptbetas. Callclient.beta.models.list(betas=[...])instead. - TypeScript and Ruby tool runner: client-side
compactionControl/compaction_control:is removed in favor of server-side compaction viacontext_management(withbetas: ["compact-2026-01-12"]and an edit of typecompact_20260112), with a trigger threshold of at least 50,000 tokens. - One-value enums become constants in the C#, Go, Java, PHP and Ruby SDKs, mostly on Managed Agents types. Delete the redundant
Typefield when building objects and compare against the literal value when reading: a string in Go and PHP, a symbol in Ruby (:"agent.message"), and aJsonValuein Java (_type().equals(JsonValue.from("agent.message"))). - Java: interceptors now see
anthropic-versionbefore they run, so an interceptor that sets its own version must callreplaceHeaders; several getters now returnOptional. - CLI:
--api-keyand--auth-tokenare removed because flag values leak into shell history and process listings. If you install withgo install, the path is nowgithub.com/anthropics/anthropic-cli/v2/cmd/ant@latest; the old path silently keeps installing v1. - Platform-client and credential edge cases: the guide also covers smaller per-SDK changes, such as the removal of
DefaultVersionconstants on the Vertex and Bedrock clients in Go and Ruby, TypeScript platform clients rejecting credentials they don't authenticate with, and C# platform clients ignoring credentials set throughWithOptions. If you use a cloud platform client or override credentials per request, read your language's section of the official guide end to end.
Admin API and Enterprise Analytics API field renames
If you have internal tooling that syncs RBAC groups, audits API keys, or builds usage dashboards, these renames will break it quietly: responses under 2026-10-21 no longer include the old names at all.
| API | Endpoints | Old field | New field |
|---|---|---|---|
| Admin | RBAC groups (create, list, retrieve, update) | roles | role_ids |
| Admin | RBAC group members (list, add, remove response) | group_id | rbac_group_id |
| Admin | RBAC roles (list, retrieve) | name | display_name |
| Admin | Organization and workspace rate limits | group_type | group.type |
| Admin | API keys (list, retrieve, update) | workspace_id | scope (different values) |
| Enterprise Analytics | Per-user usage and cost reports | actor.email | actor.email_address |
| Enterprise Analytics | Activity summaries | summaries | data |
The API key change needs logic, not just a rename. Under 2023-06-01, workspace_id was null both for Default Workspace keys and for all-workspaces keys. Under 2026-10-21, a workspace-bound key (including the Default Workspace) has scope.workspace_id set to that workspace's real ID, and an all-workspaces key has scope of {"type": "organization"}. If your code treated null as "Default Workspace", look up the Default Workspace ID via List Workspaces with include_default=true and compare against it. The group_type query parameter and the workspace_id filter on list endpoints are unchanged.
Stopgap: keep specific calls on 2023-06-01
Anthropic's guide is explicit that a request relying on a non-default sampling value can stay on 2023-06-01. Because the new SDK majors no longer expose temperature, top_p or top_k, the practical way to do that is to keep those calls on the previous SDK major, which sends 2023-06-01:
# Python: stay on the 1.x line for now
pip install "anthropic>=1,<2"
# TypeScript: stay on the 0.x line for now
npm install "@anthropic-ai/sdk@<1"A pin applies to the whole service or virtualenv, not to individual calls: you can't run anthropic 1.x and 2.x side by side in one Python environment, and running two majors of @anthropic-ai/sdk in one Node app is awkward at best. Either isolate the temperature-dependent workload in its own service, or send just those requests over raw HTTP with anthropic-version: 2023-06-01 while the rest of the codebase moves to the new SDK.
If you call the API over raw HTTP, keep sending anthropic-version: 2023-06-01 on those requests and 2026-10-21 on the rest. Under Anthropic's versioning policy, existing input and output parameters are preserved for a given version, so pinned calls should keep behaving as they do today.
Treat this as a bridge, not a destination. Anthropic recommends the latest API version whenever possible, the newest models (Claude 4.7 and later) don't honor non-default sampling values anyway, and staying on an old SDK major means skipping the type and auth changes you'll eventually have to make. As of October 2026, the versioning page does not list a retirement date for 2023-06-01. If a workload genuinely needs low-variance output, invest in structured outputs, strict tool use, or tighter prompts rather than parking on the old version indefinitely.
Let Claude Code do the first pass: /claude-api api-version
Claude Code 2.1.307 added a /claude-api api-version command that moves Claude API code to API version 2026-10-21, according to the Claude Code changelog. For a repository with many call sites, running it on a branch is a reasonable way to produce a first-pass diff toward 2026-10-21. The changelog doesn't list exactly what it edits, so expect changes such as removed sampling parameters and SDK type updates, and check what it actually touched. Review that diff as you would any automated refactor. It can't know whether a temperature=0.2 was load-bearing for your eval suite, and it won't see a gateway config that lives in another repo. If Claude Code itself throws errors during the run, our Claude Code troubleshooting guide covers the common ones.
A rollout checklist that avoids surprise 400s
- Pin before you plan. If Dependabot or Renovate auto-merges minor and major bumps, pin
anthropic/@anthropic-ai/sdkbelow the new major until the migration branch is ready. - Upgrade packages in lockstep. In TypeScript, move
@anthropic-ai/bedrock-sdk,vertex-sdk,foundry-sdk,aws-sdkandgoogle-cloud-sdkto v1 together with the core SDK, or your package manager will install a second, older copy of@anthropic-ai/sdk. In Go, update every import path to/v2. - Fix the API-level breaks: remove sampling parameters, move documents out of assistant turns, strip user-side citations.
- Fix the SDK-level breaks: message types, one credential per client, Bearer auth in proxies and middleware, Text Completions call sites.
- Update Admin and Analytics consumers to the new field names and the
scopelogic for API keys. - Test against
2026-10-21in staging, then release the SDK bump and the code fixes together, as Anthropic's guide recommends. - Update license records from MIT to Apache 2.0 for the new SDK and CLI versions.
Inventory the call sites. Search for the patterns that break:
grep -rnE "temperature|top_p|top_k|completions\.create|MessageParam\(|[Xx]-[Aa]pi-[Kk]ey|citations" src/Also check shared config files, eval harnesses, and any LLM gateway or proxy repo.
If the same sprint also includes a model change, sequence it deliberately. Model migrations bring their own 400s (for example, forced tool_choice and manual thinking budgets are rejected on several 5.x models), so doing the API version and the model move as separate releases makes failures easier to attribute. Our Claude Opus 5.5 migration guide covers the model-side changes.
FAQ
Will my existing code break if I don't upgrade the SDK?
No. Earlier SDK and CLI versions send anthropic-version: 2023-06-01, and Anthropic's versioning policy preserves existing input and output parameters for a given version. The break happens when you install the new major (or set the header to 2026-10-21 yourself) without making the code changes.
Why does temperature=1.0 fail if 1.0 is the default?
Because the parameters are removed, not restricted. Under 2026-10-21, sending temperature, top_p or top_k at any value returns 400. Omitting them gives you the same default sampling.
Is there a replacement for low temperature?
Anthropic says no other setting replaces it. For newer models it was already moot, since Claude 4.7 and later accept only the defaults. For consistency, rely on structured outputs, strict tool use, and prompt design.
Does the Python SDK 2.0 remove Text Completions?
No. Anthropic's migration guide lists the Text Completions removal only for the TypeScript, Go, Java and Ruby SDKs and the ant CLI, not Python 2.0.0. Newer models and features still don't support Text Completions, so port to the Messages API anyway.
Do I need to change anything for Amazon Bedrock?
Yes, the same code changes apply on Bedrock, because the new SDKs' Bedrock clients send 2026-10-21 too. The extra Bedrock caveat: 2026-10-21 is available there only for Claude 4.7 and later, so keep older-model Bedrock traffic on your current SDK version until you switch models.
Is the license change a problem for commercial use?
The SDKs and CLI moved from MIT to Apache License 2.0, a permissive license widely used in commercial software. No code change is needed; update your dependency license inventory and check it against your organization's policy.
Sources
- Anthropic: Migrating to API version 2026-10-21
- Anthropic: API versions
- Anthropic: Claude Platform release notes
- Claude Code CHANGELOG (2.1.307)
If an SDK or model migration is competing with your roadmap, Codersera can help you hire vetted remote developers, Python and TypeScript included, to own the upgrade, from call-site inventory to staged rollout. Hire vetted remote Python developers.