--- title: "User Profiles" sidebarTitle: "User Profiles" description: "What a profile is, how supermemory derives it from a container's memories, and how to inject it into a prompt without wrecking your cache" icon: "circle-user" --- Every container tag has a profile: supermemory's current understanding of the entity that container is about, derived from everything you've ingested there. You don't write it, update it, or manage it. You fetch it: ```typescript TypeScript import Supermemory from "supermemory"; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY }); const { profile } = await client.profile({ containerTag: "user_4f8a", }); ``` ```python Python from supermemory import Supermemory client = Supermemory() result = client.profile(container_tag="user_4f8a") ``` ```bash cURL curl -X POST "https://api.supermemory.ai/v4/profile" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTag": "user_4f8a"}' ``` And you get back two arrays of plain sentences: ```json { "profile": { "static": [ "Sarah is a senior engineer at Meridian, working on the payments platform", "Sarah prefers short, technical answers with code over prose", "Sarah works from Lisbon, usually async" ], "dynamic": [ "[Recent] Sarah's being promoted to VP of Product", "[Recent] Sarah is migrating the billing service off the legacy queue", "[2026-07-14] Sarah hit a deadlock in the payout worker and is still debugging it" ] } } ``` That's the whole interface. The interesting part is where those sentences come from, and what to do with them. ## The profile samples the container A profile isn't a record you populate — it's derived. When you [add content](/add-memories), the ingestion pipeline extracts memories, connects them into the [graph](/concepts/graph-memory), and continuously distills the container's memories into a compact summary of the entity behind them. The profile *samples the container*: it's the engine's answer to "given everything in here, what should an assistant know about this entity right now?" ```mermaid graph LR A[documents you ingest] --> B[derived memories] B --> C[knowledge graph] B --> D["profile (per container tag)"] C --> D ``` Two consequences fall out of that: **One container tag, one profile.** The profile is scoped to exactly one container. It never reads across container tags — a profile call for `user_4f8a` can't surface anything derived from another user's container, full stop. That's the same isolation boundary that governs [search and writes](/concepts/permissioning), and it's why the standard pattern is one container tag per user. **Everything in the container is fair game.** If you put ten users' conversations in one container, the profile blends all ten — because as far as the engine can tell, that's one entity. It can bite you in a subtler way too: ingest a user's email and the profile can pick up facts about the people they *correspond with*, not just the user. The fix is `entityContext` — tell the engine who the container is about ("This container is about Sarah Chen, a Meridian employee; other people mentioned are her contacts, not the subject") and extraction prioritizes accordingly. See [Customization](/concepts/customization). ## Static vs dynamic The two arrays split by how long-lived a fact is, not by topic: - **`static`** — durable facts: who they are, what they do, standing preferences. This is the stuff that's true next month. - **`dynamic`** — recent episodes and current state: what they're working on, what happened lately. Entries carry `[Recent]` or `[YYYY-MM-DD]` prefixes so your model can weigh recency; strip them if you only want the text. Static populates as the engine sees the same durable facts hold up across ingestions — a brand-new container won't have a settled static section after one message, and early on you'll mostly see dynamic entries. {/* CONFIRM: exact static-population timing/threshold */} Older dynamic context doesn't pile up forever either: it gets periodically consolidated into denser summaries, so the profile stays compact instead of growing with the container. Beyond the lifespan axis, you can define **buckets** — topical categories like `preferences` or `goals` that a classifier assigns memories to at ingestion time. Every org starts with a built-in `preferences` bucket; you define more in the console, and request them with `include: ["buckets"]` on the profile call. Buckets and `filterPrompt` (your rules for what's worth remembering at all) are the two levers that shape what lands in a profile — bucket descriptions steer where facts get filed, `filterPrompt` steers what gets extracted in the first place. Mechanics for both are in the [profile API reference](/user-profiles) and [Customization](/concepts/customization). ## Combine profile with search Pass `q` and the same call also runs a search in that container, returned alongside the profile: ```typescript TypeScript const result = await client.profile({ containerTag: "user_4f8a", q: "billing migration", }); result.profile.static; // who Sarah is result.profile.dynamic; // what she's up to result.searchResults?.results; // memories matching "billing migration" ``` ```python Python result = client.profile( container_tag="user_4f8a", q="billing migration", ) result.profile.static result.profile.dynamic result.search_results.results if result.search_results else [] ``` ```bash cURL curl -X POST "https://api.supermemory.ai/v4/profile" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTag": "user_4f8a", "q": "billing migration"}' ``` `searchResults` only appears when you pass `q`. This is the one-round-trip context call: broad understanding plus query-specific memories, instead of a profile fetch and a separate [search](/search). ## Inject it without wrecking your prompt cache The profile is built to sit in a prompt: plain sentences, and compact — it stays within roughly a 1k-token budget rather than growing with the container. But *where* you put each piece matters, because prompt caching works on stable prefixes. The pattern that works: - **Static profile goes in the system prompt.** It changes rarely, so the prefix stays byte-identical across turns and your provider's prompt cache keeps hitting. - **Dynamic context (and `q` results) get appended to the user message.** They change turn to turn — put them in the system prompt and you bust the cache on every message. To wire that up: ```typescript async function buildMessages(userId: string, userMessage: string) { const result = await client.profile({ containerTag: userId, q: userMessage }); const system = `You are a personal assistant. About this user: ${result.profile.static?.join("\n") ?? "No profile yet."}`; const memories = result.searchResults?.results ?.map((r) => r.memory) .join("\n"); // dynamic + search context ride with the message, not the cached prefix const user = `${userMessage} ${result.profile.dynamic?.join("\n") ?? ""} ${memories ?? ""} `; return [ { role: "system", content: system }, { role: "user", content: user }, ]; } ``` If you're on the Vercel AI SDK, `withSupermemory` does this injection for you — see the [AI SDK integration](/integrations/ai-sdk). The profile call is a read — it doesn't count as ingestion, so calling it on every message costs you latency, not quota. It's fast enough to sit in the hot path. {/* CONFIRM: profile latency ~100ms publishable */} ## What profiles are not **Not a key-value store.** There's no API to set a profile field, and that's deliberate — the profile is derived understanding, so it stays consistent with the memories underneath it. If you need to assert a fact, ingest it ("Sarah's preferred language is Portuguese") and the profile absorbs it. **Not cross-user data.** A profile can't aggregate across users, and you shouldn't try to make it — a shared "everyone" container gives you a mushy profile of no one. For team-wide knowledge, use a shared container for the *content* and per-user containers for the *people*; the [multi-tenant pattern](/patterns/multi-tenant-saas) shows the split. **Not a replacement for search.** The profile answers "who is this entity" in ~1k tokens. Specific recall — "what did Sarah say about the payout worker" — is a [search](/concepts/hybrid-search) question. Use both: profile as the standing context, search (or the `q` param) for the details a given message needs. That's it — your assistant opens every conversation already knowing who it's talking to. ## Where next - [Profile API reference](/user-profiles) — every parameter, buckets, response schema - [Permissioning](/concepts/permissioning) — container tags, isolation, scoped keys - [Customization](/concepts/customization) — `entityContext`, `filterPrompt`, and shaping extraction - [AI SDK integration](/integrations/ai-sdk) — automatic profile injection with `withSupermemory`