Claude API Chatbot Memory Persistence: How It Works
Why Claude "forgets" between requests
If you've built a chatbot on the Claude API and noticed it has no idea what the user said five minutes ago — or even in the previous message — that's expected behavior, not a bug. The Claude API is stateless. Every call to the Messages endpoint is independent; Anthropic's servers don't store your conversation history on your behalf between requests.
To get persistent memory, you have to send the relevant conversation history back to the model on every call, and you have to store that history somewhere on your own infrastructure (a database, a cache, a session store). This article covers the practical patterns for doing that: full-history replay, sliding windows, summarization, and external memory stores, plus where each one breaks down.
The baseline: replay the message array
The simplest form of memory is just resending the full list of prior turns in the messages array on every request. Claude's chat format is explicitly built for this — each call takes an ordered array of user and assistant turns, and the model treats that array as the entire context of the conversation.
const history = [
{ role: "user", content: "My deployment target is a Node 20 Lambda." },
{ role: "assistant", content: "Got it, I'll account for that in suggestions." },
{ role: "user", content: "Now help me write a handler for S3 events." }
];
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-opus-4-20250514",
max_tokens: 1024,
messages: history
})
});
This works well for short-lived conversations, but it has an obvious limit: token usage grows linearly with conversation length, and Claude's context window, while large, is still finite. For a support bot that runs for 40 turns, replaying the whole history on every call gets expensive and eventually hits the ceiling. See /docs/messages for the exact request shape and limits.
Persisting history across sessions
Replaying history solves memory within a single API call sequence, but real chatbots need memory across sessions — a user closes the tab and comes back tomorrow expecting the bot to remember context. That requires storing the conversation somewhere durable, keyed by a session or user ID:
- Relational DB (Postgres/MySQL): a
messagestable withconversation_id,role,content,created_at. Simple, queryable, easy to paginate. - Redis or similar: fast for active sessions, with a TTL for ephemeral conversations that don't need long-term storage.
- Document store: convenient if you're already storing each conversation as a JSON blob.
On each new user message, you load the stored history for that conversation ID, append the new turn, send it to Claude, then persist both the user turn and the assistant's reply back to storage. The API call itself stays stateless — your storage layer is what makes the product feel stateful.
async function sendMessage(conversationId, userText) {
const history = await db.getMessages(conversationId); // load prior turns
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-opus-4-20250514",
max_tokens: 1024,
messages: history
})
});
const data = await res.json();
const reply = data.content[0].text;
await db.saveMessages(conversationId, [
{ role: "user", content: userText },
{ role: "assistant", content: reply }
]);
return reply;
}
Managing growth: windows, summaries, and retrieval
Once conversations run long, you need a strategy to keep the context sent to Claude bounded while preserving the useful parts of memory.
Sliding window: only send the last N turns. Cheap and simple, but the bot loses anything outside the window — fine for transactional bots, bad for anything that needs long-term recall (a user's preferences, past orders, prior decisions).
Summarization: periodically ask Claude to compress older turns into a short summary, then replace those turns with the summary in future requests. This keeps token usage flat while retaining the gist of earlier context. A common pattern is to summarize every 10–15 turns and keep the last few turns verbatim alongside the running summary.
Retrieval-augmented memory: store facts or past turns in a vector database, embed the current user message, and retrieve the most relevant prior context to inject into the prompt. This is the right approach for bots that need to recall specific facts from long histories (a support bot remembering a customer's account details from three weeks ago) without resending everything.
Hybrid approach (most production bots use this): keep a running summary of the full conversation, the last few turns verbatim, and retrieve relevant facts from a vector store when needed. None of this logic lives in the model API itself — it's entirely in your application layer.
Where an API layer helps
None of the memory logic above is something the raw Claude API provides, and it shouldn't try to — memory strategy is product-specific. What does help is having clean visibility into what each conversation is costing you as history grows, since token usage scales with context length.
SubToAPI sits in front of your Claude access and gives every application its own sub_live_... key, with usage metadata per key so you can see exactly how much a given chatbot's growing context is costing in tokens. If you're running a summarization job to compress history, that's a second model call worth tracking separately from the main chat traffic — usage breakdowns make that visible instead of guesswork. Streaming responses for a chat UI works the same way through /docs/streaming, and tool calls (if your bot looks things up instead of just recalling) are documented at /docs/tools.
Start with /docs/quickstart if you're setting up the API layer for the first time, and check /pricing if you're deciding between Solo, Team, or Scale seats for a bot with multiple contributors.
questions
Does the Claude API have a built-in conversation memory feature? No. Every request is stateless — you must send the relevant message history yourself on each call and store it in your own database or cache between sessions.
What's the best way to handle long chatbot conversations without hitting context limits? Use a sliding window for recent turns combined with periodic summarization of older turns, or retrieve only relevant facts from a vector store instead of resending the full history.
Can I use SubToAPI to manage chatbot memory? SubToAPI gives you API access, keys, and usage visibility for your Claude calls, but the memory logic — storage, summarization, retrieval — lives in your application, not in the API layer itself.