← Blog

Claude API Multi-Turn Conversation Memory Explained

2026-09-26 · 5 min read · SubToAPI Team

Claude's API has no built-in memory. Every request is stateless — there is no session ID, no server-side conversation store, and no "remember this user" flag. If you want multi-turn conversation memory, you build it yourself by sending the full message history back with every request. This is the single most common point of confusion for developers moving from a chat UI (which feels like it remembers you) to the raw API (which does not).

The good news is that implementing multi-turn memory is straightforward once you understand the mental model: you are the memory. Claude only knows what's in the messages array you send on each call. This article covers how that works, how to manage growing context, and the tradeoffs between naive history replay and smarter summarization.

How Conversation State Actually Works

Every call to the Messages endpoint takes an array of messages, each with a role (user or assistant) and content. To continue a conversation, you append the new user message to the array that already contains prior turns, and send the whole thing again:

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-3-5-sonnet-latest",
    "max_tokens": 512,
    "messages": [
      {"role": "user", "content": "My name is Alex and I work at a logistics startup."},
      {"role": "assistant", "content": "Nice to meet you, Alex! What can I help you with at your logistics startup?"},
      {"role": "user", "content": "What was my name again?"}
    ]
  }'

Claude answers "Alex" not because it remembers the earlier request, but because that turn is still physically present in this request's messages array. Drop the first two messages and Claude has no idea who Alex is. This is why "memory" in the Claude API is really context management — you decide what gets kept, trimmed, or summarized before each call.

Building a Simple Conversation Store

For most apps, the pattern is:

  1. Store each conversation's messages in a database, keyed by a conversation ID or session ID.
  2. On each new user turn, load the stored history, append the new message.
  3. Send the full array to the API.
  4. Append Claude's response to the stored history and save it back.
async function sendTurn(conversationId, userText) {
  const history = await db.getMessages(conversationId); // [{role, content}, ...]
  history.push({ role: "user", content: userText });

  const res = await fetch("https://api.subtoapi.app/v1/messages", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
      "content-type": "application/json"
    },
    body: JSON.stringify({
      model: "claude-3-5-sonnet-latest",
      max_tokens: 1024,
      messages: history
    })
  });

  const data = await res.json();
  const reply = data.content[0].text;
  history.push({ role: "assistant", content: reply });
  await db.saveMessages(conversationId, history);
  return reply;
}

If you're routing requests through SubToAPI, this pattern doesn't change — you still own the history array, but you get a stable HTTPS endpoint, streaming, and per-key usage metadata on top of it, which is useful when several team members or app instances are generating conversations against the same underlying Claude access. See the quickstart and messages docs for the exact request shape.

Managing Context Growth

The obvious problem: conversations grow, and every turn resends the entire history, which costs tokens and eventually hits the model's context window limit. A few practical strategies:

const summary = await summarizeOldTurns(history.slice(0, -10));
const trimmedHistory = [
  { role: "user", content: `Conversation summary so far: ${summary}` },
  ...history.slice(-10)
];

This keeps token usage predictable and avoids silent truncation errors when a conversation grows past the model's limit.

Streaming and Tool Use in Multi-Turn Contexts

Multi-turn memory interacts with two other features you'll likely need:

Practical Checklist

questions

Does Claude remember previous conversations across sessions automatically? No. Claude has no server-side memory. Anything from a previous session must be re-sent as part of the messages array or stored separately in your own database and reinjected.

How many turns can I include before hitting context limits? It depends on the model's context window and the length of each message. Track cumulative token usage per conversation and start summarizing or trimming well before you approach the model's stated limit.

Should I store conversation history as plain text or structured JSON? Store it as structured JSON matching the API's message format (role + content, including any tool_use/tool_result blocks). This lets you resend it directly without reformatting and avoids losing tool-call context.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →