Give Claude Persistent Memory with REM Labs

Anthropic's Claude API is stateless by design -- every message request starts fresh. If you are building a Claude-powered assistant that needs to remember users across sessions, you need an external memory layer. This guide shows how to wire REM Labs into your Claude application so it remembers everything, permanently.

Why Claude Needs External Memory

Claude's Messages API processes each request independently. There is no built-in mechanism to persist context between API calls. When your application restarts, when a user returns the next day, or when you deploy a new version -- all prior context is gone unless you saved it somewhere.

You could store raw conversation logs in a database and dump them into the system prompt. But that breaks down quickly: context windows have limits, old messages dilute relevance, and keyword search misses semantic connections. REM Labs solves this with a purpose-built memory API that stores, indexes, and retrieves memories using vector embeddings, full-text search, and entity graphs -- all in one call.

Step 1: Get Your API Key

Sign up at remlabs.ai/console or run npx @remlabs/memory from your terminal. The free tier gives you 1,000 memory operations per month.

Step 2: Store Memories from Claude Conversations

import anthropic import requests CLAUDE_KEY = "sk-ant-..." REM_KEY = "sk-slop-..." REM_BASE = "https://api.api.remlabs.ai" client = anthropic.Anthropic(api_key=CLAUDE_KEY) user_msg = "I'm a Python developer at Datadog. I prefer functional programming patterns." resp = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, messages=[{"role": "user", "content": user_msg}] ) reply = resp.content[0].text # Store the interaction as a memory requests.post(f"{REM_BASE}/v1/memory-set", json={ "key": "claude-agent", "value": f"User: {user_msg}\nAssistant: {reply}", "namespace": "user-456", "tags": ["conversation", "preferences"] }, headers={"Authorization": f"Bearer {REM_KEY}"})

The POST /v1/memory-set endpoint stores the memory and automatically indexes it three ways: vector embedding for semantic search, full-text index for exact matches, and entity extraction for structured queries. The namespace isolates this user's memories from all others.

Step 3: Recall Context Before Each Claude Request

# New session -- user returns the next day user_msg = "What programming patterns do I prefer?" # Search for relevant memories search = requests.post(f"{REM_BASE}/v1/memory/search", json={ "query": user_msg, "namespace": "user-456", "limit": 5 }, headers={"Authorization": f"Bearer {REM_KEY}"}) memories = search.json().get("results", []) context = "\n".join([m["value"] for m in memories]) resp = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, system=f"Relevant context from previous conversations:\n{context}", messages=[{"role": "user", "content": user_msg}] ) print(resp.content[0].text) # "You mentioned you prefer functional programming patterns. You're a Python developer at Datadog."

The search endpoint returns memories ranked by relevance using reciprocal rank fusion across all three retrieval paths. Claude receives only the most relevant context -- not your entire conversation history.

Step 4: Node.js with the Anthropic SDK

import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ apiKey: "sk-ant-..." }); const REM_BASE = "https://api.api.remlabs.ai"; const REM_KEY = "sk-slop-..."; // Store a memory await fetch(`${REM_BASE}/v1/memory-set`, { method: "POST", headers: { "Authorization": `Bearer ${REM_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ key: "claude-agent", value: "User is a Python developer at Datadog. Prefers functional patterns.", namespace: "user-456" }) }); // Recall and use with Claude const search = await fetch(`${REM_BASE}/v1/memory/search`, { method: "POST", headers: { "Authorization": `Bearer ${REM_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ query: "user background", namespace: "user-456", limit: 5 }) }); const { results } = await search.json(); const context = results.map(r => r.value).join("\n"); const msg = await client.messages.create({ model: "claude-sonnet-4-20250514", max_tokens: 1024, system: `Relevant memories:\n${context}`, messages: [{ role: "user", content: "Recommend a project for me." }] });

Memory Across Claude and Other Models

Because REM Labs is model-agnostic, the same memory store works across providers. A user could interact with Claude in your web app and GPT-4 in your mobile app -- and both agents would share the same persistent memory through the same namespace. No vendor lock-in.

Also works with MCP: If you use Claude Code or Claude Desktop, REM Labs provides an MCP server for direct memory access. See the MCP integration guide.

Give your Claude app a memory

Free tier. No credit card. Works with Claude Sonnet, Opus, and Haiku.

Get started free →