← Blog

Claude API Memory Persistence for Chatbots

2026-11-06 · 5 min read · SubToAPI Team

Why Claude API chatbots "forget" by default

The Claude API is stateless. Every call to the messages endpoint is independent — there is no session, no server-side conversation object, and no built-in memory between requests. If you send a single user message with no prior context, Claude has no idea what was said five minutes ago, five messages ago, or in a conversation that happened yesterday. This surprises a lot of developers building their first chatbot: they assume the model "remembers" the user because ChatGPT-style products feel that way, but that continuity is built entirely in the application layer, not the model.

To get persistent memory, you need to store conversation state yourself and pass the relevant parts back to Claude on every request. This article covers the practical patterns for doing that: full history replay, rolling summarization, long-term memory with retrieval, and how to manage token budgets as conversations grow. None of this requires a special "memory API" — it's a design pattern you implement around whatever endpoint you're calling, whether that's the Anthropic API directly or a wrapper like SubToAPI.

Pattern 1: Full history replay

The simplest approach is to store every message in a database and resend the entire thread with each new request.

{
  "model": "claude-sonnet-4-5",
  "max_tokens": 1024,
  "messages": [
    { "role": "user", "content": "My name is Priya and I prefer concise answers." },
    { "role": "assistant", "content": "Got it, Priya. I'll keep responses short." },
    { "role": "user", "content": "What's the capital of Portugal?" }
  ]
}

This works well for short-lived sessions — support widgets, onboarding flows, single-session assistants. It's easy to implement and debug: your database table is just (conversation_id, role, content, created_at), and you reconstruct the messages array by querying that table in order.

The problem is scale. Every message you add increases the token count of every subsequent request. A 50-turn conversation can easily push 10,000+ tokens of history into each call, which costs money and eventually hits context limits.

Pattern 2: Rolling summarization

Once a conversation gets long, replace older turns with a compressed summary instead of dropping them or paying full price to resend them.

A common structure:

[
  { "role": "user", "content": "[Conversation summary: User is Priya, a backend engineer debugging a Postgres connection pool issue. She prefers short answers. Already tried increasing max_connections without success.]" },
  { "role": "user", "content": "Could the issue be with pgbouncer instead?" }
]

To generate the summary, periodically make a separate Claude call asking it to compress the last N turns into a few sentences, then store that summary alongside (or instead of) the raw messages. A practical trigger: summarize whenever the stored history exceeds a token threshold, say 3,000 tokens, and keep the last 2–3 raw turns intact for immediate context plus the summary for everything older.

This keeps per-request token usage roughly flat regardless of how long the conversation has run, at the cost of some fidelity — fine detail from early turns gets lost in compression.

Pattern 3: Long-term memory with retrieval

Summarization handles within-session memory well, but it doesn't help when a user returns days later and expects the bot to remember preferences, past issues, or facts from previous sessions. For that, you need persistent, searchable memory independent of any single conversation.

The typical setup:

  1. After each session (or after key moments), extract durable facts ("user's timezone is CET," "user is on the Pro plan," "user reported a billing bug on March 3") and store them with embeddings in a vector store or even a simple keyed table if the facts are structured.
  2. On a new session, retrieve the facts relevant to the current query (via embedding similarity or straightforward lookup by user ID) and inject them into the system prompt or an early user turn.
  3. Keep this injected context small and relevant — dumping a user's entire history into every prompt defeats the purpose and wastes tokens.
const memories = await fetchRelevantMemories(userId, currentMessage);

const systemPrompt = `You are a support assistant. Known facts about this user:
${memories.map(m => `- ${m.text}`).join("\n")}`;

This is the same retrieval pattern used in RAG pipelines, just applied to user-specific memory instead of document corpora.

Choosing a storage layer

For most chatbots, memory persistence doesn't need anything exotic:

Don't over-engineer this early. A single Postgres table with conversation_id, role, content, and token_count columns covers the first 90% of chatbot use cases. Add summarization when context windows start costing real money, and add vector retrieval only when users genuinely expect cross-session memory.

Managing cost and token budgets

Memory persistence directly affects your API bill, since every stored message you replay gets billed as input tokens on every subsequent call. A few practical habits:

If your chatbot also needs to call external functions mid-conversation — looking up an order, checking a calendar — make sure your memory layer stores tool call results too, not just text turns, so follow-up questions about "that order I asked about" still resolve correctly. The tool use docs cover how tool calls and results fit into the message format.

questions

Does the Claude API have a built-in memory or session feature? No. Every request is stateless. You must store conversation history yourself and include the relevant messages in each new API call.

What's the best way to handle very long conversations without hitting context limits? Use rolling summarization: periodically compress older turns into a short summary and keep only the most recent raw turns plus that summary in each request.

How do I give a chatbot memory across multiple sessions, not just within one conversation? Store durable facts about the user separately (a vector store or simple key-value table) and inject only the relevant ones into the prompt when a new session starts.

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 →