Claude message threads: faster TTFT, same token bill
Here's something I never thought much about until Anthropic put a number on it. In a long agent session, every request ships the entire conversation back to the API, including every file the agent read and every tool result it got back. Anthropic says the median request in long conversations was about 800 KB. With Claude message threads, it's 1.8 KB.
Message threads went into public beta on the Messages API on November 2, 2026. The API keeps your conversation on its side for a few minutes, so each turn you send only what's new and Claude starts answering sooner. Your token bill doesn't change at all. Plenty of people missed that part at launch.
I've been through the docs, the GitHub issues and the early reactions. Here's how threads work, where prompt caching fits, the five-minute catch, and when I wouldn't bother with them.
What are Claude message threads?
Claude message threads are a beta Messages API feature that keeps your conversation in memory on Anthropic's servers for five minutes after each request. You send the full conversation once, then only the new messages plus the previous response's ID. That cuts time to first token on long contexts. Token usage and pricing stay the same.
Mechanically it's one new parameter, thread, on an ordinary Messages API request, plus the message-threads-2026-08-12 beta header. The response is the usual message. The docs list it as beta on every model, on the Claude API, Claude Platform on AWS, Microsoft Foundry and Claude Models on Google Cloud (the Anthropic-managed one). Amazon Bedrock isn't on that list, in case you were about to ask.
Not the same as the other Claude threads
Search for "Claude threads" and you'll trip over at least two other things. Claude Managed Agents has session threads, which are persistent, separate event streams for each agent in a multiagent session, with their own endpoints. The message threads docs go out of their way to say the two are different. Then there's the Claude Slack app, which replies in Slack threads and has nothing to do with any of this. Three features, one word. Naming things is still the hardest problem in computer science.
What it means for Claude Code
If you use Claude Code, you're very likely on threads already. Anthropic switched them on by default for most Claude Code users and reports that median time to first token on requests with 500k to 1M tokens dropped by about 35%. Read that scope carefully. It's the median, on very big requests, and it doesn't mean "Claude Code is 35% faster". You don't need to change anything either.
If Claude Code felt slow to start answering deep into a long session, this is aimed straight at that. It also wasn't new on November 2. Proxy maintainers were seeing Claude Code send the thread beta header weeks earlier.
How message threads work: create, continue, replay
There are three operations, and you'll need all three.
- Create. Send the full conversation with
systemandtools, exactly as you do today, and add"thread": {"type": "create"}. The API stores the conversation, Claude's reply included, under the responseid. - Continue. Send only the messages since the previous response, and add
"thread": {"type": "continue", "previous_message_id": "<id>"}. Leavesystemandtoolsout, because the thread already has them. - Replay. When a continue comes back as a 404 with
thread_not_found, send the full conversation again as a new create and carry on from that response'sid.
In raw request terms, turn two of a coding agent looks something like this:
{
"model": "claude-opus-5-5",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [{ "type": "tool_result", "tool_use_id": "toolu_...", "content": "..." }],
},
],
"thread": { "type": "continue", "previous_message_id": "msg_..." },
}That's the whole payload. No system prompt, no tool schemas, no 400k tokens of history. Send the message-threads-2026-08-12 beta header on every request of the thread, and if you stream, wait for message_stop before you continue from an id (it shows up earlier, in message_start, which is a trap).
Replay is the step that's tempting to leave for later. Don't. Threads are best-effort, and no response tells you whether the API actually stored that turn. You find out on the next continue. The 404 doesn't say why either. It could be expiry, a different API key or workspace, a reply written by a fallback model, a response that's still generating, or a tool call cut off by max_tokens that the API served but never stored. So your app stays the source of truth: keep your own copy of every message and build the replay path on day one. One more trap while we're here. A request for a model that doesn't exist also returns 404, so check error.details.error_code before you replay.
The field rules that will bite you
A thread fingerprints your request, and every field falls into one of three groups:
| Group | Fields | Rule |
|---|---|---|
| Create-only | system, tools |
Create request only. On a continue they're a 400, even as null |
| Fixed | model, tool_choice, thinking, output_config, context_management, mcp_servers (bar auth tokens), skills in container, top-level cache_control, the set of beta headers |
Same value on every request, or a 400 thread_fingerprint_mismatch |
| Free | max_tokens, stop_sequences, stream, metadata, service_tier |
Change them whenever you like |
So switching models mid-conversation, adding a tool or tweaking the system prompt all mean a new thread. Same if you change your structured output schema, since output_config is fixed (I wrote up structured outputs in the Claude API separately if you haven't used them yet).
Threads are also append-only. Each response id can be continued exactly once. You can't branch from an earlier turn or edit a message you already sent, and the API doesn't dedupe, so a message you resend by accident can show up twice.
Let the SDK do the boring part
Every official SDK has a thread helper: Python, TypeScript, Go, C#, Java, PHP and Ruby. I live in Go and TypeScript, but the docs lead with Python, so here's that version, lightly adapted:
client = anthropic.Anthropic()
# Creating the helper sends nothing yet
thread = client.beta.messages.thread(
model="claude-opus-5-5",
max_tokens=4096,
cache_control={"type": "ephemeral"},
system="You are a careful coding agent.",
)
# Turn 1 creates the thread and sends everything
thread.send({"role": "user", "content": "Find out why the auth tests fail."})
# Turn 2 continues it and sends only the new message
reply = thread.send({"role": "user", "content": "Now fix it."})The helper adds the beta header, creates on the first turn and continues after that. It replays once when a continue fails with thread_not_found, thread_already_continued or thread_continue_in_flight, and it starts a fresh thread after a refusal. It won't retry a dropped stream for you, and it keeps the conversation in memory only. If your agent has to survive a restart, you still need your own copy.
If you use the SDK's beta tool runner, you're already on threads. It uses one by default.
Message threads vs prompt caching
This is where most of the confusion lives. A thread does not cache your prompt. Prompt caching does that, and the two fix different problems.
| Message threads | Prompt caching | |
|---|---|---|
| What it cuts | Uploading, validating and tokenizing the history | Reprocessing a repeated prefix |
| Effect on your bill | None | Cache reads cost 10% of the base input price (5-minute writes cost 1.25x) |
| Lifetime | 5 minutes after the last request | 5 minutes by default, 1 hour at extra cost |
| How you turn it on | thread parameter plus a beta header |
cache_control |
Without caching, Claude still reads the whole conversation on every turn. Anthropic says as much in the announcement: requests get smaller, token usage stays the same. Threads stop you shipping the history over the wire and re-tokenizing it. Caching stops you paying full price to have it reprocessed. You want both.
To cache each turn on a thread, send a top-level cache_control on every request. It's a fixed field, so it has to be identical every time. The API applies it to the last cacheable block of each request, which means every turn reads the earlier conversation from cache and writes the new turn.
The breakpoint budget is where it gets fiddly. A threaded request can carry at most four cache_control breakpoints, and the top-level one counts. Breakpoints you put on system and tools in the create request count again on every continue. You also can't move or remove a breakpoint once you've sent it, because a continue can't resend old messages. If your caching strategy rolls breakpoints forward each turn, it won't survive the switch. The Hermes Agent maintainers flagged exactly that when they looked at adding support.
The 5-minute TTL and 4 other gotchas
Five minutes, then it's gone
A thread expires five minutes after its last request, and each stored response restarts the clock. That's fine for an agent firing tool calls back to back. It's bad news for anything with a human in the loop: approval steps, a tool that stalls for a few minutes, you wandering off to make a coffee. The thread dies and the next turn pays for a full upload anyway.
Someone measured it properly in Claude Code issue #102850. Threads were still alive after 3, 200 and 270 seconds idle, and gone at 360. A one-hour prompt cache didn't keep them alive. And because the client doesn't assume expiry, every turn after a long pause first burns a failed request, then replays. Dishant Sharma put it neatly: "It was built for tight machine loops, not for people thinking."
One number gets mixed up a lot. The API deletes thread data no more than an hour after last use. That's a retention ceiling, not a longer TTL. After five quiet minutes, you can't continue.
One thread, one key
A thread stays with the API key (or user, or service account) and the workspace that created it. On Claude Platform on AWS it's tied to the IAM principal. Rotate keys mid-session, or spread load across a credential pool, and you're starting new threads.
Some features just don't work
Message Batches, token counting, compaction on demand and most context editing are off the table (only clear_thinking_20251015 with keep: "all" gets through). Orgs with HIPAA readiness or inference hooks turned on can't use threads at all, and threads aren't ZDR eligible. If you compact long sessions, that's now your job: summarise in your own code and start a new thread from the summary.
Your proxy might be eating your context
This one's nasty. If you run Claude Code through a proxy or gateway that doesn't understand threads, it can forward a continue request as if it were the whole conversation. In raine/claude-code-proxy #148 that meant "No tool call found" 400s, around 820 of them in two days on one machine, plus context quietly going missing. The fix is for the proxy to reject the beta header with a capability_rejected marker. Claude Code then drops threading for that session and resends the full history. If you maintain a gateway, go and check yours.
It won't lower your bill
Pricing, rate limits and token usage don't change. Requests get smaller on the wire, not in tokens. If someone sells you threads as a cost optimisation, they read the launch tweet and skipped the docs.
How it compares to OpenAI Responses API and Gemini Interactions API
If you've used OpenAI's Responses API, this will feel familiar. OpenAI chains on previous_response_id, Gemini's Interactions API on previous_interaction_id, and Anthropic on previous_message_id. Same request shape, very different idea of what the server should keep.
| Claude message threads | OpenAI Responses API | Gemini Interactions API | |
|---|---|---|---|
| Chain with | previous_message_id |
previous_response_id |
previous_interaction_id |
| How long it's kept | 5 minutes after the last request, in memory | 30 days by default | 55 days on the paid tier, 1 day on free |
| Written to durable storage | No | Yes | Yes |
| Get or delete by ID | No API for it | Yes | Yes |
| Branch from an earlier turn | No | Yes | Yes |
Sources: OpenAI's conversation state guide and Google's Interactions API docs.
OpenAI and Google built conversation stores you can come back to days later. Anthropic built a latency trick that forgets everything on purpose and never touches disk. If you already keep your own transcript (and you should), the short retention costs you less than it sounds. Losing branching is the bigger trade.
When to use Claude message threads (and when to skip them)
Threads pay off when the context is big and the turns come fast. Long-running coding agents are the obvious fit. If you've built anything with the Claude Agent SDK, you know how quickly a coding loop piles up context. Computer-use agents are the other big one. The New Stack ran the numbers: by the 50th request, an agent would otherwise be uploading about 7.5 MB of accumulated screenshots, against roughly 0.15 MB with a thread.
I'd skip them when:
- your conversations stay small (under roughly 100k tokens, the upload probably isn't your bottleneck)
- a human approves steps, or tools regularly take minutes
- you rely on Batches, token counting or compaction on demand
- you need ZDR or HIPAA
If you do turn them on, a few habits help. Treat the thread as a cache you can lose at any moment, and keep your own transcript. Use one API key per thread. And don't wait for the 404: one agent project only continues if the last reply came less than 290 seconds ago and creates a fresh thread otherwise. Cheap, boring, and it dodges the wasted round trip.
Quick answers
Do message threads reduce cost?
No. Token usage, pricing and rate limits stay the same. For cost, use prompt caching, ideally alongside threads.
How long does a Claude message thread last?
Five minutes after its last request, and every stored response restarts the timer. The data is deleted no more than an hour after last use, but you can't continue an expired thread.
Can I use message threads with prompt caching?
Yes, and you should. Send the same top-level cache_control on every request and stay within four breakpoints.
Do I need to change anything in Claude Code?
No. Threads are on by default for most Claude Code users. If you route through a proxy, make sure it either handles continue requests or rejects the beta header properly.
Threads are a good, narrow fix: faster first tokens for agents that never stop talking, and nothing at all for your bill. If you're building agents and want someone to look at the loop before it hits 800 KB a request, that's the kind of work I do.
Related Articles
Claude API Memory Tool: Build Agents That Learn
Claude's Memory Tool lets your AI agents retain knowledge across sessions. Learn how to implement persistent memory without building your own solution.
7 min read
TutorialsHow to Use the Claude Code Frontend-Design Plugin to Stop Shipping AI Slop
The Claude Code frontend-design plugin fixes generic AI design. Here's how it works, how to prompt it, and how to apply it to WordPress and Shopify themes.
13 min read
TutorialsClaude API Structured Output: Complete Guide to Schema-Guaranteed Responses
Claude API structured output uses constrained decoding to guarantee JSON schema compliance. Learn implementation, best practices, and production gotchas.
6 min read