Skip to main content

What is the recall operation

The .recall operation is the main retrieval entry point in Cognee v1.0. It searches memory using the best available source for the request.
  • Auto-routing by default: when you do not specify a search type, recall() matches the query against a short table of rules and picks a retrieval strategy automatically. The table is deliberately narrow — only three rules — so most queries land on the HYBRID_COMPLETION default.
  • Session-aware: with session_id, it can search session cache entries first and fall through to the permanent graph if needed.
  • Graph-backed by default: for permanent memory, recall() runs graph retrieval — not plain embedding similarity. HYBRID_COMPLETION is the fallback whenever no routing rule matches, which is the common case.
  • Source tagging: each recall result includes a source field ("graph", "session", "trace", "session_context", …) so you can tell where it came from — see the full source table.

Where recall fits

  • Use recall() as the default way to ask questions over memory in v1.0.
  • Use it after Remember has created either permanent or session memory.
  • Use explicit query_type only when you want to force a specific retrieval mode.
  • Use datasets to scope results to a specific knowledge base.

What happens under the hood

  1. Check session scope
    • If you pass session_id without datasets and without query_type, Cognee first searches the session cache directly.
    • Session search is keyword-based over stored question, context, and answer fields.
  2. Choose the retrieval strategy
    • If query_type is provided, Cognee uses it directly.
    • Otherwise, if no usable LLM is configured, Cognee resolves to CHUNKS — plain vector search over chunks — because nothing could write a completion answer. This applies under either auto_route setting, and is decided by LLM availability alone, never by which extractor built the graph.
    • Otherwise, if auto_route=True, a rule-based router checks the query against three ordered rules and uses HYBRID_COMPLETION when none matches. See Auto-routing behavior below for the exact rules.
    • If auto_route=False, Cognee falls back to HYBRID_COMPLETION.
    • If a rule matched and the backend rejects the routed type, recall retries once as HYBRID_COMPLETION; it also retries when CHUNKS_LEXICAL or CODING_RULES returns nothing.
  3. Run graph retrieval when needed
    • If session search finds nothing, or if graph retrieval is requested, recall() queries the permanent knowledge graph.
    • The retrieval strategy selected in step 2 determines how that graph query is executed.

After recall finishes

  • Session-only recall: you get matching session entries when cache hits exist, each tagged with source="session".
  • Graph-backed recall: you get normalized graph result objects tagged with source="graph".
  • Hybrid behavior: with session_id plus graph-scoping inputs like datasets or query_type, recall uses the permanent graph path rather than session-only lookup.

Examples and details

recall() only reads from memory — it never initializes anything itself. Populate memory first with remember() (or the legacy add() + cognify() sequence). The first ingestion run creates the relational, vector, and graph databases and the default user.
If you call recall() before any data exists, it raises RecallPreconditionError (HTTP 422), triggered by the underlying DatabaseNotCreatedError (“The database has not been created yet. Please call await setup() first.”) or UserNotFoundError. The fix is to run remember() (or add() + cognify()) first.recall() can also fail when the configured LLM provider (or LiteLLM proxy) reports that its token budget is exhausted. In that case it raises LLMPaymentRequiredError, which the API surfaces as HTTP 402 (Payment Required) with body {"detail": "LLM provider requires payment or token budget is exhausted. [LLMPaymentRequiredError]"} — or, when the provider’s own budget sentence can be identified, {"detail": "LLM budget exhausted: <provider sentence> [LLMPaymentRequiredError]"} (see 402 Payment Required). This error is terminal — Cognee does not retry budget-exhaustion failures — so treat a 402 as final for the request and prompt the user to top up their token budget rather than reissuing the call.recall() also accepts only its documented parameters — there is no catch-all **kwargs. Passing an unsupported keyword such as node_type raises a TypeError. Use node_name to scope retrieval to specific nodes or node sets; node_type belongs to the legacy search() API. See the recall() API reference for the full parameter list.
recall() can run while another dataset is still being ingested or indexed in the background.
  • There is no global lock that blocks retrieval while remember() is processing.
  • recall() only sees data that has already made it through indexing.
  • If a dataset is mid-run, results may be incomplete until that run reaches completion.
If you need to confirm a dataset is fully ready before querying it, check its status with cognee.datasets.get_status() or the indexing-status guidance on Remember.
Auto-routing is on by default, and it is rule-based, not LLM-based. recall() decides the search type in one of these ways:What the router recognizesRules are checked in order and the first match wins. The shape rule comes first, so a query that is entirely quoted is treated as what it looks like even when the text inside also reads as intent: "coding rules" — with the quotes — is a lexical search, while What are our coding rules? routes to CODING_RULES. The temporal rule comes last, so a coding-rules question that also names a date keeps its CODING_RULES route.Everything else is reachable only by passing query_type yourself. That includes GRAPH_COMPLETION_COT, GRAPH_COMPLETION_CONTEXT_EXTENSION, GRAPH_SUMMARY_COMPLETION, CYPHER, GRAPH_COMPLETION, SUMMARIES, CHUNKS, RAG_COMPLETION, TRIPLET_COMPLETION, GRAPH_COMPLETION_DECOMPOSITION, NATURAL_LANGUAGE, BROAD, and AGENTIC_COMPLETION. The router is scoped this way on purpose: it may only pick a strategy that is at least as good as HYBRID_COMPLETION on a default-built graph and that does not add LLM calls without an unambiguous signal. HYBRID_COMPLETION already searches document chunks, summaries, and the entity neighbourhood in one LLM call, so the first two rules fire only on inputs that are not natural-language questions — cases where hybrid is the wrong operation rather than a worse one. The temporal rule is the one exception: it fires on a question scoped to an absolute date, and TEMPORAL reranks hybrid’s own candidates by that date rather than shrinking the context. Other question-shaped intent (summary, reasoning, context extension) stays behind an explicit query_type. Pass query_type=SearchType.TEMPORAL yourself for time questions the rule does not catch.The quoted-phrase and coding-rules rules land on retrieval-only types, so an auto-routed call is not guaranteed to produce a generated answer — though the retry below covers the case where the routed type comes up empty. Beyond those two, FEELING_LUCKY’s LLM selector is the only thing that can reach a retrieval-only type on its own: it can pick SUMMARIES or CHUNKS, which return payloads instead of an answer, so if you want an answer every time, do not use FEELING_LUCKY. For the full answer-vs-payload breakdown per type, see SearchType and What recall returns.When a routed guess missesRecall re-runs the search as HYBRID_COMPLETION when the guess does not work out:
  • the backend rejects the routed type (UnsupportedSearchTypeError / SearchTypeNotSupported) — a deployment-level choice, not the caller’s; this applies to all three rules; or
  • a routed CHUNKS_LEXICAL or CODING_RULES returned no results at all. A routed TEMPORAL is not retried on empty: when it finds no time window or nothing dated inside it, it already answers with plain hybrid results.
The retry applies only to a type the router chose. A query_type you pinned yourself is never second-guessed — it raises or returns empty as usual — and neither is the no-LLM CHUNKS default. Each retry is logged at INFO (Rule … routed to …, which returned nothing; retrying as HYBRID_COMPLETION.), and each result’s search_type reports the type that actually answered.Confirming what the router choseThe router logs its decision at DEBUG level under the query_router logger (see Logging). The line carries the matched rule name only — never the query text:
rule=default is not an error — it is the normal line for a plain factual question, and it means recall ran HYBRID_COMPLETION. You can also read the type that actually ran off each result’s search_type field.When you pass query_type, the router does not run at all, so there is no routing line to read and no LLM cost either way.When to override query_type
  • You want raw payloads rather than a generated answer — pass CHUNKS or SUMMARIES.
  • You want a strategy the router no longer picks for you. Chain-of-thought reasoning, context extension, and summary completion all take an explicit value now: pass query_type=SearchType.GRAPH_COMPLETION_COT, GRAPH_COMPLETION_CONTEXT_EXTENSION, or GRAPH_SUMMARY_COMPLETION. Time-aware search is routed only for absolute dates; pass TEMPORAL for anything else.
  • Your query needs a multi-part breakdown that suits GRAPH_COMPLETION_DECOMPOSITION, or another type outside the router’s three rules (GRAPH_COMPLETION, RAG_COMPLETION, TRIPLET_COMPLETION, NATURAL_LANGUAGE, AGENTIC_COMPLETION, …).
  • A quoted query routed to lexical search when you wanted an answer — drop the quotes, or pass query_type=SearchType.HYBRID_COMPLETION.
  • You want predictable latency and cost across calls — pinning query_type makes every call use the same retriever. auto_route=False pins it to HYBRID_COMPLETION.
CYPHER is never auto-routed, whatever the query looks like — a query beginning with MATCH or RETURN runs as HYBRID_COMPLETION like any other text. A Cypher retriever runs the query text verbatim against the graph engine while recall only ever checks read permission, so routing on text alone would let a request body such as MATCH (n) DETACH DELETE n mutate the graph for anyone who can read it. Reaching CYPHER takes an explicit query_type=SearchType.CYPHER, which is a deliberate act by the caller. It still requires ALLOW_CYPHER_QUERY=true; otherwise it raises UnsupportedSearchTypeError (“Cypher query search types are disabled.”).
Most users should think in terms of asking memory a question, not selecting a retriever manually.Under the hood, retrieval strategies — graph completion, summary, temporal, lexical, coding-rules — do the actual work. recall() is intentionally one layer above them:
  • you call recall()
  • Cognee chooses or accepts a query_type
  • the matching retriever runs underneath
  • the result comes back through the recall API
If you want to understand or control the lower-level retrieval behavior directly, see the lower-level search reference.Each retriever exposes its stages as separate methods — get_retrieved_objects(), get_context_from_objects(), and get_completion_from_context() — and the last two take the evidence as an argument, so they can also format evidence you assembled yourself. HybridRetriever renders a dictionary keyed chunks, entities, and facts, the sections the Inspecting Hybrid Retrieval Context guide tunes; a chunk_summaries key is accepted but not rendered.The demo examples/demos/graph_completion_to_hybrid.py shows this end to end. It runs a GraphCompletionRetriever search and sorts the returned triplets into that shape: DocumentChunk nodes become chunks, Entity nodes become entities with their edges attached, and a labeled edge touching no retrieved entity becomes a fact. A TextSummary paired with its source chunk goes into chunk_summaries, so it is not rendered; an unpaired one is kept as a passage. The result goes to hybrid’s context and completion stages, so the printed context and answer match the format SearchType.HYBRID_COMPLETION produces.
  • Session-only recall returns matching session cache entries.
  • Graph-backed recall returns the output of the underlying retrieval mode selected by the router or query_type.
  • Depending on the retrieval path, each graph result object’s text can contain a plain answer, retrieved context, chunk text, or rendered structured output.
  • Each recall result is tagged with source so callers can distinguish session, graph, trace, and graph-context results.
  • only_context=True skips the final LLM answer-generation step and returns what the LLM would have received instead — the user prompt for completion types, the retrieved context for the rest.
  • verbose=True exposes extra retrieval details from the lower-level graph search path.
When recall() returns session cache hits, the result is a list of ResponseQAEntry objects tagged with source="session".These entries follow the session QA shape documented in Sessions and Caching, with fields such as:
  • time
  • qa_id
  • question
  • context
  • answer
  • feedback_text
  • feedback_score
  • source
Set only_context=True when you want what the LLM would have been sent, without asking Cognee to produce a final answer.When only_context=True, Cognee stops before the completion call:
  • the LLM answer is not generated
  • the final completion step is skipped
  • for completion search types the returned text is the user prompt — the conversation history, then the question and retrieval context rendered through the retriever’s template, then the session guidance block — and the retriever’s task template is on the item’s separate system_prompt field
  • for the non-generative types (CHUNKS, SUMMARIES, CODE, …) and the opt-outs CYPHER and AGENTIC_COMPLETION, which never send one templated prompt, text is the retrieved context as before
This is useful when you want to:
  • inspect what would have been sent before answer generation
  • hand the prompt to your own model instead of Cognee’s
  • debug retrieval quality separately from answer quality
The prompt is assembled read-only by the same code a real completion uses: no LLM completion call, no session write, and no QA entry. Because it is one artifact rather than one entry per retrieved item, recall returns a single item for it; a query that retrieved nothing returns the bare context instead, so “nothing found” stays detectable. See Inspecting the LLM input for the full shape and its limits.
session_id makes recall session-aware, but the exact behavior depends on the recall mode.Session reads:
  • With session_id and no explicit query_type, recall() searches session-cache QA entries by keyword and returns matches tagged with source="session".
  • If session_id is used with datasets or dataset_ids, recall can combine session lookup with graph retrieval.
  • If you pass an explicit query_type, the default path is graph-backed retrieval. Session history can still be used during the LLM completion step, but session-cache QA lookup is not part of the default source set unless you pass scope.
  • scope=["session_first"] asks for the session-hit short-circuit by name: it expands to session plus graph, and a session hit still skips the graph search. Use it when you want that behavior together with inputs that would otherwise disable it, such as an explicit query_type or datasets.
  • scope also reaches the two session sources that auto never picks on its own: scope=["trace"] searches recorded agent traces, and scope=["session_context"] renders the session’s accumulated guidance. The guidance read takes a context_profile — "qa" (the default) for what conversation turns taught the session, or "agent" for lessons drawn from agent tool traces — and the two profiles are separate, so a query under one never returns the other’s entries. Both reads are read-only: serving guidance stamps nothing and ages no entry. See the recall() API reference for every scope value and context_profile.
Graph-backed completion with session history:When graph-backed recall runs with session_id and the LLM completion step is enabled, Cognee loads prior session conversation history and prepends it to the completion prompt. If improve() has built the global context index and the retriever is configured with include_global_context_index, a background-knowledge prelude — the dataset’s root summary plus the top matching bucket summaries — is also prepended. That prelude comes from the index, not from a per-session snapshot.Session writes:When the LLM completion step runs through session-enabled graph retrieval, Cognee appends a new QA entry to the session cache with the question, stored context, and generated answer. The next compatible recall on the same session_id can see that entry.A turn that is gated by the session-context analysis — a feedback-only or instruction-only message, which returns a short acknowledgement rather than a generated answer (see Session-context guidance) — is appended too, with the raw message as question, the acknowledgement as answer, and an empty context. So a QA entry is written for every recall turn the session layer answers or gates — but not for a turn that skips completion entirely, which is the only_context=True case below.Using only_context=True:Pass only_context=True to skip the final LLM completion. Because the QA write happens during completion, only_context=True also avoids adding a new QA entry to the session cache.
Use only_context=True when you want the LLM input without LLM summarization and without growing the session cache.
  • Scoping is exclusive. When you pass datasets (or dataset_ids), retrieval runs against only those datasets — Cognee does not pull from any other dataset, even ones the current user can read.
  • When you omit both, recall searches across all datasets the current user has read access to. Supply a dataset to narrow that down to a single knowledge base.
  • datasets limits graph retrieval to named datasets. With backend access control enabled, names are resolved only against datasets owned by the current user.
  • dataset_ids scopes retrieval by dataset UUID instead of name. Use this for shared datasets that the current user can access but did not create. When supplied, it takes precedence over datasets and the name-to-UUID resolution step is skipped.
  • With session_id plus either datasets or dataset_ids, recall becomes hybrid: session context is available, but graph retrieval is scoped to the selected dataset or dataset UUIDs.
If you set query_type=SearchType.AGENTIC_COMPLETION, the resolved graph scope must contain exactly one dataset. Passing multiple dataset names or UUIDs, or leaving scope broad enough to match multiple readable datasets, can raise 422 InvalidAgenticDatasetScope.
If Alice created shared_dataset and Bob only has permission to use it, Bob should query it with dataset_ids=[shared_id], not datasets=["shared_dataset"]. Name-based lookup fails for non-owners even when they have read access: names are resolved strictly, so any name that matches no dataset you own — shared, mistyped, or deleted — raises DatasetNotFoundError (HTTP 404) before any retrieval runs, rather than being dropped from the scope.
recall() runs Search under the hood for graph-backed retrieval.Use legacy Search directly when you need to select a specific retriever, inspect retrieval internals, or use advanced parameters not exposed by recall(). See also Search Basics for the full set of retrieval options.
Use recall() when you want to ask a question and get an answer. It handles routing, session lookup, and source attribution automatically.Use search() directly when you need a specific retriever, lower-level parameters such as neighborhood_depth or node_type, or when building custom pipelines.

Remember

Create permanent or session memory

Improve

Enrich the graph for better future recall

Understand Recall with RAG Completion

Hands-on guide to recall() and RAG_COMPLETION