Two changes to the Claude API landed within two weeks of each other, and both of them point at the same three request parameters: temperature, top_p and top_k.
On October 21, Anthropic released API version 2026-10-21. In that version, a Messages request that sends any of the three gets a 400 error, even when the value you send is the default. The new major versions of the official SDKs send that version by default and no longer have the three parameters at all.
On November 3, Anthropic's text watermark reached three older models: Claude Mythos 5, Claude Opus 4.7 and Claude Sonnet 4.6. On Sonnet 4.6, a request that sets any of the three to a non-default value might come back without the watermark. According to an Anthropic email to API customers reported by The Decoder, four more models (Opus 4.6, Opus 4.5, Sonnet 4.5 and Haiku 4.5) get the watermark starting November 16, with the same rule about sampling settings.
If your app, backend or internal tool calls Claude, there is a decent chance one of these touches you. Maybe you set temperature: 0 two years ago to get steadier JSON. Maybe a framework sets it for you. Maybe you never set it and none of this matters. This post walks through how to find out which case you are in, and what to change, with scripts you can run today.
What changed, from Anthropic's own pages
Everything in this section comes from Anthropic's release notes, its API migration guide, its platform docs and its support article on content marking.
Start with API version 2026-10-21. The migration guide says a request on this version "returns a 400 error, even at the parameter's default value" if it sends temperature, top_p or top_k. The rule covers POST /v1/messages, token counting, and each request inside a Message Batch created with the new version. Without the parameters, requests sample with the defaults, including a temperature of 1.0.
The guide also says models that already accept only the defaults, such as Claude 4.7 and later models, sample as before. On a model that still accepts other values, such as Claude Sonnet 4.6, a request that lowered temperature for more consistent output loses that effect. The guide says plainly that no other setting replaces it. Your two options are to stop sending the parameter or to keep that request on the older 2023-06-01 version.
The SDK releases that go with it are Python SDK 2.0.0, TypeScript SDK 1.0.0, Go SDK 2.0.0, Java SDK 3.0.0, C# SDK 13.0.0, PHP SDK 1.0.0, Ruby SDK 2.0.0 and the ant CLI 2.0.0. They also carry other breaking changes and a license change from MIT to Apache 2.0, so a major-version bump is more than a one-line edit.
One more detail matters if you use Amazon Bedrock: on Bedrock, 2026-10-21 is available only for Claude 4.7 and later models. If you call Opus 4.6 or an earlier model there, the guide says to stay on the SDK version you use today.
Then the text watermark. Anthropic's platform docs say text from supported models carries the watermark, it adds no tokens or hidden characters, and it carries no information about you or your organization. You don't need to change your requests for it. The one exception: "On Claude Sonnet 4.6, text generated for a request that sets temperature, top_p, or top_k to a non-default value might not carry the watermark." If you rely on the watermark, the docs say to omit all three, "including any that a framework or client library sets for you."
The support article lists which models carry the watermark today. As of November 6, it shows Fable 5.1, Fable 5, Mythos 5.1, Mythos 5, Opus 5.5, Opus 5, Opus 4.8, Opus 4.7, Sonnet 5.5, Sonnet 5, Sonnet 4.6 (with the sampling caveat) and Haiku 5.5 as covered on Anthropic's own platform. It still lists Opus 4.6, Opus 4.5, Sonnet 4.5 and Haiku 4.5 as not covered for text. The November 16 date for those four comes from the customer email, so check the support table again after that date before you build anything on it. On Amazon Bedrock, the article says the rollout for some models completes by November 10.
What the watermark is, and what it is not
It's easy to picture the watermark as something stuck onto the text, and that picture leads to bad decisions. Anthropic's announcement describes it as a variant of SynthID-Text, the method Google DeepMind published in Nature in 2024. When the model has several equally good next words, a secret key decides which one gets picked. Someone holding the key can later measure how well a passage matches those choices and estimate how likely it is that Claude wrote part of it.
That has some practical consequences:
- There is nothing to strip. Anthropic says nothing is added to the text and there are no hidden characters. Tools that promise to "remove the Claude watermark" by deleting zero-width characters are removing something that isn't there. At best they do nothing. At worst they damage text that legitimately contains those characters, such as emoji sequences or some scripts.
- Code and facts carry little of it. When only one word or token is correct, there is no free choice for the key to steer. Anthropic says watermarking in code is generally sparser and may show up in arbitrary choices such as comments.
- Short text is weak evidence. Detection confidence grows with length. A one-line push notification or a commit message will not tell anyone much.
- Detection is not public. The detection API is in private preview for eligible organizations: regulators, law enforcement, media, fact-checkers, researchers, educational organizations, EU civil society groups and enterprises checking their own output. You can't paste text into a public checker and get an answer.
- A positive result is only a likelihood. The support article says a detected mark means content may have been processed by Claude, and a missing mark does not rule Claude out.
- Files are separate. For supported files such as PNG and JPEG, Claude attaches a signed C2PA Content Credential in the metadata. That one you can check yourself with the free checker at claude.com/check-content.
The announcement also says the watermark adds no tokens, has a negligible effect on speed and does not change the price. So for most apps the watermark changes nothing you can see. The places it matters are compliance, and the sampling parameters.
Who should care about this
Anthropic says it added the watermark to meet the EU AI Act's rules on marking AI-generated content. Article 50 of the Act applies from August 2, 2026. Paragraph 2 puts the marking duty on providers of systems that generate synthetic text, images, audio or video: outputs must be "marked in a machine-readable format and detectable as artificially generated or manipulated." Paragraph 4 puts a separate disclosure duty on deployers in some cases, including text published to inform the public on matters of public interest, unless a human reviewed it and someone holds editorial responsibility.
Anthropic's support article tells organizations that deploy Claude in their own products to assess their own Article 50 obligations. I'm not a lawyer, and nothing here is legal advice. What I can say as an engineer is this: if your compliance team has written down "the model provider's watermark covers our marking," then the sampling settings on your requests are now part of that claim. A temperature: 0.2 buried in a helper function can quietly make it untrue on Sonnet 4.6, and after November 16 possibly on four more models.
If nobody in your company relies on the watermark, you still have the API version change. Sooner or later you will upgrade the SDK, and any request that still sends one of the three parameters will start failing with a 400.
Step 1: Find every place that sets a sampling parameter
Start with a plain search. This Python script walks a project, looks only at files that mention Anthropic or Claude, and prints every line that names one of the three parameters. It skips the usual build and dependency folders.
import pathlib, re, sys
ROOT = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else ".")
EXTS = {".py", ".ts", ".js", ".mjs", ".swift", ".kt", ".go", ".rb", ".json", ".yaml", ".yml", ".toml", ".env"}
SKIP = {"node_modules", ".git", "venv", ".venv", "Pods", "DerivedData", "build", "dist"}
PARAM = re.compile(r"\b(temperature|top_p|topP|top_k|topK)\b")
CLAUDE = re.compile(r"anthropic|claude", re.I)
for path in ROOT.rglob("*"):
if path.suffix not in EXTS or any(p in SKIP for p in path.parts) or not path.is_file():
continue
text = path.read_text(errors="ignore")
if not CLAUDE.search(text):
continue
for n, line in enumerate(text.splitlines(), 1):
if PARAM.search(line):
print(f"{path}:{n}: {line.strip()[:120]}")
Run it as python3 scan_sampling.py path/to/repo. On a small test project with one Python call and one JSON config, it printed:
demo/config.json:1: {"llm": {"provider": "anthropic", "model": "claude-sonnet-4-6", "top_p": 0.95}}
demo/src/app.py:3: r = client.messages.create(model="claude-sonnet-4-6", max_tokens=300, temperature=0.9, messages=[])
Notice the config file. Sampling settings often live in YAML or JSON files, environment variables or a database row that an admin screen writes to. A code-only search misses those. The script includes config extensions for that reason, but it can't see values stored outside the repo, so ask whoever runs your deployment where model settings come from.
The search will also give false positives, for example a temperature field in a weather app that happens to mention Claude in a comment. Read each hit. It takes a few minutes, and it's the step that finds the surprises.
Step 2: Check what actually leaves your server
A search tells you what the code says. It doesn't tell you what a framework adds on its way out. The platform docs warn about exactly this: parameters "that a framework or client library sets for you." The reliable check is to log the outgoing request body.
With the Python SDK, you can pass your own HTTP client and attach a request hook. Version 2 of the SDK (I tested 2.6.0) is built on the httpx2 package; older versions use httpx, and the hook works the same way. This test uses a mock transport, so it makes no network calls and needs no real key:
import json, anthropic
import httpx2 as httpx # SDK 2.x uses httpx2; on 0.x SDKs use "import httpx"
def show_sampling(request: httpx.Request):
if request.url.path.endswith("/v1/messages"):
body = json.loads(request.content)
sent = {k: body[k] for k in ("model", "temperature", "top_p", "top_k") if k in body}
print("outbound:", request.headers.get("anthropic-version"), sent)
def fake_api(request): # stands in for the real API in this test
return httpx.Response(200, json={"id": "msg_test", "type": "message", "role": "assistant",
"model": "claude-sonnet-4-6", "content": [{"type": "text", "text": "ok"}],
"stop_reason": "end_turn", "stop_sequence": None,
"usage": {"input_tokens": 1, "output_tokens": 1}})
http = httpx.Client(transport=httpx.MockTransport(fake_api), event_hooks={"request": [show_sampling]})
client = anthropic.Anthropic(api_key="test", http_client=http)
client.messages.create(model="claude-sonnet-4-6", max_tokens=64,
messages=[{"role": "user", "content": "hi"}])
# a wrapper that still injects temperature, the way an old helper might
client.messages.create(model="claude-sonnet-4-6", max_tokens=64,
extra_body={"temperature": 0.7},
messages=[{"role": "user", "content": "hi"}])
The output:
outbound: 2026-10-21 {'model': 'claude-sonnet-4-6'}
outbound: 2026-10-21 {'model': 'claude-sonnet-4-6', 'temperature': 0.7}
Two things show up here. First, SDK 2.6.0 sends anthropic-version: 2026-10-21 by default, which matches the migration guide. Second, the SDK no longer accepts temperature as a named argument (calling messages.create(..., temperature=0.7) raised a TypeError in my test), but anything passed through extra_body still goes out. Against the real API on the new version, that second request would get a 400. So an old wrapper that pushes settings through extra_body will fail at runtime, after the code compiles and passes a type check.
In staging, swap the mock transport for a normal client, keep the hook, and run through your main features once. Every line that prints a sampling key is a request you need to decide about.
Step 3: Decide what each call should do
Every call you found falls into one of a few cases. Here is how I'd sort them:
| Your situation | What to do |
|---|---|
| Model is Claude 4.7 or later, and the call sends a sampling parameter | Remove it. The migration guide says these models already accept only the defaults, so the setting was doing nothing for you. |
| Sonnet 4.6, call sets a non-default value, and someone relies on the watermark | Remove the parameter, or move the call to a newer model. Keeping both the custom value and the claim that the output is watermarked doesn't hold up. |
| Sonnet 4.6, non-default value, nobody relies on the watermark, and the lower temperature really matters | You can keep the request on 2023-06-01 for now. Write down why, and treat it as debt with a date on it. |
| Opus 4.6, Opus 4.5, Sonnet 4.5 or Haiku 4.5 | Treat them like Sonnet 4.6 from November 16, based on Anthropic's email. Confirm on the support page after that date. |
| Older model on Amazon Bedrock | Don't move it to 2026-10-21; Bedrock offers that version only for Claude 4.7 and later. Plan a model upgrade first. |
| The parameter is set to the default value, "just to be explicit" | Delete it. On the new version, even the default value causes a 400. |
Step 4: Put a guard in the one place every request passes through
Most apps have, or should have, one function that builds Claude requests. That's where to enforce the rule, so the next person who copies an old snippet from a blog post can't bring the parameters back.
SAMPLING_KEYS = ("temperature", "top_p", "top_k")
WATERMARK_SENSITIVE = {"claude-sonnet-4-6", "claude-opus-4-6", "claude-opus-4-5",
"claude-sonnet-4-5", "claude-haiku-4-5"}
def compliant_params(params: dict, rely_on_watermark: bool = True) -> dict:
"""Return a copy without sampling overrides for models where they can drop the mark."""
if not rely_on_watermark:
return dict(params)
model = params.get("model", "")
if not any(model.startswith(m) for m in WATERMARK_SENSITIVE):
return dict(params)
dropped = [k for k in SAMPLING_KEYS if k in params]
if dropped:
print(f"note: dropped {dropped} for {model}")
return {k: v for k, v in params.items() if k not in SAMPLING_KEYS}
Running it on a Sonnet 4.6 request with temperature: 0.2 printed note: dropped ['temperature'] for claude-sonnet-4-6 and returned the request without it. A request for claude-opus-5-5 passed through unchanged, because that's a different problem: on the new API version it would fail anyway, which you want to catch in tests, not hide.
Two notes on this guard. Use your logger instead of print in production, and count how often it fires; a guard that fires a thousand times a day means some code path still thinks it controls sampling. And once your whole codebase is on SDK 2.x or later, you can simplify it to "never send these keys", because the API version settles the question for you.
Step 5: Replace what temperature was doing for you
The usual reason to lower temperature is more consistent output. A support bot that phrases the refund policy the same way every time. A classifier that returns the same label for the same ticket. A JSON extractor that doesn't wander.
The migration guide is clear that no other setting replaces it. So the consistency has to come from the rest of your system:
- Tighter instructions and examples. Two or three worked examples of the exact output you expect usually do more for consistency than a low temperature did.
- Validate, then retry. If you need JSON, parse it and check it against a schema in your own code. On failure, retry once with the error message included. Log how often that happens.
- Keep the fixed parts out of the model. If the refund policy must read the same every time, store it as text and have the model decide when to show it, instead of having the model rewrite it.
- Measure before and after. Pick 20 to 50 real inputs, run them through the old and the new request shape, and compare. You'll know within an hour whether removing temperature changed anything your users would notice.
That last step matters most. You may find your "temperature 0" setting was a habit and the outputs look the same without it. Or you may find one feature that really did depend on it. Either way, a test set tells you before a user report does.
Step 6: Upgrade the SDK when you decide to
The quickest way to get surprised by the 400 is an unpinned dependency. If your requirements.txt says anthropic with no version, or your package.json uses a caret range across a major version, a fresh build can pull the new major and change your API version without anyone choosing it.
- Pin the current major version today, so builds stay where they are.
- Run the search and the logger from Steps 1 and 2.
- Remove or decide on each sampling parameter.
- Read the rest of the migration guide. Besides sampling, it lists document blocks in assistant messages and citations in user messages as new 400 errors, plus SDK changes such as role-specific message types and API keys sent as a bearer token.
- Bump the major version in a branch, run your tests and your 20-to-50 input comparison, then ship.
If you build iOS or Android apps on Claude
Mobile developers mostly meet this change on the server side, and that's where it should be. An app that calls the Claude API straight from the device has to ship the API key inside the app bundle, and anyone with the binary can pull it out. The usual setup is a small backend that holds the key and forwards requests. That backend is where the search, the logger and the guard belong.
If your app does call the API directly with URLSession or a third-party Swift or Kotlin package, you set the anthropic-version header yourself, or the package sets it. Check which one it sends, and check whether the request body includes any of the three parameters. A shipped app can't be patched as fast as a server, and users don't always update. An app build that sends temperature on the new version will fail on every request until people install a fix. That's one more reason to put a server in front of the API.
What I wouldn't do
- Don't install "watermark remover" tools. By Anthropic's own description there are no hidden characters to remove. And deliberately trying to defeat a compliance marker in a product you ship is a conversation you don't want to have with your legal team.
- Don't use the watermark to judge your users. If you run a school or a writing platform, the detector isn't public, short texts give weak signals, and Anthropic itself says a mark is not conclusive. Accusing someone on that basis would be unfair to them.
- Don't hard-code the model list from this post. The support table will change as rollouts finish. Link to it in your code comments and read it again when you upgrade models.
- Don't stay on
2023-06-01forever by accident. Keeping one request on the old version can be a fine choice. Keeping the whole app there because nobody looked is how next year's migration turns into an emergency.
A short checklist
- Search the repo and config for
temperature,top_pandtop_knear Claude calls. - Log outbound request bodies in staging and confirm what really gets sent, including the
anthropic-versionheader. - For Claude 4.7 and later, delete the parameters.
- For Sonnet 4.6, and from November 16 the four older models, decide whether you rely on the watermark. If you do, omit the parameters.
- Add one guard where requests are built.
- Pin your SDK major version, then upgrade on purpose with a before-and-after test set.
- On Bedrock with Opus 4.6 or earlier, stay on your current SDK until you move models.
- Check the support article's model table again after November 16.
If you'd like a structure for this kind of change, the Opus 5.5 Coding Workflow & Cost Kit on my Gumroad store is a set of Markdown templates (project brief, implementation plan, session handoff, bug investigation and final code review), a 13-page PDF guide, and an Excel calculator for estimating standard Claude API token charges. It's built for working with AI coding tools on real code, and the templates fit a migration task like this one well. It doesn't include API access or check your account.
Sources
- Claude Platform Docs: Migrating to API version 2026-10-21
- Claude Platform Docs: Release notes (October 21 and November 3, 2026 entries)
- Claude Platform Docs: Text watermark
- Claude Help Center: How Claude marks AI-generated content (model table read November 6, 2026)
- Anthropic: How Claude's text watermark works
- The Decoder: Anthropic's text watermark is easy to bypass on several older Claude models (reports Anthropic's email with the November 16 date)
- EU AI Act, Article 50: Transparency obligations

Comments