News

Claude Message Threads: Send Only New Messages, Replay on 404

Last reviewed:

On November 2 Anthropic put message threads into beta: the API keeps an agent's conversation on the server, and each later turn sends only what is new. The catch is in the rules. A thread is best-effort, lives 5 minutes, rejects changed settings and is not ZDR or HIPAA eligible.

A long row of orange-lit hollow frames sits on a dark track behind a single glowing cube, beside a dark tower holding a small hourglass above a cyan-edged glass platform.
A thread keeps the earlier turns on the server for a short time, so each request carries only the newest messages, and it expires 5 minutes after the last request.

What the November 2 release note says

Anthropic's release note for November 2, 2026 says long conversations and agent loops on the Messages API "can now send smaller requests and get a lower time to first token" with message threads, in beta with the message-threads-2026-08-12 beta header. The API keeps the conversation on the server, so each later request sends only the new messages. Threads are available on the Claude API, Claude Platform on AWS, Claude Models on Google Cloud (Anthropic managed) and Claude in Microsoft Foundry.

The Message threads page explains where the gain comes from. Without a thread, every turn uploads the whole conversation again, and the API validates and tokenizes all of it again; with a thread, most of that work covers only the new turn. Anthropic publishes no latency figures, and neither do we. Measure time to first token on your own longest loops before and after. If the Messages API itself is new to you, start with our Claude API guide; this report covers only the new feature.

Create, then continue

A thread is one extra field, thread, on an ordinary Messages request. The first request carries the full conversation, the system prompt and the tools, plus "thread": {"type": "create"}. The API stores the conversation, including Claude's reply, under the response id. Each later request sends only the messages since that response and "thread": {"type": "continue", "previous_message_id": "<previous id>"}, with system and tools left out. This is Anthropic's own two-turn example:

# Turn 1: send the full conversation and create a thread
curl -sS --fail-with-body https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: message-threads-2026-08-12" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 1024,
    "cache_control": {"type": "ephemeral"},
    "system": "You are a support agent for Acme. Keep replies short.",
    "messages": [{"role": "user", "content": "Hi! I have a question about an order."}],
    "thread": {"type": "create"}
  }'

# Turn 2: send only the new message. Set message_id to the id of the turn 1 response.
# Leave out system and tools: the thread keeps them.
curl -sS --fail-with-body https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: message-threads-2026-08-12" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 1024,
    "cache_control": {"type": "ephemeral"},
    "messages": [{"role": "user", "content": "How long does standard shipping usually take?"}],
    "thread": {"type": "continue", "previous_message_id": "'"$message_id"'"}
  }'

The beta header goes on every request of a thread. A thread is append-only, and each response id can be continued once, so always continue from the latest one. Do not resend messages the thread already holds: the API does not check them, so a repeated message can appear twice or the request can fail. The SDK thread helper builds these requests, adds the header and leaves out system and tools for you. The SDK's beta tool runner also uses a thread by default, which you can turn off when you create the runner.

Three field groups decide what you send

The page sorts request fields into three groups. Get them wrong and the API answers with a 400 error before the model runs.

GroupFieldsRule
Create-onlysystem, toolsSend on the create request only. Sending either one on a continue request, even as null, returns a 400 error.
Fixedmodel, tool_choice, thinking, mcp_servers (apart from each server's authorization token), context_management, output_config, the skills in container, the top-level cache_control, and the set of anthropic-beta valuesSend on every request with the same values as on create. A different value returns a 400 error with the code thread_fingerprint_mismatch, and the message names the fields that differ.
Freemax_tokens, stop_sequences, stream, metadata, service_tier, and other fields the page does not nameCan change from turn to turn.

The fixed list is where agent frameworks get caught. A loop that switches tool_choice to force a final answer, raises the thinking budget on hard turns or adds a beta header halfway through now breaks the thread. If you use server-side fallback, fallbacks follows a similar rule: send it on every request of a thread or on none. The API also does not store MCP server URLs, MCP authorization tokens or the container id, so send mcp_servers and container on every request that uses them. To change any create-only or fixed field, start a new thread with the full conversation and the new values.

Build the replay path before you adopt threads

A thread is best-effort. It expires 5 minutes after its last request, each response the API stores restarts the 5 minutes, and the API deletes it no more than 1 hour after its last use. No response, to a create or a continue request, says whether the API stored that turn; you find out on the next continue request. Anthropic's advice is to keep your application as the source of truth, with your own copy of every message.

When the API cannot continue a thread, the continue request returns a 404 with error.details.error_code set to thread_not_found, and the response does not say why. The documented causes include an expired thread, a turn that was served but not stored (for example a response that ends in a tool call cut off by max_tokens), a response written by a fallback model, a previous response still generating, and a request from a different organization, workspace, user or service account. A request for a model that does not exist also returns a 404, so check the error code before you replay.

The replay itself is simple: send the full conversation from your copy in a new create request, with system and tools, and continue from the new response id. Two other codes lead to the same path after a dropped stream. Continue from a streamed response only after message_stop; if the stream of a continue request drops, send the same request again, and if that returns thread_already_continued or thread_continue_in_flight, replay. The SDK helper replays once by itself, but it keeps its copy only in memory while the helper object exists. If the conversation has to outlive your process, it has to live in your storage.

A cheap test before rollout: run one long agent loop, pause it for longer than 5 minutes mid-task, and confirm that the next turn gets thread_not_found, replays from your stored copy and completes the task.

Caching and cost do not change by themselves

The page is explicit: threads do not change pricing or rate limits, and a thread does not cache your prompt for you. Prompt caching works as it does without a thread, with breakpoints you choose. To cache each turn, send the same top-level cache_control on every request; it is a fixed field. A request with thread can carry at most 4 cache_control breakpoints, the top-level one counts as one, and breakpoints on the create request's system and tools count again on every continue request. One too many returns a 400 error with the code thread_unsupported_request. When you replay, remove breakpoints from earlier messages and keep one on the last block. Our prompt cache hit rate guide covers how to measure whether those breakpoints actually hit.

Data handling can rule it out

Message threads are not ZDR (zero data retention) eligible and not HIPAA eligible. The API holds a thread in memory, encrypted under a server-managed key, never writes it to durable storage, and deletes it no more than 1 hour after its last use. Using threads does not affect your other ZDR eligible features, but organizations with HIPAA readiness enabled get thread_unsupported_request, and so do organizations with Inference hooks turned on. There is no API to list, read or delete a thread. Because the beta tool runner turns threads on by default, a team with a ZDR commitment should check that setting before upgrading its SDK.

Threads also do not work with Message Batches, token counting, compaction or context editing, apart from clear_thinking_20251015 with keep: "all". To shorten a long conversation, write the summary yourself and start a new thread from it. Agents that rely on server-side compaction today should stay unthreaded.

FAQ

Do Claude message threads lower the token bill?

Not by themselves. Anthropic's page says threads do not change pricing or rate limits, and a thread does not cache your prompt for you; caching still depends on the cache_control you send.

How long does a Claude message thread last?

A thread expires 5 minutes after its last request, and each response the API stores restarts those 5 minutes. The API deletes it no more than 1 hour after its last use.

Can I use message threads with zero data retention or HIPAA?

Message threads are not ZDR eligible and not HIPAA eligible, and organizations with HIPAA readiness enabled cannot use them. Using threads does not affect your use of other ZDR eligible features.

Can I switch models in the middle of a thread?

No. model is a fixed field, so a different value returns a 400 error with the code thread_fingerprint_mismatch. Start a new thread with a create request that sends the full conversation instead.

Sources checked 2026-11-03