9io.ai / Blog

Claude message threads: what they speed up and what can break

Anthropic’s message threads beta stops agents resending the whole conversation. What it changes, the errors to handle, and when to leave it off.

Key takeaways

  • Message threads let a Claude agent send only new messages each turn; the server keeps the rest for a short time.
  • Anthropic reports about 35% lower median time to first token on 500K to 1M token requests.
  • Threads change no prices or rate limits: every turn is still billed for the full conversation.
  • A thread expires 5 minutes after its last request, so every client needs a replay path.
  • Threads aren’t ZDR or HIPAA eligible and don’t work with batches, token counting or compaction.

Anthropic opened message threads to the public on 2 November 2026, as a beta of the Messages API.1 With a thread, an agent sends its full conversation once and then sends only the new messages on each turn. The server holds the rest in memory for a few minutes. Anthropic reports that, on requests of 500,000 to 1 million tokens, median time to first token drops by about 35%.1 That number is Anthropic’s own, and the announcement doesn’t say how it was measured.

Prices and rate limits stay the same, because the model still reads the whole conversation on every turn.2 A thread can disappear at any moment, so your code keeps its own copy of every message and needs a way to replay it. Several features don’t work on a thread at all, and it isn’t eligible for zero data retention (ZDR) or HIPAA.

This post covers what a thread does on each request, which fields lock once a thread starts, the errors your code has to handle, how it compares with OpenAI’s and Google’s stateful APIs, and when we’d turn it on. We haven’t run threads in production yet. Everything below comes from Anthropic’s announcement and documentation, read on 4 November 2026.

What a message thread changes on each request

Without a thread, every turn of an agent loop uploads the whole conversation again, and the API validates and tokenises all of it again.2 In a coding agent that has read twenty files and run forty tool calls, most of each request is history the API has already seen. Anthropic’s announcement says coding agents can make hundreds of requests in one session, and that resending conversations of hundreds of thousands of tokens adds delay before Claude starts to reply.1

A thread moves that history to the server. You add one field, thread, to the request:

  • Create. The first request is the usual one, with the full conversation, system and tools, plus "thread": {"type": "create"}. The API stores the conversation and Claude’s reply under the response id.
  • Continue. Later requests send only the messages since the previous response, plus "thread": {"type": "continue", "previous_message_id": "<id>"}. They leave system and tools out, because the thread kept them.

Every request needs the beta header message-threads-2026-08-12.2 The response is an ordinary message. A thread is append-only and each response id can be continued once, so you always continue from the latest one. To edit or branch an earlier turn, you start a new thread.

The docs name three workloads that gain the most: long-running coding agents, computer use agents whose screenshots would otherwise be uploaded again, and workflows heavy with documents and images.1 A short support chat of six turns gains little, since its requests were small to begin with.

The fields that lock when a thread starts

The documentation splits request fields into three groups, and the split decides how much of your existing code needs to change.2

Group Fields Rule
Create-only system, tools Sent on the create request only. Sending either on a continue request, even as null, returns a 400.
Fixed model, tool_choice, thinking, mcp_servers (apart from auth tokens), context_management, output_config, container skills, top-level cache_control, the set of beta headers Sent on every request with the values from the create request. A change returns a 400 with thread_fingerprint_mismatch.
Free max_tokens, stop_sequences, stream, metadata, service_tier and others Can change every turn.

Source: Anthropic, “Message threads”, read 4 November 2026.

Two consequences are easy to miss. First, an agent that swaps its tool list between phases (planning tools, then editing tools) can’t do that inside one thread. It has to start a new thread with the full conversation and the new tools. Second, the API doesn’t store MCP server URLs, MCP authorisation tokens or the container id, so those still go on every request that uses them.2 A short-lived MCP token can be refreshed between turns.

On Claude Fable 5.1, Mythos 5.1, Opus 5.5, Sonnet 5.5 and Haiku 5.5, a new thread with a changed system or tools can also hit a 400 because the earlier thinking blocks no longer match. The docs’ fix is to drop every thinking and redacted_thinking block from the conversation you resend, at the cost of the model losing that earlier reasoning.2

Threads expire, so build the replay path first

A thread is best-effort. It expires 5 minutes after its last request, each stored response restarts the 5 minutes, and the API deletes it no more than 1 hour after its last use.2 When the API can’t continue a thread, a continue request returns a 404 with error.details.error_code set to thread_not_found, and the response doesn’t say why.

The docs list six causes, and most have nothing to do with a slow user.

  • The thread expired.
  • The previous turn was served but not stored. A response that ends in a tool call cut off by max_tokens is one example.
  • A fallback model wrote the previous response.
  • The previous response is still generating.
  • The request came from a different organisation, workspace, user or service account.
  • The new messages add something the stored conversation can’t take, such as the first image in a conversation that had none.

No response tells you whether the API stored that turn. You find out on the next continue request. Anthropic’s advice is that your application stays the source of truth: keep your own copy of the conversation and resend it in a new create request when a continue fails.2 The SDK thread helper keeps that copy in memory only, so a conversation that has to survive a process restart needs your own store.

The other thread errors arrive as 400s before the model runs, use no tokens and leave the thread as it was:

Error code What it means What to do
thread_fingerprint_mismatch A fixed field changed; the message names it Keep the field the same or start a new thread
thread_already_continued That response id was already continued Continue from the latest id, or replay
thread_continue_in_flight Another continue from that id is running Let it finish, or replay
thread_unsupported_request A feature or org setting threads don’t support Remove it, or send the request without thread

Source: Anthropic, “Message threads”, Errors section, read 4 November 2026.

A dropped stream is the case most likely to catch a team out. The response id arrives in message_start, but you may continue from it only after message_stop.2 If a stream drops partway, the server may already have saved the turn, so a retry can come back as thread_already_continued. Replaying is always safe. The SDK helper replays once by itself on thread_not_found, thread_already_continued and thread_continue_in_flight, but it doesn’t retry a continue after a dropped stream.

If you build requests yourself, the loop looks something like this. It’s a sketch written for this post, with the replay rule from the docs:

def next_turn(client, state, new_messages):
    state.history.extend(new_messages)
    if state.last_id:
        try:
            reply = client.beta.messages.create(
                **state.fixed, messages=new_messages,
                thread={"type": "continue", "previous_message_id": state.last_id},
                betas=["message-threads-2026-08-12"])
        except anthropic.NotFoundError as e:
            if not is_thread_not_found(e):  # a bad model name is also a 404
                raise
            reply = None
    if not state.last_id or reply is None:
        reply = client.beta.messages.create(
            **state.fixed, system=state.system, tools=state.tools,
            messages=state.history, thread={"type": "create"},
            betas=["message-threads-2026-08-12"])
    state.history.append({"role": "assistant", "content": reply.content})
    state.last_id = reply.id
    return reply

Check error.details.error_code before replaying, because a request for a model that doesn’t exist also returns a 404.2 If a continue still fails straight after a replay, the conversation may be too large for the API to store. Send it without thread; the SDK helper does this when created with unsupported_request_behavior set to continue_unthreaded.

Refusals and fallbacks need their own branch

Two stop conditions change how a thread continues.2

After a response with stop_reason: "refusal", don’t continue the thread. The API may already hold the refused turn, and a continue request can’t remove it. Start a new thread that leaves out or rephrases the turn that led to the refusal. The SDK helper does this for you.

Server-side fallback works with threads on the Claude API, with one rule: send fallbacks on every request of a thread or on none. A thread keeps one model, so the API never stores a turn written by a fallback model. When usage.iterations contains a fallback_message entry, you resend the full conversation in a new create request, either to the original model again or as a new thread on the fallback model. The docs add that sticky routing can send create requests for the same conversation straight to the fallback model for about an hour, in which case every turn sends the full conversation and the latency gain disappears.2 If your agent sits near a model’s refusal boundary, measure how often this happens before you count on the speed-up.

Prompt caching still decides the bill

A thread saves the upload, validation and tokenisation of the history on each turn. The cost of the model reading that history is a separate matter, and prompt caching is what reduces it. Anthropic is explicit that a thread doesn’t cache anything for you.2

The simplest setup is a top-level cache_control on every request of the thread. The API applies it to the last cacheable block of each request’s messages, so each turn reads the earlier conversation from the cache and writes the new turn to it. Because the top-level cache_control is a fixed field, you send the same value on every request. A thread request can carry at most 4 breakpoints, and breakpoints on the create request’s system and tools count again on every continue request. One too many returns thread_unsupported_request. When you replay a conversation into a new thread, strip the breakpoints from earlier messages and keep one on the last block.

If you haven’t set up caching on a long agent loop yet, that change probably saves more money than threads save time. Our prompt caching comparison covers how breakpoints, TTLs and cache prices differ across Claude, OpenAI and Gemini.

What threads don’t support

The docs list these as unsupported:2

  • Message Batches: a batch item with thread fails with invalid_request_error.
  • Token counting: count the full conversation, with system and tools, instead.
  • Compaction, including compaction on demand. To shorten a thread, write the summary yourself and start a new thread from it.
  • Context editing, apart from clear_thinking_20251015 with keep: "all".
  • Organisations with HIPAA readiness or inference hooks turned on.

There’s also no API to list, read or delete a thread.2 On data handling, a thread is held in memory, encrypted under a server-managed key, never written to durable storage and deleted no more than 1 hour after its last use. It still isn’t ZDR eligible, though using it doesn’t affect your other ZDR-eligible features. Files are held by reference, so every file the conversation uses has to stay available while the thread is in use.

For teams in health or finance, that settles it for now: if your organisation runs under ZDR or HIPAA terms, keep sending the full conversation. Our post on healthcare app development with AI covers what those terms mean for LLM features more generally.

How it compares with OpenAI and Gemini

OpenAI and Google both offer server-side conversation state, but their designs store much more for much longer.

Claude message threads OpenAI Responses previous_response_id Gemini Interactions previous_interaction_id
Status Public beta since 2 Nov 2026 Generally available Generally available since June 2026
Where state lives In memory only Stored response objects Stored interactions
How long Expires 5 min after last request; deleted within 1 hour 30 days by default; Conversations API items have no 30-day limit 55 days on paid tier, 1 day on free tier
System prompt and tools Kept from the create request Not stated on the conversation state page Resent on every turn
Billing of history Full conversation, unchanged prices All previous input tokens billed as input Not stated on the overview page
Opt out of storage Don’t send thread store: false store: false, which also disables chaining

Sources: Anthropic, “Message threads”; OpenAI, “Conversation state”; Google AI Studio, “Interactions overview”. All read 4 November 2026.

OpenAI and Google built state you can come back to days later. Anthropic built a short-lived cache of the request body and tells you to keep the real record yourself.234 For an agent loop that runs for an hour and then ends, the short window is fine. For a chat product where a user returns tomorrow, a Claude thread won’t be there, and your database is the conversation store either way.

If your product routes between providers, this matters for the abstraction. A wrapper that assumes “send only the new turn” works everywhere will break on Claude after five idle minutes unless it replays. Design the interface around your own copy of the history and treat each provider’s server state as an optimisation.

When we’d turn threads on

We’d use threads for agent loops that grow past roughly 100,000 tokens and run turns in quick succession: coding agents, computer use, and document review that keeps large PDFs or images in context. Anthropic’s 35% figure is for 500K to 1M token requests, so the gain on shorter conversations is unknown until you measure it.1 If you already use the SDK’s beta tool runner, it uses a thread by default, so check whether you’re on threads before you add them.2

We’d leave them off for short chats, batch jobs, any organisation under ZDR or HIPAA, and agents that change their tools or system prompt between phases.

Before switching a production agent over, check these:

  1. Your code keeps the full conversation and can replay it in a create request.
  2. You handle thread_not_found by reading error_code, since a wrong model name is also a 404.
  3. Fixed fields come from one place, so a config change can’t trigger thread_fingerprint_mismatch mid-session.
  4. Streaming code continues only after message_stop, and replays after a dropped stream.
  5. Refusals and fallback responses start a new thread.
  6. Prompt caching is on, with a top-level cache_control and no more than 4 breakpoints.
  7. You measure time to first token on your own traffic, with and without thread, at the conversation lengths you actually see.

The beta header is dated 12 August 2026 and the feature is still in beta, so field names and limits may change before it is generally available. Recheck the docs before you build on the details.


  1. Anthropic, “Make long-running agents faster with message threads”, 2 November 2026, https://claude.com/resources/articles/message-threads ↩↩↩↩↩

  2. Anthropic, “Message threads”, Claude Platform Docs, read 4 November 2026, https://platform.claude.com/docs/en/build-with-claude/message-threads ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  3. OpenAI, “Conversation state”, OpenAI API docs, read 4 November 2026, https://developers.openai.com/api/docs/guides/conversation-state ↩

  4. Google, “Interactions overview”, Google AI Studio docs, read 4 November 2026, https://aistudio.google.com/docs/interactions-overview ↩

Frequently asked questions

What are Claude message threads?

A beta feature of the Messages API, opened to the public on 2 November 2026. You send the full conversation once with a thread parameter, then send only the new messages and the previous response’s id on each later turn. The API rebuilds the conversation on the server before it calls the model.

Do Claude message threads reduce token costs?

No. Anthropic says threads change neither pricing nor rate limits, and the model still reads the whole conversation on every turn. To cut the cost of the repeated history, use prompt caching, which works on a thread when you send a top-level cache_control on every request.

How long does a Claude message thread last?

It expires 5 minutes after its last request, and each stored response restarts the 5 minutes. The API deletes it no more than 1 hour after its last use. After that, a continue request returns a 404 with the code thread_not_found and you resend the full conversation.

Are Claude message threads ZDR or HIPAA eligible?

No. Anthropic’s docs list threads as not eligible for zero data retention or HIPAA, and organisations with HIPAA readiness enabled get a 400 error. Using threads doesn’t affect other ZDR-eligible features.

How are Claude message threads different from OpenAI’s previous_response_id?

Both let you send only the new turn. OpenAI stores response objects for 30 days by default and bills all previous input tokens in the chain. Claude keeps a thread in memory only, expires it after 5 minutes idle and also bills the full conversation.

Does Claude Code use message threads?

Yes. Anthropic’s announcement says threads are on by default for most Claude Code users, and the SDK’s beta tool runner uses a thread by default unless you turn it off.

Work with us

Building something like this?

9io is a small team of senior engineers with a fractional CTO. We work by the hour, and plans start at $5,000. Send us a note about your product. The reply comes from the person who'd do the work.