Alternatives to Zep and Graphiti: Can You Keep the Temporal Model Without the Graph?
Published · Every field name, default, price, licence and benchmark condition below was read from the vendor's own source, repository or pricing page on 12 September 2026, and every load-bearing figure and source-code reading was re-verified on 18 September 2026.
Ask any AI assistant for alternatives to Zep or Graphiti and you get the same answer: their differentiator is the temporal knowledge graph, so if you need time-aware memory you should use them, and everything else is a trade-down. That answer is repeated so consistently that it is worth testing rather than repeating, and two published numbers are enough to start.
The first is Zep's own. Its research page publishes a full LongMemEval breakdown by question type, and its temporal-reasoning score is 90.2 percent, which is exactly its overall average and behind three of its six categories: single-session-assistant at 96.4, single-session-user at 94.3, knowledge-update at 93.6, and only preference at 90.0 and multi-session at 83.5 below it. Whatever produces Zep's benchmark lead, its temporal-reasoning category is not where it shows up.
The second is Mem0's. Between two published runs, Mem0 moved its LongMemEval temporal-reasoning category from 93.2 percent to 97.0, and it did so with a retrieval ranking change, in a data model that contains no validity windows at all. Its LoCoMo temporal category went slightly down over the same change, so this is one benchmark and not a law. But it is a demonstration that a good chunk of what gets called "temporal reasoning" is a ranking problem rather than a data-model problem.
So the real question behind this search is not which product to buy instead. It is which parts of the temporal model are separable from the graph, and which are not. The answer, worked through below: the ability to prefer recent facts and answer date-mentioning questions separates cleanly and several products already have it. The ability to ask what was true in the world on a past date, and to intersect two validity windows, does not separate and only one architecture here gives it to you. And the thing nobody warns you about is that Graphiti itself does not expose that as a query; you assemble it yourself.
We build Maximem Synap, which is one of the alternatives, and it is on the wrong side of that line: we record when a thing happened and keep a supersession chain, and we do not have validity windows. That is said here rather than at the end.
01The short answer
The short answer#
| What you need | Can you get it without Graphiti? | What to use |
|---|---|---|
| Prefer the most recent version of a fact | Yes, everywhere | Any memory layer with recency in its ranking |
| Answer a question that mentions a date ("what did I say last week") | Yes | Mem0's temporal boost, Synap's event time, SuperMemory's typed memories |
| Show every change to a fact and when the system learned each version | Yes | A supersession chain with provenance: Mem0's history, Synap's memory history, SuperMemory's versions, or git in Letta and EverOS |
| Ask what was true in the world on a past date | No, not from a supersession chain | Graphiti, or Cognee's valid_to with real caveats, or Postgres range types you build on |
| Intersect two validity windows ("who owned the account while that contract was live") | No | Graphiti, or Postgres 18 temporal keys |
| Point-in-time query as a single API call | Not even from Graphiti | Nobody. You assemble the predicate yourself |
| The temporal model without operating a graph database | Partly | Zep Cloud, or Cognee's embedded defaults, or Postgres |
| The temporal model without a paid managed service | Yes | Graphiti self-hosted, Apache 2.0, plus a graph database you run |
Everything below is the evidence for those rows, starting with what Graphiti actually contains.
02What Graphiti is
What Graphiti is, from its own source#
Graphiti is an Apache 2.0 Python library that builds a temporal knowledge graph from episodes. It is not a service and it is not a database. Python 3.10 or later, and you bring the database: Neo4j 5.26, FalkorDB 1.1.2, or an Amazon Neptune cluster or analytics graph plus an Amazon OpenSearch Serverless collection, which Graphiti uses as its full-text search backend. Kuzu 0.11.2 is still driven but deprecated and slated for removal, because the upstream project is no longer maintained, which quietly removes the embedded-graph option that most third-party comparison pages still list. New projects should assume Neo4j or FalkorDB, and FalkorDB Lite needs Python 3.12 or later.
Four operational facts from the repository that decide whether you can run it, none of which appears on a comparison page we read.
Structured output is a hard dependency, not a preference. The README: Graphiti "works best with LLM services that support Structured Output (such as OpenAI, Anthropic, and Gemini). Using other services may result in incorrect output schemas and ingestion failures. This is particularly problematic when using smaller models", and elsewhere, "very small models frequently emit JSON that does not match the requested schema, which surfaces as extraction failures" (quoted with the contraction expanded). Local operation is possible through any OpenAI-compatible endpoint with structured_output_mode set to json_schema or json_object, and the repository's own caveat is that reliability varies across providers and especially on smaller and local models. If your reason for self-hosting is that you cannot call a frontier API, read that paragraph twice.
Concurrency is capped globally, and the number is disputed by its own documentation. graphiti_core/helpers.py line 38 reads SEMAPHORE_LIMIT = int(os.getenv('SEMAPHORE_LIMIT', 20)); the README says the default is 10. Either way it is one asyncio.Semaphore shared across every parallel stage in the library, so extraction, deduplication, the candidate searches and the reranking all draw on the same budget of in-flight coroutines. That is why Graphiti feels slow on first run, and the README tells you the fix in as many words: if you find it slow, increase concurrency, and watch for 429s from your model provider.
Telemetry is opt-out. Every Graphiti initialisation sends an anonymous identifier, stored at ~/.cache/graphiti/telemetry_anon_id, along with the operating system, Python version, architecture, Graphiti version, model provider, database backend and embedder provider to PostHog, unless you set GRAPHITI_TELEMETRY_ENABLED=false. Set it before an air-gapped deployment rather than after.
group_id is the whole of multi-tenancy. It is a partition key. There is no access control attached to it, so cross-namespace questions are application code and N round trips, and "one graph per subject" is the shape Zep's own page describes for Graphiti while reserving "millions of governed context graphs" for itself.
One version note, because the cadence is brisk enough to date a comparison page: the latest tag on GitHub is v0.30.2, cut on 8 September 2026, and the graphiti-core package on PyPI is at 0.30.2 from the same day. Four tags landed between 8 June and 8 September 2026, so treat any page quoting a v0.29 number as a quarter behind.
03What Zep adds
What Zep adds, in Zep's own words#
Zep publishes the comparison itself, which saves everyone an argument. Against Graphiti, Zep Cloud provides: a proprietary Context Graph Engine so that no third-party graph database vendor is required; management of "vast numbers of per-user/entity context graphs with governance" where Graphiti builds and queries individual graphs; built-in users, threads and message storage, where Graphiti says build your own; pre-configured production retrieval with sub-200 millisecond performance at scale, where Graphiti says custom implementation required and performance depends on your setup; a dashboard with graph visualisation, debug logs and API logs, plus SDKs for Python, TypeScript and Go, where Graphiti says build your own tools; and service level agreements, support and security guarantees, where Graphiti says self-managed. Zep's own summary line is the cleanest: "Graphiti builds the graph; Zep operates it at scale."
One thing on that list is more consequential than the others and is easy to miss. Zep attributes to itself, not to Graphiti, its "proprietary extraction LLMs, reranker, and embedding models". Self-hosting Graphiti reproduces the architecture, not the pipeline, which is the reason Zep's published accuracy and latency figures do not transfer to a Graphiti deployment and Graphiti's own README says performance depends on your setup. If you self-host and measure 600 milliseconds, you have not misconfigured anything.
04What bi-temporal validity is
What bi-temporal validity actually is#
Two independent timelines per fact: when it was true in the world, and when your system believed it. Graphiti puts the validity window on the edge and never on an entity node, and there are five timestamps on it rather than the four every comparison page lists. An episode node carries its own valid_at, which is the reference time of the document rather than a window, and an entity node carries nothing but created_at.
| Field | What it means | Zep's own example |
|---|---|---|
created_at | When the system learned the fact | "The time Zep learned that the user got married" |
valid_at | When the fact became true in the world | "The time the user got married" |
invalid_at | When the fact stopped being true in the world | "The time the user got divorced" |
expired_at | When the system learned it had stopped | "The time Zep learned that the user got divorced" |
reference_time | The reference timestamp of the episode that produced the edge | Not documented in the comparison pages |
Three mechanics matter more than the field list.
valid_at is inferred, not supplied. You pass a reference_time into add_episode(), and Graphiti then makes a dedicated model call, extract_edges.extract_timestamps, whose entire job is to read the fact text against that reference point and return valid_at and invalid_at. So "I left Acme last March" can produce a valid_at earlier than the message that carried it. This is the feature working as designed, and it has a migration consequence covered below: a derived value that was never computed cannot be exported.#
Invalidation is mechanical, and it runs in two passes. When deduplication marks a new edge as contradicting existing ones, Graphiti first checks the incoming edge against its contradiction candidates: if any of them became valid later than the incoming edge, the incoming edge is the stale one, and Graphiti expires it, setting its invalid_at to that candidate's valid_at and its expired_at to now. Only then does resolve_edge_contradictions run, and it expires an existing edge only when that edge became valid earlier than the incoming one, setting its invalid_at to the new edge's valid_at and, if it does not already carry one, its expired_at to now. The part people miss is that this is what stops a late-arriving stale message overwriting current truth, and that neither pass touches a pair of facts whose windows do not overlap.#
There is no point-in-time query API. SearchFilters carries valid_at, invalid_at, created_at and expired_at, each typed as a list of lists of date filters, which is an OR of ANDs of comparison operators. You assemble valid_at <= T AND (invalid_at > T OR invalid_at IS NULL) yourself. There is no as_of parameter, and the official searching documentation does not mention date filtering at all. A widely mirrored third-party wiki page shows SearchFilters(valid_time=datetime(...)); that parameter does not exist in the source, and if you have copied it from there it has never worked.#
The question only a validity window answers#
Here is the one to hold in your head while reading the rest of this page, because it is the test that separates the four architectures below.
"Which of our customers were on the Enterprise plan on 3 March, and which of those had an open compliance exception at the same time?"
A store that knows only current truth answers the wrong question. A store with an event time but no end time knows when the plan started and cannot tell you it had already ended. Only start and end on both facts lets you intersect two intervals. If you have never needed to ask something of that shape, and most products have not, the rest of this page will save you money.
05Does the graph win on temporal reasoning
Is the temporal model where Zep's lead comes from?#
No, on Zep's own published breakdown, and this is the single most useful thing in the comparison.
Zep's research page gives LongMemEval by question type: single-session-assistant 96.4 percent, single-session-user 94.3, knowledge-update 93.6, temporal reasoning 90.2, single-session-preference 90.0, multi-session 83.5, for an overall 90.2 percent over 451 of 500 questions, with reader and judge both gpt-5.4 and no run date. Its LoCoMo temporal category is 95.6 percent over 311 of 325 questions against an overall 94.7. Read those two ways. Against its own average, Zep's temporal reasoning is middling. Against the alternative of no memory layer at all, it is where the original paper's largest gain sat: the January 2025 paper reports temporal reasoning rising from 36.5 percent for full-context gpt-4o-mini to 54.1 percent with Zep, and 45.1 to 62.4 percent on gpt-4o, judged by GPT-4o against the benchmark's own question-specific prompts, with no per-category question count published. Both readings are true and they answer different questions. Temporal modelling buys a great deal against stuffing a transcript into a prompt, and it is not what puts Zep above other memory layers on a leaderboard.
Mem0 is the counter-example that makes the point sharper. Its published category breakdown moved temporal reasoning from 93.2 to 97.0 percent through a retrieval change described as boosting memories whose date metadata matches the query, with no validity windows in the data model, while its LoCoMo temporal category moved from 92.8 to 92.5. SuperMemory publishes per-category Recall@15 with temporal reasoning at 91 percent, which is a retrieval metric and must never be charted against an accuracy figure. And the vendors publishing no temporal sub-score at all are Cognee, whose research page shows only BEAM aggregates, Hindsight, whose results payload returns a null category on every row today, Letta, which publishes one LoCoMo figure and no breakdown, and us.
None of those figures is comparable with any other. Zep names its reader and judge and omits the run date; Mem0 names neither model; SuperMemory's metric is a different metric. The point is not the ranking. It is that "temporal reasoning", as the only benchmark that measures it defines the category, is substantially answerable by ranking, and the vendor with the best temporal architecture does not lead its own scorecard on it.
One check catches most of what a conditions list misses. Ask whether every system in the table ran on the same configuration, because a vendor chart that puts its own tuned retrieval mode beside its rivals' defaults is measuring its tuning rather than its architecture, and the tell is a configuration name that appears in one column and nowhere else.
06What the temporal graph costs
What the temporal graph costs to run#
Three bills, and the one people underestimate is the reranker.
Ingestion. One add_episode() call is not one model call. The floor is four, plus one to two per extracted edge: entity extraction, node deduplication, a batched entity-summary call, and edge extraction, then one edge-resolution call per extracted edge and one timestamp-extraction call per new edge that did not get timestamps during extraction, plus attribute extraction for anything matching a custom type. An episode yielding five edges is therefore roughly fourteen model calls. It also issues two hybrid graph searches per extracted edge, for duplicate candidates and invalidation candidates, so ingestion loads your graph database as well as your model provider. Community building is off by default, which is the one large stage you are not paying for unless you ask for it.
Retrieval. The default cross-encoder reranker is one model call per candidate passage, using a small model with a single output token and log probabilities. That is the largest read-path cost lever in Graphiti and almost nobody writes about it. A local alternative ships in the box, a BGE reranker client, and switching to it is the first thing to try if your read path is expensive rather than slow.
The graph database. Managed Neo4j AuraDB Professional is $65 per gigabyte per month with a one-gigabyte minimum, and Business Critical is $146 per gigabyte with a two-gigabyte minimum. FalkorDB Cloud starts around $73 for one gigabyte on its startup tier and $350 for eight gigabytes on Pro. Amazon Neptune gives a 30-day free allowance of 750 instance-hours, ten million I/Os and a gigabyte of storage, then charges per instance-hour, per gigabyte-month and per million I/Os, and Graphiti additionally requires an OpenSearch Serverless collection alongside it, which is a second bill.
Nobody publishes an episodes-per-second throughput figure for Graphiti or Zep, so plan a load test rather than trusting an estimate.
Run the numbers on your own traffic.
The playground runs the memory engine behind our figures — no signup, no key. Or start free and point your own agent at it.
07Getting it without Graphiti
The four ways to get time-awareness without Graphiti, and the fifth answer#
One: bi-temporal edges in another engine#
In agent memory, there is no second option. No product in this comparison other than Graphiti and Zep puts start and end validity on a relationship. Two adjacent facts are worth knowing. First, no graph database Graphiti runs on has native bi-temporality either: Neo4j, FalkorDB and Neptune give you properties and you implement the semantics, Memgraph's nearest feature is a time-to-live that deletes rather than invalidates, and Datomic records transaction time only. The complete SQL:2011 contract, with automatic valid-time and system-time columns and an AS OF clause, exists in XTDB, which is not a graph. Second, Cognee ships a Graphiti-backed temporal mode, so if what you want is Graphiti's model without adopting Graphiti's surface, Cognee will run it for you, with graphiti-core's Neo4j requirement coming along.
Two: a single validity field#
Cognee's open-source primitive is the closest non-Graphiti thing and its limits are specific enough that you must know them before choosing it for this reason. The field is valid_to, a millisecond epoch on a data point, and there is no valid_from to pair with it. It sits on a node, not an edge. It persists only on the Ladybug backend; other backends log a warning and return false. Closing is last-write-wins and not idempotent. And the limit that matters most is Cognee's own sentence: search and graph completion neither filter nor down-weight closed nodes. So the field records that a fact ended; nothing in the read path acts on it unless you write that yourself. What Cognee's Enterprise engagement adds, listed on its pricing page as "bi-temporal memory and conflict resolution", is not documented anywhere, so treat the open primitive as what you are buying on a self-serve plan.
Three: event time plus a supersession chain#
Most of the field sits here, including us, and it is worth being precise about what it can and cannot do, because it is more capable than the incumbent framing admits and less capable than its vendors imply.
Mem0 stores a timestamp per memory, an expiration_date that hides a memory from search after a date without deleting it, and a Platform-only temporal reasoning boost that scores candidates on date metadata extracted at write time; superseded facts are kept and filterable. SuperMemory records an updates relation and a latest flag, keeping history for audit. Synap records two dates per statement, when it was stored and when the thing it describes happened, and keeps a provenance chain from a superseded statement to its successor with the reason for the change. Letta and EverOS put memory in git, so the chain is commit history.
What that shape gives you, exactly: the transaction axis, completely. When you started believing a fact is your own write time; when you stopped believing it is the write time of its successor. So created_at and expired_at fall out of a supersession chain for free, and with them the three questions auditors actually ask, which are why does the agent think this, show me every change to this record, and prove we did not know X before date D.
What it does not give you: the valid-time axis. A chain says "on 12 April we replaced plan equals Pro with plan equals Enterprise". It does not say the upgrade took effect on 1 March, and no amount of chain-walking recovers that, because the information was never captured. Two consequences follow and both are worth stating plainly. Retroactive corrections become invisible: learn on 20 April that the change was really on 1 March, and the chain records a change on 20 April while the question "what plan on 15 March" now answers wrongly with no signal that it is wrong. And overlapping-window questions die entirely, because an interval problem needs two intervals and a chain gives you two points.
There is a cheap mitigation that closes most of the gap and almost nobody does it. When you extract a statement, also extract and store an explicit effective-from date, and an effective-to when the text supplies one, as ordinary fields separate from your write time. Nothing enforces them and nothing indexes the intervals, so it is not bi-temporality in the database sense, and it recovers most of the questions that matter at a cost of one prompt instruction. If you are evaluating a store in this category, ask the vendor whether you can put your own effective date on a memory and filter on it. That is a more useful question than asking whether they have a temporal model.
Four: build it, which got materially cheaper in 2025#
The database half is nearly free now, and this is new enough that no comparison page has caught up. Postgres has always had tstzrange with GiST indexing and the exclusion constraint that stops two versions of a fact claiming the same instant. As of PostgreSQL 18, released 25 September 2025, you no longer hand-roll even that: the release notes record support for non-overlapping primary key and unique constraints via WITHOUT OVERLAPS, and temporal foreign keys via PERIOD. That is SQL:2011 temporal keys in stock Postgres, so PRIMARY KEY (subject, predicate, object, validity WITHOUT OVERLAPS) gives you real temporal identity and REFERENCES ... PERIOD validity gives you temporal referential integrity. Add two plain columns for the transaction axis and you have both timelines in one range column and two timestamps.
pgvector complicates exactly one thing: with approximate indexes, filtering is applied after the index is scanned, so a query that a validity predicate filters heavily returns fewer rows than your limit asked for. The documented mitigations are an index on the filter column and, from pgvector 0.8.0, iterative index scans that keep scanning until enough results are found.
What you still have to write is the whole product, and in rising order of difficulty it is extraction, entity resolution, the invalidation decision, and retrieval ranking. Graphiti spends four model calls per episode plus one to two per edge on the first of those alone. Build it if you have an unusual temporal requirement and a small, well-understood extraction problem. Do not build it because the schema looks easy, because the schema is the part that is easy.
And the fifth answer, which is not a way of getting it: decide you do not need it#
The most common right answer, and the one no vendor page will give you. If nobody on your team has asked a question of the form "what was true on date D" or "while X was the case", and your failures are "it forgot what I told it" and "it repeated something that changed", then you need recency in ranking and a supersession chain, and you can have both from any product in the next section at a fraction of the operational cost. Buy the graph when a question needs it, not when a comparison table has a row for it.
08The alternatives
The alternatives#
Read this list against the four architectures above rather than as a ranking. Cognee and a bare graph database are the graph options; Mem0, Synap, SuperMemory, Hindsight and EverOS are statement stores with an event time and a supersession chain; Letta is a runtime; LangMem and the cloud primitives are neither. The entry that matters for most readers is the last one, because most of what people dislike about Graphiti is configuration rather than architecture.
Cognee. The closest architectural neighbour that is not Zep's own engine: Apache 2.0, roughly 30,000 stars, relational plus vector plus graph storage with ontologies, embedded defaults that need no separate graph database, the deepest permission model in the open-source group with tenants containing users containing roles containing dataset access, and the widest file-format ingestion here. remember, recall, forget and improve are first-class verbs, which makes it one of the few systems where deletion is part of the API rather than bolted on. Cloud is $1.00 per million tokens with a free million and no card, and managed connectors are the thing self-hosting does not hand you. Its temporal story is section two above, with the caveats. It publishes no LongMemEval or LoCoMo figure; its benchmark is BEAM, and its own report rather than its comparison chart is the figure to cite, because the competitor rows on that chart read differently today than they did two days ago.
Mem0. The widest adoption and the shortest path to working memory, sold as an Apache 2.0 library and a managed Platform, 22 named integrations including the LangChain and LlamaIndex that Zep lacks, a hosted MCP server, and $19 to start. It is also the empirical case for separability, having improved its temporal-reasoning category by ranking alone. Two things to check before choosing it for this reason: the write path has been additive since April 2026, so a changed fact sits beside the old one and ranking is expected to surface the newer, with background consolidation marking supersession; and graph memory was removed from the open-source SDK entirely, so the self-hosted build has entity linking without a traversable graph.
Hindsight. MIT, from Vectorize, and the one that answers "how many retrieval strategies run" with four in parallel rather than one: semantic, keyword, entity-graph traversal and temporal filtering, merged by a cross-encoder. It self-hosts from a single Docker command, with an embedded PostgreSQL for development and an external Postgres with pgvector for production, and runs offline against a local model. It records when events occurred and uses that for temporal search, and it documents no validity end date. It is append-only with no endpoint to permanently delete an individual memory, which is the wrong shape if erasure is a requirement.
Maximem Synap. Ours. A statement store rather than a graph: each memory is a self-contained sentence with a type, a confidence score, a storage time and an event time. It is in category three above, so it answers the transaction-axis questions and not the world-axis ones, and Graphiti is the better tool if you need the second kind. What it has that neither Zep nor Graphiti does: a scope ladder with client, customer and user rungs plus the conversation, enforced at write time, where a read prefers the narrowest rung and inherits everything above it and never sees a rung below or a neighbour, extendable to any depth with your own rung names; lossless supersession by rule, so a newer statement that is poorer than the one it resembles never replaces it and the old one is kept, marked historical and linked to its replacement with the reason; entity resolution that routes ambiguous matches to a review queue; and an anticipatory read path that pushes likely context into a cache inside your own process, which is where the asserted in-conversation P75 under 15 milliseconds comes from, with a cold-retrieval P95 of 450 milliseconds published as a target. Ingestion covers conversation, documents, images and audio, with seven first-party connectors. Pricing is credits at $0.00175, from a free Trial through $19, $249 and $999, with every plan getting every memory capability. Benchmarks are 92 percent on LongMemEval over the full 500 questions and 93.2 percent on LoCoMo over 1,540 in categories one to four, gpt-5-mini as both answer model and judge, binary judging, a single run, reproduced on our open harness rather than independently verified. We publish no temporal-reasoning sub-score, which on the evidence in this article is a gap we should close rather than a detail.
SuperMemory. The breadth option: audio, video, images, PDFs with OCR, Office files, standing user profiles, typed memories where preferences strengthen and episodes decay, and, contrary to several comparison tables, SOC 2 Type II with a HIPAA business associate agreement and a self-hosted option, all at the $399 Scale tier. Its headline is a recall metric, not accuracy.
Letta. An agent runtime rather than a memory layer, formerly MemGPT, with memory as git-versioned Markdown the agent edits itself. No validity fields, no entity resolution, and the only native diff and rollback in this set, because those are git operations. Adopting it means adopting an agent loop and its current SDK is TypeScript only.
LangMem and the LangGraph store. For a team already on LangGraph that wants no new vendor. A namespaced document store with optional embedding search, plus a prompt optimiser that is the clearest procedural-memory implementation here. No temporal fields, no entity resolution, and its last release was October 2025.
Evermind EverOS. Apache 2.0, local-first, Markdown with SQLite and LanceDB indexes, agent cases and skills beside user episodes, human-readable and git-diffable. Its published figures are LoCoMo 93.05 percent and LongMemEval-S 83.00 percent with GPT-4.1-mini answering and a GPT-4o-mini-led judge panel, with baselines taken from another leaderboard rather than re-run.
A graph database and your own extraction. Neo4j, FalkorDB or Neptune with code you write. You get exactly the schema you want, including real bi-temporality if you implement the semantics, and you own extraction, deduplication, invalidation and ranking. This is Graphiti's job description, so the honest version of this option is "fork or vendor Graphiti", not "start from the database".
Cloud primitives. Amazon Bedrock AgentCore Memory has been generally available since October 2025, Google's Memory Bank re-priced from September 2026, and Microsoft Foundry's memory is in public preview. None publishes a LongMemEval or LoCoMo figure, none models validity windows, and each ties your memory to its cloud.
Staying on Graphiti and replacing only the part you dislike. Worth naming as a first-class option, because most of what people dislike is swappable. The reranker is a configuration choice and the local BGE client removes a model call per candidate. The concurrency cap is an environment variable. The graph backend is a driver. Telemetry is one variable. The embedded-database option going away is real and pushes you to Neo4j or FalkorDB, and that is the change most likely to be the actual reason to leave.
09Memory or context
Memory or context: which layer are you buying#
One distinction worth drawing before the axes, because it decides what is still unbuilt after you buy. A memory layer decides what persists: it extracts, reconciles, retires and scopes durable facts, and everything in this comparison is one. A context layer decides what goes into the prompt on this turn: it assembles, budgets tokens, orders and truncates. The model never uses memory directly; it uses context, and the two are different jobs.
What you still owe after buying only one of them: buy a memory layer and you still write the assembly, the token budget and the decision about what to pin versus retrieve, unless the vendor ships a pre-assembled block, which Zep does with its Context Block and most statement stores do not. Buy only a context layer, meaning the products that sync your company's systems into a prompt-ready surface, and you still owe extraction, supersession, deletion and scoping, because a context layer assembles what it is given and decides nothing about what survives. Sorting this page: Zep is the only vendor here that sells both halves as a product; Graphiti ships the memory half and tells you to build the tooling; Mem0, Cognee, SuperMemory, Hindsight and Synap ship the memory half with a formatted block or a ranked list at the boundary; Letta collapses the distinction rather than shipping both, because its pinned files are the context, which is a different thing from buying two layers.
10The axes
The axes#
Temporal model, per vendor#
| Vendor | Validity fields | On what | Point-in-time query | Retrieval acts on it |
|---|---|---|---|---|
| Graphiti and Zep | created_at, valid_at, invalid_at, expired_at, plus reference_time | Edges only; nodes have none | No API; assemble the predicate from SearchFilters | Yes, once you write the filter |
| Cognee, open build | valid_to only, no valid_from | A node | No | No: search neither filters nor down-weights closed nodes |
| Mem0 | Timestamp, expiration_date | A memory | No | Yes, via a Platform-only date-matching boost, and expiry hides from search |
| SuperMemory | Latest flag, updates relation | A memory | No | Yes, supersession applies at search time |
| Maximem Synap | Storage time and event time, plus a supersession chain with provenance | A statement | No | Yes, recency is weighted with a 30-day half-life |
| Hindsight | Event occurrence time | A memory | No | Yes, temporal filtering is one of four parallel strategies |
| Letta, EverOS | None; git commit times | Files | No | No |
| LangMem, cloud primitives | None documented | None | No | No |
| Postgres you build | Whatever you declare; PG 18 gives you WITHOUT OVERLAPS and PERIOD | Rows | Yes, it is a WHERE clause | Yes, and pgvector post-filters |
Free tiers, in units#
Whether you can evaluate without a purchase order, read today. Zep gives 10,000 credits a month with two projects, which at one credit per 350-byte episode is roughly 3.5 MB of ingestion with unlimited reads: enough to evaluate a read path properly, tight for a document corpus. Mem0's Hobby gives 10,000 add requests and 1,000 retrievals, which is the opposite shape and will run out on reads first. Cognee gives a million tokens, one workspace, unlimited users and unlimited API calls with no card, plus the whole engine free forever self-hosted. SuperMemory gives $5 of credit a month with the full API. Synap's Trial is free with 5,000 credits. Graphiti, Hindsight's open build, Letta's harness and EverOS are free and unlimited, and you pay the model and infrastructure bill instead. The warning worth more than any of those numbers: a tier metered on writes runs out during a backfill of real transcripts, and a tier metered on tokens runs out on documents, so choose your evaluation corpus to match the meter.
Graph operation#
Graphiti requires a graph database. Cognee ships embedded defaults, so a self-hosted install needs no separate graph database at all, and it is the shape to reach for if you would rather run one container than four. One correction to a claim that circulates about it: running Cognee's whole memory layer on a single Postgres instance is, in Cognee's own words, a demo feature, with the production version available as a licensed product, so do not plan a production deployment around it. Mem0's open build removed external graph stores entirely in version 3. Hindsight embeds a PostgreSQL build for development and expects an external Postgres with pgvector in production, so it removes the graph database rather than the database. Nothing else here operates a graph at all. If your objection to Graphiti is the fourth container, Cognee is the closest thing to a like-for-like answer.
Retrieval#
Hindsight runs four strategies in parallel with a cross-encoder merge. Zep runs semantic, BM25 and graph traversal in a single ranked result with a cross-encoder at the highest cost tier. Graphiti gives you the same searches and a reranker you choose. Mem0 fuses semantic, keyword, entity and temporal signals at a top-k budget. Synap runs several techniques concurrently and degrades to a narrower answer with a flag rather than failing. The question worth asking is not the storage model but how many paths a query has to succeed on, because a single-strategy system fails on the query whose vocabulary does not match its index.
Latency#
Zep publishes 87 milliseconds p50 and 155 p95 on LoCoMo and 104 and 162 on LongMemEval, plus sub-200 milliseconds regardless of graph size. Graphiti publishes nothing and tells you performance depends on your setup, which given the per-candidate reranker is a fair warning. Mem0 publishes 0.88 seconds p50 on LoCoMo with no p95. SuperMemory states under 300 milliseconds. Hindsight's own runs average 675 milliseconds. Synap publishes an asserted in-conversation P75 under 15 milliseconds from a pre-fetched in-process cache and a cold P95 of 450 milliseconds as a target. Cognee, Letta, LangMem and EverOS publish nothing.
Price, at one stated volume#
Meters differ so entry prices tell you little. Take a month of 50,000 ingested episodes of about 350 bytes, roughly 4.4 million tokens, plus 10,000 retrievals.
| Vendor | Unit | That month costs |
|---|---|---|
| Zep Cloud | 1 credit per 350-byte episode, retrieval free | 50,000 credits, which is exactly the $125 Flex allowance; reads add nothing |
| Graphiti self-hosted | Nothing to the vendor | Your model spend for roughly 4 calls per episode plus 1 to 2 per edge, a reranker call per candidate passage, and a graph database from $65/GB/month on managed Neo4j with a 1 GB minimum |
| Mem0 | Add and retrieval requests | 50,000 adds fits Starter's 50,000 exactly, but 10,000 reads is twice Starter's 5,000 cap, so Pro at $249 |
| Cognee Cloud | $1.00 per million tokens | About $4.40 |
| SuperMemory | $0.005 per 1,000 plain-text memory tokens, $0.005 per 1,000 searches | About $22 plus $0.05 |
| Maximem Synap | Credits at $0.00175; ingestion max(2, tokens/500); retrieval 1 fast | 100,000 ingestion credits plus 10,000 retrieval credits is 110,000, so Pro at $249 covers it |
The ranking inverts with your read-to-write ratio, which is the actual lesson: a token-metered vendor is cheapest for bulk text, a request-metered vendor is worst for a chatty agent, and Zep's per-episode credit makes batching a bigger lever than switching, because ten turns sent singly cost ten credits and the same ten in one episode cost four. Add the graph-database line to the Graphiti column before comparing it to anything.
Contradiction handling without edge invalidation#
Graphiti invalidates and keeps history. Cognee closes a node, with the caveat that the read path ignores it. Mem0 is additive with background supersession marking. SuperMemory versions and flags. Synap supersedes by rule and never lets a poorer statement replace a richer one, keeping the old statement linked to its replacement with the reason. Letta and LangMem leave it to the model rewriting text. The practical test for any of them is the same, and it is worth running before you sign: store "takes 20mg of Atorvastatin daily", then say "takes a cholesterol medication", then ask what the person takes.
Entity resolution#
The capability that makes one person one person across channels, and the mechanisms differ enough to matter. Graphiti runs an LLM deduplication step at ingest returning a duplicate-candidate identifier or none for each extracted entity, and supports developer-defined entity types. Hindsight matches on trigram similarity, with a 0.15 candidate threshold and a 0.5 in-batch merge threshold, reinforced by co-occurrence, and its own documentation warns that a short new name can be absorbed into an existing entity by mistake. Cognee ships an opt-in consolidation pipeline clustering entity names at 0.85 cosine similarity with a dry-run mode. Mem0 stores each entity once and embeds it so that differently phrased references match, with no threshold or review control described. Synap matches in order exact, alias, semantic then contextual, narrowest scope first, and sends ambiguous matches to a dashboard review queue rather than guessing. SuperMemory, Letta, LangMem and EverOS document nothing here.
n-ary facts#
An edge has two endpoints and many facts have more. "Sarah approved the Q3 budget for the Berlin office on 12 March" has four participants. In Graphiti you either fold the extras into the edge's fact sentence and typed attributes, which keeps the sentence and makes Berlin unqueryable as an endpoint, or you reify the approval into its own node with four edges, which is queryable and is schema design you do rather than something the engine does. A statement store never has the problem and never gets the query: the sentence survives whole and you cannot ask which approvals Sarah made for Berlin. Neither is better; they fail in opposite directions.
Adapters, SDKs and MCP#
| Vendor | Framework adapters | SDK languages | MCP |
|---|---|---|---|
| Zep | 14 named, with no LangChain and no LlamaIndex | Python, TypeScript, Go | Hosted Memory MCP, seats per tier |
| Graphiti | None; it is a library you call | Python | MCP server in-repo |
| Mem0 | 22 named, including LangChain and LlamaIndex | Python, TypeScript | Hosted, eleven tools, free with a key |
| Cognee | Nine agent-framework adapters among 26 integration pages | Python, TypeScript, Rust | cognee-mcp, included in the free tier |
| SuperMemory | The main Python and TypeScript frameworks | Python, TypeScript | Hosted, with coding-agent plugins |
| Hindsight | None documented | Python, TypeScript, Go | Built in, one endpoint per bank |
| Maximem Synap | 23 installable packages | Python, TypeScript, REST, CLI | Hosted, four tools, holds no secrets |
| Letta | None; it is the runtime | TypeScript only | MCP client, not a server |
| LangMem | LangGraph only, by design | Python | None |
Observability of the extraction decision#
The thing you can see differs more than the thing you can store. Zep is the only vendor here that explains an extraction decision: Debug Mode captures episode workflow logs, and Ingestion Traces explain why selected model stages produced each decision, retained 30 days, with traces unavailable on the free tier and the debug window at 60 minutes on standard plans and 24 hours on Enterprise. Graphiti has per-stage logging and a tracing extra, so you can see what ran without being told why it decided. Mem0 gives a per-memory history endpoint returning the event type with the old and new text and the triggering messages, plus a project event feed. Cognee has an optional provenance ledger, a contradiction-detection flag and a graph visualiser. Synap records what a statement replaced and why, readable in the dashboard, with audit logs retained 90 days. SuperMemory versions updates and exposes request logs. Letta and EverOS give you git. LangMem and the cloud primitives expose nothing.
Scoping, deletion, compliance, maturity#
Briefly, because these are the same across this comparison and the P0 pages on Zep cover them at length. Scoping: Graphiti has group_id as a bare partition key; Zep adds UserGroups with attribute-based access control; Cognee has a real tenant hierarchy; Synap has the client, customer, user ladder; everything else keys on identifier strings. Deletion: a graph makes it materially harder, and Zep documents the sharp edges in detail, including that a user entity node is never deleted even when all its episodes are; Cognee's forget reaches graph, vectors and session cache; Hindsight cannot hard-delete a single memory at all. Compliance, ranked on what is actually held: Zep with SOC 2 Type II and a HIPAA business associate agreement, both gated to Enterprise, with audit-log retention tiered up to seven years; SuperMemory with SOC 2 Type II and a BAA from its $399 Scale tier; Vectorize, which builds Hindsight, SOC 2 Type II at company level with on-premise deployment on Enterprise; Mem0 stating SOC 2 Type I with HIPAA described as ready and on-premise on Enterprise; Cognee holding neither SOC 2 nor ISO 27001 and saying so; Synap with SOC 2 Type II in progress against a Q3 2026 target and HIPAA explicitly not certified. Graphiti self-hosted has none by construction, which for some readers is the point. Maturity: the signal worth acting on is whether a vendor has already withdrawn something, and two here have, Zep by deprecating Community Edition and Mem0 by removing graph memory from its open SDK.
11What noise costs
What noise costs, as distinct from a bad retriever#
Worth separating because the fixes are different and only one of them is a retrieval problem. A bad retriever misses a fact that is in the store, and the fix is more retrieval paths or a better index. A noisy store returns facts that are present, findable and no longer true, and the fix is supersession discipline and a confidence floor. The second failure is the expensive one for three reasons. It costs tokens on every turn, because every superseded fact that survives retrieval is billed again in the prompt. It costs accuracy in a way a retrieval metric cannot see, because the system found the right document and the wrong version of it. And it costs support time, because the agent said something confidently wrong rather than saying nothing, which is the class of error a customer escalates. That is why additive write paths deserve scrutiny: a design that stores the new fact beside the old one and relies on ranking to hide the old one has converted a data-model problem into a ranking problem, which is exactly the trade this article says is available, and which you should make knowingly rather than discover.
12Why the answer repeats
Why every assistant gives the same answer#
One more thing worth naming, because it explains the search result that brought you here. The consensus that Zep and Graphiti own temporal reasoning is a data-model claim being repeated as a capability claim. It traces to two real things: one unusually clear vendor sentence about bi-temporal edges, and one real paper delta showing large gains against no memory layer at all. Everything after that is echo, including the competitors' own pages, which repeat it while selling against it. Three things make the echo out of date rather than wrong. It predates Kuzu's deprecation, which removed Graphiti's embedded-database option. It predates the retirement of Zep's self-hostable Community Edition, which changed what "use Graphiti instead" means. And it predates Mem0 publishing a temporal-reasoning improvement produced by ranking. The honest summary is that no graph engine Graphiti runs on is itself bi-temporal, no benchmark shows temporal graphs leading other memory layers on the temporal category, and the architecture is still the only way to get the half of the model that does not separate.
13Migrating
Migrating#
Off Graphiti, when you own the database. Better than you would expect, because you have the database. Everything is queryable directly in Neo4j, FalkorDB or Neptune, the episode is the original text you sent, and the derived graph is a reproducible function of it. Export the episodes as your system of record and the edges as a validation set.
Off Zep Cloud. Harder, and one fact decides it: Zep publishes no export endpoint. Its documentation sitemap holds 324 URLs and none matches export, backup, download, dump or migrate, and the only migration guide runs inbound from Mem0. What you do instead is walk the cursor-paginated list methods for episodes, nodes, edges, observations and thread summaries, which Zep's own copy nominates for exactly this job. Two traps: episode pages cap at 50 with an explicit limit, and an API key with active attribute constraints returns edges without their endpoint names and labels, so run the export under a key without them.
What has no destination in a statement store. The typed graph itself, meaning adjacency, your ontology's labels, node communities and multi-hop paths. And one of the two time axes, because Graphiti derives them: valid_at and invalid_at are produced by a model call reading the fact text, so if you are moving into a store that never runs that step, they have nowhere to land, and if you are moving out of a store that never ran it, they were never there.
Keeping an audit trail afterwards. You can reconstruct the transaction axis exactly from a supersession chain plus write times, and provenance completely if you carry the source reference. You cannot reconstruct world-time. Extract an explicit effective date into an ordinary field at write time, as described above, and you close most of the gap for the cost of one prompt instruction.
The bake-off. Take fifty of your own conversations, split each so facts established early are needed late, run both candidates with the same answer model and the same judge kept blind to which system produced the context, and report error bars. Then add a temporal suite, because none of the standard harnesses will do it for you: a fact that changes mid-conversation and a question asked after the change; a retroactive correction, where the change is reported later than it happened; a two-interval question; and a stale message arriving out of order, which is the case Graphiti's invalidation logic handles and a naive supersession chain does not.
14Which to pick
Which to pick#
You need the temporal model and must self-host. Graphiti, on Neo4j or FalkorDB, with telemetry disabled, the concurrency cap raised, the local reranker swapped in, and no expectation that Zep's published figures apply. Or Cognee, which will run Graphiti for you inside a product with a real permission model.
You need the temporal model and will not operate a graph database. Zep Cloud, or Cognee on its embedded defaults, remembering that its single-Postgres mode is a demo feature, or Postgres 18 with your own schema if your extraction problem is narrow.
You do not need the temporal model and want lower cost and latency. Any statement store: Mem0 for the widest integration surface, Synap for scoping depth and an in-process read path, SuperMemory for ingestion breadth, Hindsight for retrieval breadth and a single self-hosted container.
You need documents and knowledge as well as conversation. Cognee or SuperMemory. Zep accepts message, text and JSON only, with a 10,000-character cap per graph write and no image or audio path.
You need a tenant hierarchy enforced at write time. Cognee or Synap. Graphiti's group_id is a partition key with no access control, and Zep's UserGroups are a security grouping rather than a tenant object.
You need compliance attestations on the contract. Zep on Enterprise, or SuperMemory at $399. Graphiti self-hosted has none.
You need memory inside a coding agent. All but Letta host or ship an MCP server; Graphiti's is in-repo, Cognee's is in its free tier, and Letta is the exception because it is the coding agent.
You should not move. If validity windows, point-in-time answers or an attested compliance package are load-bearing, and you are on Zep Cloud, staying is the cheaper decision. Swap the parts you dislike instead.
15What this is worth
What this is worth#
The technical conclusion is that the temporal model decomposes into two capabilities with very different prices. Preferring recent facts and answering date-mentioning questions is a ranking feature, several products have it, and Mem0's own numbers show it moving without a data-model change. Asking what was true in the world on a past date, and intersecting two such windows, is a data-model feature, and in agent memory exactly one architecture gives it to you, without giving you a query for it.
The business version is shorter. If your agent has to answer an auditor, or price a contract as of a date, or reason about two overlapping states, buy the graph and budget for the reranker. If it has to stop telling a customer something that changed last week, you are buying a ranking feature at the price of a graph database, and the money is better spent on the extraction quality that decides whether the right fact was ever stored.
Need the transaction axis without the graph? Try Synap.
Storage time, event time and a lossless supersession chain, with a scope ladder enforced at write time. No validity windows — that is Graphiti.
Frequently asked questions
It depends which half of the temporal model you need. If you need recency-aware ranking and a change history, Mem0, Maximem Synap, SuperMemory and Hindsight all provide it without a graph database, and Mem0's own published category scores show that half improving from a ranking change alone. If you need to ask what was true in the world on a past date, or to intersect two validity windows, no product in this comparison other than Graphiti and Zep models it, and the nearest alternatives are Cognee's single `valid_to` field with real caveats or a validity-window schema on PostgreSQL 18. Cognee also ships a Graphiti-backed temporal mode, so it is the one product that can give you Graphiti's model inside a different surface.
No. Graphiti is an Apache 2.0 Python library that builds the graph and requires you to supply the graph database, the model provider, the embedder and the reranker, and to build your own users, threads, message storage, tooling and retrieval tuning. Zep Cloud runs Graphiti inside a managed system with its own proprietary graph engine, its own extraction and reranking models, governance, a dashboard and SDKs in three languages. Zep's own line is that Graphiti builds the graph and Zep operates it at scale, and the practical consequence is that Zep's published accuracy and latency figures do not transfer to a self-hosted Graphiti deployment.
Three ways, with different ceilings. Zep Cloud gives you the full model as a service and operates the graph for you. Cognee runs on embedded defaults that need no separate graph database, though its single-PostgreSQL mode is a demo feature in its own words rather than a production one, and it has a single `valid_to` field, with no paired `valid_from`, persisted on one backend, and a read path that by Cognee's own documentation neither filters nor down-weights closed nodes. Or you implement it yourself: PostgreSQL 18 shipped `WITHOUT OVERLAPS` primary keys and `PERIOD` foreign keys in September 2025, so the schema is now stock SQL, and what remains is extraction, entity resolution, the invalidation decision and ranking, which is the actual product.
Not as an API. Its `SearchFilters` object exposes date-comparison primitives on all four validity timestamps and you assemble the as-of predicate yourself, roughly as valid-at on or before your timestamp and invalid-at after it or null. There is no `as_of` parameter, and the official searching documentation does not mention date filtering. A widely mirrored third-party page shows a `valid_time` parameter on `SearchFilters`; that parameter does not exist in the source.
Three bills. Ingestion is a floor of four model calls per episode plus one to two per extracted edge, so an episode producing five edges is roughly fourteen calls, and it also issues two hybrid graph searches per edge, so your graph database carries write load too. Retrieval defaults to a cross-encoder that is one model call per candidate passage, which is the largest read-path lever and is swappable for a local reranker that ships in the box. And the graph database itself starts at $65 per gigabyte per month on managed Neo4j with a one-gigabyte minimum, or around $73 on FalkorDB Cloud, with Neptune additionally requiring an OpenSearch Serverless collection.
Against no memory layer, yes and by a lot: Zep's paper reports its temporal-reasoning category rising from 36.5 to 54.1 percent against a full-context gpt-4o-mini baseline, judged by GPT-4o, with no per-category question count published. Against other memory layers, the published evidence is weaker than the framing suggests. Zep's own current breakdown puts its temporal reasoning at 90.2 percent, exactly its overall average and behind three of its six categories, while Mem0 reports that category at 97.0 percent with no validity windows in its data model. Cognee, Hindsight, Letta and Maximem publish no temporal sub-score at all. None of these figures shares an answer model, judge or run date, so treat the comparison as unresolved rather than settled in either direction.
Sources: every repository, source file, vendor page, paper and release note cited inline was retrieved on 12 September 2026. Graphiti field names, defaults and call structure were read from its source rather than from documentation where the two differ, and both readings are given where they disagree. Maximem product statements come from Synap's published documentation. Corrections are welcome and will be applied with the previous value left visible.