Follow Discord
Sweep 03 Nov 2026 · 22:27Z Build v2.1.318 536 read Stable v2.1.311 Latest v2.1.318 Next v2.1.318 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · api

message-threads changedbuild-with-claude/message-threads

Nearest release: v2.1.317, published 4 hours after this site recorded the change. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.

Recorded here
Lines+852added
Lines−0removed
From line — no hunk to open at
First seen 2 Nov 2026 this site's first read of the page
Recorded edits2to this page, all time

## How message threads work ## Use a thread with an SDK ## Send the `thread` field yourself ## What to send on each request ## Prompt caching on a thread ## Streaming ## Recover when a thread can’t be continued ### After a refusal ### Use fallbacks with a thread ## Compact a conversation ## How the SDK helper behaves ### Other helper methods ## Errors ## Features that threads don’t support ## Limitations ## Pricing and rate limits ## Data retention ## See also ## Availability ### API support #### [Messages API](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) ### Required beta headers ### Data handling

The whole hunk

852 lines, new page
/
lines

A whole new page. There's nothing to diff it against, so here is what it says.

---
title: Message threads
url: https://platform.claude.com/docs/en/build-with-claude/message-threads
description: Give long-running agents a faster time to first token, especially in long conversations, by sending only the new messages on each request.
featureMetadata:
  status: beta
  betaHeader: message-threads-2026-08-12
  zdr:
    eligibility: not-eligible
    note: A thread's contents (the conversation, system prompt, and tools) are held in memory, encrypted under a server-managed key, never written to durable storage, and deleted no more than 1 hour after the thread's last use. Using this feature does not affect your use of other ZDR eligible features.
  supportedModels:
    - claude-fable-5-1
    - claude-mythos-5-1
    - claude-fable-5
    - claude-mythos-5
    - claude-mythos-preview
    - claude-opus-5-5
    - claude-opus-5
    - claude-sonnet-5-5
    - claude-sonnet-5
    - claude-haiku-5-5
    - claude-haiku-4-5-20251001
    - claude-opus-4-8
    - claude-opus-4-7
    - claude-opus-4-6
    - claude-sonnet-4-6
    - claude-opus-4-5-20251101
    - claude-sonnet-4-5-20250929
  supportedPlatforms:
    Claude API: beta
    Claude Platform on AWS: beta
    Claude Models on Google Cloud (Anthropic managed): beta
    Microsoft Foundry: beta
---

With a message thread, requests are smaller, and time to first token stays lower and steadier as the conversation grows. Threads help most in long agent loops. 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.

A message thread keeps a conversation on the server for a short time. You send the full conversation once, with your system prompt and tools. On each later turn, you send only the new messages and the `id` of the previous response. The API rebuilds the conversation before it calls the model. You add one parameter, `thread`, to a [Messages API](https://platform.claude.com/docs/en/api/messages) request. The response is the usual message.

<Note>
  Message threads are different from [session threads](https://platform.claude.com/docs/en/managed-agents/session-threads) in Claude Managed Agents.
</Note>

## How message threads work

* **Create.** Send your first request as you do today, with the full conversation, `system`, and `tools`, and add `"thread": {"type": "create"}`. The API stores the conversation, including Claude’s reply, under the response `id`.
* **Continue.** Send only the messages since the previous response, and add `"thread": {"type": "continue", "previous_message_id": "<previous id>"}`. Leave `system` and `tools` out of the request. The thread keeps the values from the create request. Use each new response `id` for the next turn.
* **Replay.** A thread is best-effort. It expires 5 minutes after its last request, and the API deletes it no more than 1 hour after its last use. Each response that the API stores restarts the 5 minutes. If the API can no longer continue the thread, a continue request returns a 404 error. Send the full conversation again in a new create request, and continue from that response’s `id`. The SDK thread helper does this replay for you (see [How the SDK helper behaves](https://platform.claude.com/docs/en/build-with-claude/message-threads#sdk-helper-handles)). After a response from a fallback model, see [Use fallbacks with a thread](https://platform.claude.com/docs/en/build-with-claude/message-threads#fallbacks).

Your application stays the source of truth for the conversation. Keep your own copy of every message, and build the [replay path](https://platform.claude.com/docs/en/build-with-claude/message-threads#recover) from the start. No response, to a create or a continue request, says whether the API stored that turn. You find out on the next continue request. The SDK thread helper keeps this copy in memory while the helper object exists, and saves it nowhere else. If the conversation has to outlive your process, keep your own copy.

## Use a thread with an SDK

The SDK has a thread helper that builds the thread requests for you. Create it with the same fields as a Messages request, then send it one turn at a time. The helper keeps the full conversation in memory.

This example has two turns. The helper creates a thread on the first turn and continues it on the second, and it adds the beta header for you. The top-level `cache_control` is optional. It asks for prompt caching on each turn (see [Prompt caching on a thread](https://platform.claude.com/docs/en/build-with-claude/message-threads#prompt-caching)). Without it, each later turn still sends only the new messages, but nothing is cached unless you add cache breakpoints yourself.

<CodeGroup exclude="shell">
  ```python Python
  client = anthropic.Anthropic()

  # Create the helper. This call sends no request.
  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.",
  )

  # Turn 1: the helper creates a thread and sends the full conversation
  thread.send({"role": "user", "content": "Hi! I have a question about an order."})

  # Turn 2: the helper continues the thread and sends only the new message
  second = thread.send(
      {"role": "user", "content": "How long does standard shipping usually take?"}
  )

  for block in second.content:
      if block.type == "text":
          print(block.text)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  // Create the helper. This call sends no request.
  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."
  });

  // Turn 1: the helper creates a thread and sends the full conversation
  await thread.send({ role: "user", content: "Hi! I have a question about an order." });

  // Turn 2: the helper continues the thread and sends only the new message
  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);
    }
  }
  ```

  ```csharp C#
  using Anthropic.Helpers.Beta;

  AnthropicClient client = new();

  // Create the helper. This call sends no request.
  var thread = client.Beta.Messages.Thread(
      new ThreadCreateParams
      {
          Model = Messages::Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages = [],
          CacheControl = new(),
          System = "You are a support agent for Acme. Keep replies short.",
      }
  );

  // Turn 1: the helper creates a thread and sends the full conversation
  await thread.Send(
      new BetaUserMessageParam { Content = "Hi! I have a question about an order." }
  );

  // Turn 2: the helper continues the thread and sends only the new message
  var second = await thread.Send(
      new BetaUserMessageParam { Content = "How long does standard shipping usually take?" }
  );

  foreach (var block in second.Content)
  {
      if (block.TryPickText(out var textBlock))
      {
          Console.WriteLine(textBlock.Text);
      }
  }
  ```

  ```go Go
  client := anthropic.NewClient()
  ctx := context.Background()

  // Create the helper. This call sends no request.
  thread := client.Beta.Messages.NewThread(anthropic.BetaMessageThreadCreateParams{
  	Model:        anthropic.ModelClaudeOpus5_5,
  	MaxTokens:    1024,
  	CacheControl: anthropic.NewBetaCacheControlEphemeralParam(),
  	System: []anthropic.BetaTextBlockParam{
  		{Text: "You are a support agent for Acme. Keep replies short."},
  	},
  })

  // Turn 1: the helper creates a thread and sends the full conversation
  _, err := thread.Send(ctx, anthropic.BetaMessageThreadSendParams{
  	Messages: []anthropic.BetaMessageParam{
  		anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Hi! I have a question about an order.")),
  	},
  })
  if err != nil {
  	panic(err)
  }

  // Turn 2: the helper continues the thread and sends only the new message
  second, err := thread.Send(ctx, anthropic.BetaMessageThreadSendParams{
  	Messages: []anthropic.BetaMessageParam{
  		anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("How long does standard shipping usually take?")),
  	},
  })
  if err != nil {
  	panic(err)
  }

  for _, block := range second.Content {
  	if textBlock, ok := block.AsAny().(anthropic.BetaTextBlock); ok {
  		fmt.Println(textBlock.Text)
  	}
  }
  ```

  ```java Java
  var client = AnthropicOkHttpClient.fromEnv();

  // Create the helper. This call sends no request.
  var thread = client.beta().messages().thread(
      ThreadCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024)
          .cacheControl(BetaCacheControlEphemeral.builder().build())
          .system("You are a support agent for Acme. Keep replies short.")
          .build()
  );

  // Turn 1: the helper creates a thread and sends the full conversation
  thread.send("Hi! I have a question about an order.");

  // Turn 2: the helper continues the thread and sends only the new message
  var second = thread.send("How long does standard shipping usually take?");

  second.content().stream()
      .flatMap(block -> block.text().stream())
      .forEach(textBlock -> IO.println(textBlock.text()));
  ```

  ```php PHP
  $client = new Client();

  // Create the helper. This call sends no request.
  $thread = $client->beta->messages->thread(
      maxTokens: 1024,
      model: Model::CLAUDE_OPUS_5_5,
      cacheControl: new BetaCacheControlEphemeral,
      system: 'You are a support agent for Acme. Keep replies short.',
  );

  // Turn 1: the helper creates a thread and sends the full conversation
  $thread->send(['role' => 'user', 'content' => 'Hi! I have a question about an order.']);

  // Turn 2: the helper continues the thread and sends only the new message
  $second = $thread->send(['role' => 'user', 'content' => 'How long does standard shipping usually take?']);

  foreach ($second->content as $block) {
      if ($block instanceof BetaTextBlock) {
          echo $block->text, PHP_EOL;
      }
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  # Create the helper. This call sends no request.
  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."
  )

  # Turn 1: the helper creates a thread and sends the full conversation
  thread.send_message({role: "user", content: "Hi! I have a question about an order."})

  # Turn 2: the helper continues the thread and sends only the new message
  second = thread.send_message({role: "user", content: "How long does standard shipping usually take?"})

  second.content.each do |block|
    puts block.text if block.type == :text
  end
  ```
</CodeGroup>

## Send the `thread` field yourself

To build thread requests yourself, for example with cURL, the CLI, or an SDK’s usual `create` method, add the `thread` field to each request.

Send the `message-threads-2026-08-12` beta header on every request of a thread.

This example creates a thread on the first turn and continues it on the second. The top-level `cache_control` asks for prompt caching on each turn. It takes effect once the conversation is longer than the [minimum cacheable length](https://platform.claude.com/docs/en/build-with-claude/prompt-caching).

<CodeGroup>
  ```bash cURL
  # 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"'"}
    }'
  ```

  ```bash CLI
  # Turn 1: send the full conversation and create a thread

Cut at 300 lines. The page has the rest.

Feedback