GPT-6 Sol & Claude Opus 5.5 are liveGPT-6 Sol at half the price of 5.6 Sol

Claude Message Threads API guide

Send the conversation once, then continue with only the new messages.

Kiki, OmniaKey COO9 min read
On this page

The Claude message threads API lets you send a long conversation once, then send only the new messages on every later turn. Add "thread": {"type": "create"} to the first Messages request plus the message-threads-2026-08-12 beta header, and the API keeps the conversation in memory so the next request uploads less (Anthropic's Message threads page, checked November 4, 2026).

It is a beta on every model; 17 model IDs can use it, including Claude Opus 5.5, which the code below calls. The beta header goes on every request of a thread.

What a thread saves

Without a thread, every turn carries the whole transcript: the system prompt, the tool definitions, every earlier message and every tool result. A thread stores that conversation, including Claude's reply, under the response id, and a continue request sends only what happened since (Message threads page, checked November 4, 2026). Less to upload on every turn.

Prompt caching helps the same conversation in another way. A cache hit makes repeated input cheaper — on Opus 5.5, $0.20 per million tokens against $4 for fresh input (Anthropic's pricing page, checked November 4, 2026) — but the request still carries that input. A thread cuts what you send; caching cuts the price of what repeats. Long sessions want both, and reducing Claude Code token usage covers the context side.

Threads change neither pricing nor rate limits (Message threads page, checked November 4, 2026). Current rates are on pricing.

Threads leave caching to you. Send a top-level cache_control on every request and the API applies it to the last cacheable block of that request's messages. The four-breakpoint limit is a platform-wide prompt-caching rule, and automatic caching uses one of the four slots (Anthropic's prompt caching page, checked November 4, 2026). Spend them like this:

  1. the top-level cache_control, one slot on every request of the thread;
  2. one on system, sent with the create request;
  3. one on tools, also on create;
  4. one on the newest message.

Breakpoints on earlier stored messages sit outside that budget, since a continue request sends only new messages. Once sent, a breakpoint stays where it is. When you replay a conversation, strip the breakpoints from the earlier messages and keep one on the last block (Message threads page, checked November 4, 2026).

Create the first turn, then continue

Three steps:

  1. Put your Anthropic key in ANTHROPIC_API_KEY.
  2. Send the full conversation, with system and tools if you use them, plus "thread": {"type": "create"}.
  3. Send only the messages since the last response, plus "thread": {"type": "continue", "previous_message_id": "<previous id>"}, and leave system and tools out.

Every request carries the beta header and anthropic-version: 2026-10-21, the current version (Anthropic's versioning page, checked November 4, 2026).

bash
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2026-10-21" \
  -H "anthropic-beta: message-threads-2026-08-12" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 1024,
    "system": "You are a careful backend reviewer.",
    "messages": [
      {"role": "user", "content": "Read this design and list the risks."}
    ],
    "thread": {"type": "create"}
  }'

Save the id from that response. The next request sends only the new message:

bash
PREVIOUS_ID="<id from the previous response>"

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2026-10-21" \
  -H "anthropic-beta: message-threads-2026-08-12" \
  -H "content-type: application/json" \
  -d @- <<JSON
{
  "model": "claude-opus-5-5",
  "max_tokens": 1024,
  "messages": [
    {"role": "user", "content": "Turn those risks into a checklist."}
  ],
  "thread": {"type": "continue", "previous_message_id": "$PREVIOUS_ID"}
}
JSON

Keep the newest id from each reply: a thread is append-only, each response id can be continued once, and the next request must use the latest one (Message threads page, checked November 4, 2026).

If you need another way to get an API key, OmniaKey sells Claude API access with the same model IDs, so the request body stays the same. Threads are a beta on Anthropic's side, so before you depend on them in production, check that whatever proxy you use passes the thread field through unchanged.

The Python SDK keeps the ids for you. Build a helper with client.beta.messages.thread(...), which only prepares the call, then send each turn with thread.send(). The helper adds the beta header, creates on the first turn, continues with only the new messages, and replays the full conversation once in a new create request when a continue returns thread_not_found, thread_already_continued or thread_continue_in_flight (Message threads page, checked November 4, 2026).

python
import anthropic

client = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY

# The helper sends nothing yet; it holds the fields every turn reuses.
thread = client.beta.messages.thread(
    model="claude-opus-5-5",
    max_tokens=1024,
    system="You are a careful backend reviewer.",
)

first = thread.send({"role": "user", "content": "Read this design and list the risks."})
print(first.id, first.stop_reason)

second = thread.send({"role": "user", "content": "Turn those risks into a checklist."})
print(second.id)  # continue from this id next time

We did not run these requests against Anthropic's API; the request shapes come from Anthropic's Message threads page. For streams, thread.stream(...) plus stream.get_final_message() gives you the same flow, and thread.previous_message_id returns the id when you would rather track it yourself.

What to resend and what never to resend

Send the fixed fields on every request with the same values, send system and tools only on the create request, and let the free fields change from turn to turn.

GroupFieldsRule
Create-onlysystem, toolsCreate request only; sending either one on a continue request, even as null, is a 400.
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 valuesSame value every time. A different value returns a 400 that names the field that differs.
Freemax_tokens, stop_sequences, stream, metadata, service_tier, and unnamed fieldsChange them whenever you like.

The 400 for a changed fixed field carries thread_fingerprint_mismatch, and the message lists the fields that differ. To change one of those values, start a new thread with the full conversation and the new value.

Omit previous_message_id and the continue returns a 400 whose body carries a message but no error_code; including system or tools does the same. A continue should carry exactly the messages since the last response.

mcp_servers is fixed, yet the server URLs and authorization tokens live only in your request, so send the list on every request that uses MCP; a token may change between requests. The container id lives only in your request too, so send container each time (Message threads page, checked November 4, 2026).

Server-side fallback is settled at create time: send fallbacks on every request of the thread or on none. The value may change between turns, but adding or dropping the field returns thread_fingerprint_mismatch.

Expiry, replay and the 404 path

A thread expires 5 minutes after its last request, and the API deletes it no more than 1 hour after its last use. Each stored response restarts the 5 minutes. Threads are not eligible for zero data retention: the conversation, system prompt and tools sit in memory, encrypted, and are never written to durable storage (Message threads page, checked November 4, 2026).

Nothing in the response says whether the API stored a turn, on a create or a continue. You find out on the next continue request, so your application stays the source of truth and keeps its own copy of every message (Message threads page, checked November 4, 2026).

When a continue returns 404 thread_not_found, send the full conversation in a new create request and continue from that response's id. The API gives the same 404 for all of these:

  • the thread expired;
  • the previous turn was served and then dropped, for example a tool call cut off by max_tokens;
  • a fallback model wrote the previous response;
  • the previous response is still generating;
  • the request came from another organization, workspace, user or workspace key;
  • the new messages add content the stored conversation lacks, such as the first image in a conversation that had none.

Replaying the full conversation is always safe and repairs every one of those cases.

Streaming adds a rule: continue from the response id only after message_stop, because a continue sent earlier can return 404. If the stream of a continue request drops before message_stop, send the same continue request again, and replay if that returns thread_already_continued or thread_continue_in_flight. The SDK helper leaves a dropped stream for you to retry.

Three other cases have their own path:

  • pause_turn: continue from that response's id with "messages": []. If that 404s, replay with the paused response included.
  • stop_reason: "refusal": start a new thread without the turn that caused it; the API may already hold the refused turn.
  • A fallback_message entry in usage.iterations, or in the final message_delta on a stream, marks a fallback answer: replay.

Errors

All five thread errors come back before the model runs: the request uses no tokens and the thread keeps its expiry time. error.type is invalid_request_error for the 400s and not_found_error for the 404 (Message threads page, checked November 4, 2026).

  • 404, thread_not_found: the API cannot continue this thread. Send the full conversation in a new create request.
  • 400, thread_fingerprint_mismatch: a fixed field changed from the value stored with the thread. Keep it identical, or start a new thread.
  • 400, thread_already_continued: that response id was already used once. Continue from the latest id, and replay if you never received it.
  • 400, thread_continue_in_flight: another continue from the same id is still running, usually your own earlier attempt. Let it finish or replay.
  • 400, thread_unsupported_request: the request uses a feature or an organization setting that threads do not support. error.details.unsupported names one of context_management, cache_control, fallbacks, hipaa or inference_hooks; that list can grow, so if a value looks unfamiliar, send the full conversation without thread.

Message Batches rejects the thread field (a batch item with it fails with invalid_request_error), and token counting and compaction return 400; so does context editing other than clear_thinking_20251015 with keep: "all". HIPAA-ready organizations and organizations with Inference hooks on are excluded too. Listing, reading and deleting a thread are all unavailable through the API.

Frequently asked questions

What is the Claude message threads API?

It is a beta Messages API feature that stores a conversation on Anthropic's side so later turns send only new messages. The beta header is message-threads-2026-08-12, and each request adds a thread object with type set to create or continue.

Do message threads work with prompt caching?

Yes. A thread does not cache your prompt for you: send a top-level cache_control on every request and keep to four breakpoints. Breakpoints on stored messages sit outside that count; the top-level one and any on system or tools do count.

Can I change the model in the middle of a thread?

Model changes need a new thread. model is a fixed field, so a different value returns 400 thread_fingerprint_mismatch; start a new thread with the full conversation and the new model.

Why does a continue request return 400 with no error_code?

The continue is missing previous_message_id, or it includes system or tools — even as null. Those 400s carry a message and skip error_code, so read the text.

How long does a thread last, and what happens when it expires?

A thread expires 5 minutes after its last request and is deleted no more than 1 hour after its last use. After that a continue returns 404 thread_not_found; send the full conversation in a new create request and continue from its id.

Sources checked

Models in this article

Claude APIClaude Opus 5.5message threadsbetaprompt cachinglong context

Related articles

Recent posts

Popular guides