Claude API Multi-Turn Conversation Memory Explained
Does the Claude API remember previous messages?
No. The Anthropic Claude API is stateless — it has no built-in memory of past requests. Every call to the Messages endpoint is independent. If you want Claude to "remember" what was said earlier in a conversation, you have to send the entire relevant message history back to the API on every single request, inside the messages array.
This surprises a lot of developers coming from chat UIs like claude.ai, where memory feels automatic. Under the hood, even that product is just resending conversation history with each turn. Understanding this is the key to building reliable multi-turn conversations: you own the memory, not the model.
How multi-turn conversations actually work
Each request to the Messages API takes a messages array of alternating user and assistant turns. 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-sonnet-4-5",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "My name is Alex and I run a bakery."},
{"role": "assistant", "content": "Nice to meet you, Alex! How can I help with your bakery?"},
{"role": "user", "content": "What did I say my name was?"}
]
}'
Claude correctly answers "Alex" here — not because it remembers the first request, but because the entire history was included in this one. Drop the first two turns and Claude has no idea who Alex is.
Patterns for managing conversation state
1. Store the full history server-side
The simplest and most common pattern: persist the message array (in a database, Redis, or even a session object) keyed by conversation ID. On each new user message, fetch the stored history, append the new message, send it to the API, then append the assistant's reply and save again.
async function sendTurn(conversationId, userText) {
const history = await db.getMessages(conversationId); // array of {role, content}
history.push({ role: "user", content: userText });
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 1024,
messages: history,
}),
});
const data = await response.json();
const assistantText = data.content[0].text;
history.push({ role: "assistant", content: assistantText });
await db.saveMessages(conversationId, history);
return assistantText;
}
This is the standard approach for chatbots, support agents, and multi-turn tools. It's conceptually simple but has one big consequence: token usage.
2. Watch your context window and token costs
Every turn you add makes every subsequent request more expensive and slower, because the full history is re-sent and re-processed each time. For long-running conversations you need a strategy to keep this under control:
- Truncate old turns once you exceed a message or token threshold, keeping only the most recent N turns.
- Summarize older turns into a compact system-level recap and drop the raw messages.
- Cap total context at a sensible limit for your use case (customer support threads rarely need more than the last 15–20 turns).
- Use the
systemparameter for persistent instructions (tone, role, constraints) instead of repeating them in every user turn — this keeps the message array focused on actual conversation content.
A simple truncation helper:
function trimHistory(messages, maxTurns = 20) {
if (messages.length <= maxTurns) return messages;
return messages.slice(-maxTurns);
}
Be careful with truncation at the message-role boundary — Claude's Messages API expects alternating user/assistant roles, so always trim in pairs to avoid starting the array on an assistant turn.
3. Separate "memory" from "conversation"
For applications that need facts to persist across sessions — not just within one conversation — don't rely on message history alone. Extract durable facts (user preferences, account details, past decisions) into a separate store, and inject them into the system prompt or as a preamble user message at the start of each new session. This keeps your token usage from growing unbounded while still giving Claude the context it needs.
Where SubToAPI fits in
If you're building multi-turn features on top of Claude, you still need to solve the stateless-memory problem yourself — that doesn't change regardless of which gateway you use. What SubToAPI adds on top is operational: it turns your existing Claude access into a standard HTTPS API with application API keys (sub_live_...), streaming, tool use, and usage metadata per key, so you can track exactly how much context (and cost) each conversation thread is consuming across your team.
A basic conversation turn through SubToAPI looks the same as the pattern above, just pointed at a different endpoint:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4-5",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "My name is Alex and I run a bakery."},
{"role": "assistant", "content": "Nice to meet you, Alex!"},
{"role": "user", "content": "Remind me what I told you about my business."}
]
}'
You still manage the message array yourself — SubToAPI doesn't add hidden memory either — but you get per-key usage visibility, which is useful for debugging runaway context growth in long conversations. See /docs/messages for the full request shape and /docs/streaming if you want tokens streamed back turn by turn. Plans start with a free trial at /signup, and pricing is at /pricing.
questions
Does Claude have a memory feature like ChatGPT's "memory"? Not through the raw API. Some consumer chat apps built on Claude implement memory as a product feature, but the underlying API call is still stateless — the app is just managing history and injected facts for you.
How many previous turns should I send back to Claude? There's no fixed number — it depends on your model's context window and your token budget. A common practical range is the last 10–20 turns, with older context summarized rather than sent verbatim.
What happens if I don't include the conversation history? Claude treats each request as a fresh, isolated conversation with no knowledge of anything said before. Omitting history is the most common cause of Claude "forgetting" context between turns.