How to Use Claude Message Threads: Messages API Threads Beta, Send Only New Messages, ~35% Faster First Token (Claude Code / API Tested)

On November 2, Anthropic put Message Threads into public beta on the Messages API. The first request sends the full conversation and creates a thread; after that each turn sends only the new messages and the id of the previous response. Long conversations get a faster first token, while token usage and billing stay the same. This guide explains the thread parameter, the beta header, the 5-minute expiry and the 404 replay from the official docs, gives curl, Python and TypeScript examples, and reports our test with an aicodewith key: through aicodewith the thread field is currently ignored and continue turns lose earlier context, Claude Code is not affected, plus the workaround and common errors.

11 分钟阅读
How to Use Claude Message Threads: Messages API Threads Beta, Send Only New Messages, ~35% Faster First Token (Claude Code / API Tested)

The short version: Message Threads let the Messages API keep a conversation on the server for a short time. After the first turn you send only the new messages and the id of the previous response, so requests get smaller and Claude starts answering sooner in long conversations. It saves latency, not tokens.

  • According to the Anthropic release notes, message threads are in beta on the Messages API from November 2. Requests need the beta header message-threads-2026-08-12 and a thread parameter in the body. A thread expires 5 minutes after its last request and is deleted no more than 1 hour after its last use.
  • According to Anthropic's announcement, message threads are already on by default for most Claude Code users, and requests with 500k to 1M tokens see median time to first token drop by about 35%. Claude still reads the full conversation on every turn, so token usage is unchanged, and so are billing and rate limits.
  • We tested with an aicodewith key on November 3: through aicodewith, the thread field is currently ignored. Every request returns 200, but the continue turn has no earlier context and the model cannot recall what was said before. So do not use threads through aicodewith: send the full history as usual, with prompt caching. Claude Code does not send thread requests to aicodewith and keeps working normally.

Create an API Key and connect Claude in Claude Code, Codex or Cursor →

What Message Threads are

The Messages API does not remember conversations. Without a thread, an agent re-sends every earlier message and tool result on every turn, and the API validates and tokenizes all of it again. Once a conversation reaches hundreds of thousands of tokens, that alone delays the first token.

According to the official docs, a message thread works like this: the first request is sent as usual, with the full conversation, system and tools, plus "thread": {"type": "create"}. The API stores the conversation, including Claude's reply, under the response id. On each later turn you send only the messages since the previous response, plus "thread": {"type": "continue", "previous_message_id": "..."}, and the API rebuilds the full conversation before calling the model. The response is the usual message.

Without a threadWith a thread
Request body per turnFull history + system + toolsFull on turn 1; then only new messages + previous response id
Time to first tokenGrows with the conversationLower and steadier, more so as the conversation grows
Token usageFull conversation read each turnThe same, the full conversation is still read each turn
Billing and rate limitsStandardUnchanged
Who keeps the conversationYour appThe server for 5 minutes; your app still keeps a full copy

Also, according to @dotey's summary, Anthropic's numbers show the median request size in long conversations falling from about 800 KB to 1.8 KB. The @ClaudeDevs announcement had about 209,000 views, 3,681 likes and 1,041 bookmarks by early November 3 (Beijing time) and made X trends.

How to use it: the thread parameter and the beta header

Every request of a thread needs anthropic-beta: message-threads-2026-08-12. These examples target the official Anthropic API (see the next sections for aicodewith).

curl

bash
# Turn 1: full conversation + system (+ tools), create a thread
curl -sS 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: only the new message + the id of the turn 1 response
# (no system / tools here: the thread keeps them)
curl -sS 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": "'"$MSG_ID"'"}
  }'

Save the id of the first response as MSG_ID and continue from it on turn 2. Always continue from the latest response id; each id can be continued only once.

Python

The official SDK has a thread helper, client.beta.messages.thread(...). It adds the beta header, leaves system and tools out of continue requests, and replays the full conversation once on a 404. You can also pass thread to create yourself:

python
import anthropic

client = anthropic.Anthropic()  # official API, reads ANTHROPIC_API_KEY

# Option A: the SDK thread helper (adds the beta header, replays on 404 for you)
thread = client.beta.messages.thread(
    model="claude-opus-5-5",
    max_tokens=1024,
    cache_control={"type": "ephemeral"},
    system="You are a support agent for Acme. Keep replies short.",
)
thread.send({"role": "user", "content": "Hi! I have a question about an order."})
second = thread.send({"role": "user", "content": "How long does standard shipping usually take?"})
print(second.content[-1].text)

# Option B: send the thread field yourself
first = client.beta.messages.create(
    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"},
    betas=["message-threads-2026-08-12"],
)
second = client.beta.messages.create(
    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": first.id},
    betas=["message-threads-2026-08-12"],
)

TypeScript

typescript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic(); // official API, reads ANTHROPIC_API_KEY

const thread = client.beta.messages.thread({
  model: "claude-opus-5-5",
  max_tokens: 1024,
  cache_control: { type: "ephemeral" },
  system: "You are a support agent for Acme. Keep replies short.",
});

await thread.send({ role: "user", content: "Hi! I have a question about an order." });
const second = await thread.send({
  role: "user",
  content: "How long does standard shipping usually take?",
});
for (const block of second.content) {
  if (block.type === "text") console.log(block.text);
}

What to send on each request

The official docs split the fields into three groups:

GroupFieldsRule
Create onlysystem, toolsSending either on a continue request, even as null, returns a 400
Fixedmodel, tool_choice, thinking, mcp_servers, context_management, output_config, top-level cache_control, the set of anthropic-beta values and moreSend on every request with the same value as on create, or get a 400 thread_fingerprint_mismatch
Can change per turnmax_tokens, stop_sequences, stream, metadata and othersChange them as you like

To change the system prompt, the tools or the model, send the full conversation in a new create request and start a new thread.

Expiry and replay

A thread is best-effort. It expires 5 minutes after its last request; after that a continue request returns a 404 with error.details.error_code set to thread_not_found. Send the full conversation from your own copy in a new create request, then continue from the new id. So always keep a full local copy of the conversation and build the replay path from the start. The official SDK helper replays once for you.

Does it save money? No, it saves latency

According to the official docs, threads do not change billing or rate limits. Requests get smaller, but Claude still reads the full conversation every turn, so input tokens are the same as before.

To avoid reprocessing the same tokens, you need prompt caching. A thread does not cache for you: the official examples send a top-level cache_control: {"type": "ephemeral"} on every request, so each turn reads the earlier conversation from the cache and writes the new turn. The two work together: threads cut the upload, caching cuts the repeated processing.

Good fits, according to Anthropic's announcement: long-running coding agents, computer use agents with a screenshot every turn, and document- and image-heavy conversations. For short chats the gain is small.

What changed in Claude Code

According to Anthropic's announcement, message threads are already on by default for most Claude Code users, which is where the 35% figure comes from. Users do not need to change any setting.

We logged, through a local proxy, the requests Claude Code 2.1.316 sends when it points at aicodewith with a custom ANTHROPIC_BASE_URL. A task that read two files made 3 requests, and none had a thread field or message-threads-2026-08-12 in the beta header. The message count grew 2, 4, 6, each request carried the full history at about 46 KB, and the task finished normally. In other words, with a third-party base URL Claude Code keeps working the old way and is not broken by threads.

Setup:

bash
export ANTHROPIC_BASE_URL="https://api.aicodewith.ai"
export ANTHROPIC_AUTH_TOKEN="YOUR_API_KEY"
claude --model claude-opus-5-5

For persistent settings and Windows, see How to connect Claude Code to a third-party API. For a bit more speed in long sessions you can lower effort, see How to choose Claude Opus 5.5 effort.

Message Threads is a Claude Messages API feature; Codex talks to OpenAI's API and is not involved. The Codex SDK also has "threads", but those resume a client-side session, which is a different thing from this server-side storage. See Getting started with the Codex SDK: TypeScript calls, streaming events and thread resume.

Does it work through aicodewith? Test results

Early on November 3, 2026 (Beijing time), we called aicodewith's Anthropic-compatible endpoint https://api.aicodewith.ai/v1/messages with an aicodewith key, the claude-opus-5-5 model and the message-threads-2026-08-12 header, following the official examples case by case:

TestExpected on the official APIActual through aicodewith
create: full conversation + thread: create200, thread created200, normal reply
continue: new message + previous id200, model sees earlier turns200, but input_tokens was 27 and the model saw no earlier turns
continue the same id a second time400 thread_already_continued200
previous_message_id that does not exist404 thread_not_found200
continue with system400200
Python SDK 2.4.0 thread helperTurn 2 remembers turn 1Turn 2 said it had no order number

The real reply on the continue turn (excerpt):

text
Turn 1 (create):  "Hi! I have a question about an order."
  -> 200, "Happy to help. What's your order number, and what's the question about it?"
Turn 2 (continue, previous_message_id = turn 1 id):
  "How long does standard shipping usually take? Also, what did I say my question was about?"
  -> 200, input_tokens: 27
  -> "...As for your question, you didn't say what it was about beyond asking about standard shipping..."

Conclusion: aicodewith does not support message threads yet and treats the thread field as if it were not there; each request is handled as a standalone request. The catch is that nothing fails: the SDK helper never gets a 404, never replays, and the conversation silently loses its context. So through aicodewith, do not add thread to requests and do not use client.beta.messages.thread(...).

Workaround: full history + prompt caching

Send the full history every turn as usual and add a top-level cache_control to turn on automatic caching. We ran three turns with the same aicodewith key (a system prompt of about 7,500 tokens):

TurnInput (uncached)Cache writeCache readTimeReply
1247,56804.0 sNormal
281,5416,1634.4 sNormal
381,5606,2403.5 s"Your order number is A-1042."

Turns 2 and 3 hit the cache, and turn 3 correctly recalled the order number from turn 1. In Python:

python
import anthropic

client = anthropic.Anthropic(
    base_url="https://api.aicodewith.ai",
    api_key="YOUR_API_KEY",
)

SYSTEM = "You are a support agent for Acme. Keep replies short.\n\n" + open("policy.md").read()
history = [{"role": "user", "content": "Hi! My order number is A-1042 and it hasn't shipped yet."}]

def turn(next_user_message=None):
    r = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=1024,
        cache_control={"type": "ephemeral"},  # automatic caching: moves forward each turn
        system=SYSTEM,
        messages=history,                       # full history, no thread field
    )
    history.append({"role": "assistant", "content": [b.model_dump(exclude_none=True) for b in r.content]})
    if next_user_message:
        history.append({"role": "user", "content": next_user_message})
    u = r.usage
    print(u.input_tokens, u.cache_creation_input_tokens, u.cache_read_input_tokens)
    return r

turn("When will it ship?")
turn("What was my order number again?")
turn()

TypeScript is the same idea, with the full history in messages:

typescript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  baseURL: "https://api.aicodewith.ai",
  apiKey: "YOUR_API_KEY",
});

const history: Anthropic.MessageParam[] = [
  { role: "user", content: "Hi! My order number is A-1042 and it hasn't shipped yet." },
];

const r = await client.messages.create({
  model: "claude-opus-5-5",
  max_tokens: 1024,
  cache_control: { type: "ephemeral" },
  system: "You are a support agent for Acme. Keep replies short.",
  messages: history, // full history every turn, no thread field
});
history.push({ role: "assistant", content: r.content });
console.log(r.usage);

How to check whether an endpoint really supports threads

Send a continue request with a previous_message_id that cannot exist. The official API returns 404 thread_not_found; a 200 with a normal reply means the endpoint ignores thread:

bash
# continue from an id that cannot exist
curl -sS https://api.aicodewith.ai/v1/messages \
  -H "x-api-key: YOUR_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":200,"cache_control":{"type":"ephemeral"},
       "messages":[{"role":"user","content":"Hi"}],
       "thread":{"type":"continue","previous_message_id":"msg_doesnotexist123"}}'
# official API: 404, error.details.error_code = "thread_not_found"
# a gateway that ignores thread: 200 and a normal reply

When aicodewith adds thread support, this command will show it without touching your application code.

Common errors

According to the official docs, these errors come back before the model runs. They use no tokens, and the thread and its expiry time do not change.

404 thread_not_found

The thread expired (no request for 5 minutes), the previous turn was not stored, a fallback model wrote the previous response, the previous response is still generating, or the request uses a different API key, organization or workspace. Fix: send the full conversation in a new create request. A request for a model that does not exist also returns 404, so check error.details.error_code before you replay:

json
{
  "type": "error",
  "error": {
    "type": "not_found_error",
    "message": "...",
    "details": { "error_code": "thread_not_found" }
  },
  "request_id": "req_..."
}

400 thread_fingerprint_mismatch

A fixed field differs from the create request, for example a different model or thinking, a changed top-level cache_control, or one beta header more or less. The error message names the fields. Start a new thread to change them.

400 thread_already_continued / thread_continue_in_flight

Each response id can be continued once, or an earlier continue request is still running. Always continue from the latest response id. With streaming, wait for the message_stop event before continuing; continuing earlier can return a 404.

400 thread_unsupported_request

The request uses something threads do not support: more than 4 cache_control breakpoints (the top-level one counts), on-demand compaction, most context editing, or an organization with HIPAA readiness or Inference hooks. Message Batches and token counting do not accept thread either. Remove the feature, or send the full conversation without thread.

A continue request includes system or tools

Returns a 400 without an error_code. Send only the new messages on continue; the thread keeps system and tools.

Through aicodewith the continue turn lost context

That is because aicodewith currently ignores thread, and no error is raised. Remove thread and go back to full history + cache_control, as shown above.

Data retention and limits

  • According to the official docs, a thread is held in memory, encrypted, never written to durable storage, and deleted no more than 1 hour after its last use. Threads are not eligible for zero data retention (ZDR) or HIPAA.
  • There is no API to list, read or delete a thread.
  • Available on the Claude API, Claude Platform on AWS, Claude Models on Google Cloud (Anthropic managed) and Claude in Microsoft Foundry, in beta on every model.
  • Separately, according to reports, Claude's text watermark covers more models from November 3. It is unrelated to threads and needs no request changes, see Claude watermark from November 3.

Summary

Message Threads fix the latency of re-sending the whole history on every turn: create a thread on the first turn, then send only new messages and the previous response id. The first token comes sooner, token usage and billing stay the same, and you still keep a full local copy so you can replay after the 5-minute expiry. Long-running agents on the official API should turn it on. Through aicodewith, thread is currently ignored, so send the full history with cache_control; Claude Code on aicodewith needs no changes.

Create an API Key and start in Claude Code, Codex or Cursor →

相关文章

How to Use DeepSeek Harness Creator Mode: Install, Connect a Third-Party API and Build Your First Plugin

How to Use DeepSeek Harness Creator Mode: Install, Connect a Third-Party API and Build Your First Plugin

On November 2 the official DeepSeek Harness account showed off Creator Mode, where you describe a tool in one sentence and the agent writes the plugin, installs it into Harness and checks that it loaded. This guide explains what Creator Mode is, how to start Harness with npx, how to connect an OpenAI-compatible third-party API (aicodewith base URL + key) to use Claude, GPT and DeepSeek models, and walks through building a todo_scan plugin with Creator Mode, with real outputs, common errors and the Claude Code / Codex equivalents.

Claude Code auto mode failing every turn behind a gateway? 2.1.317 fixes gateway 403/422 (upgrade + tests)

Claude Code auto mode failing every turn behind a gateway? 2.1.317 fixes gateway 403/422 (upgrade + tests)

Claude Code 2.1.317 (released November 3) fixes auto mode failing every turn when ANTHROPIC_BASE_URL points at an LLM gateway that answers 403 or 422 to the server-side safety check. This guide covers what changed, which third-party gateway and relay users are affected, how to upgrade and check the version, how to set the Base URL and key and turn on auto mode, plus side-by-side tests of 2.1.316 and 2.1.317, an error table and FAQ.

How to Build Karpathy's HackerTube with Claude Code or Codex: Mine Hacker News Comments for a Video Site Without an Algorithm (Tested)

How to Build Karpathy's HackerTube with Claude Code or Codex: Mine Hacker News Comments for a Video Site Without an Algorithm (Tested)

Karpathy built HackerTube in about two hours with Claude Code and Opus 5.5, picking 6,454 YouTube videos that Hacker News commenters kept recommending over 20 years. We had Claude Code and Codex each build a mini version with our API key: fetch two months of comments from the official HN Algolia API, extract YouTube links, count and dedupe them, and generate a static page. Includes the full prompt, run times, a side-by-side comparison and the steps between the mini version and HackerTube.