Search
Hybrid memory retrieval via recall.memories.search — query, filters, response shape, and tradeoffs against context.build().
search()
def search(
self,
query: str,
limit: int = 10,
*,
memory_type: Optional[str] = None,
min_confidence: Optional[float] = None,
) -> Any: ...search is the primary read API. Internally it runs Recall's seven-stage
read pipeline — query rewrite, parallel retrievers (semantic + BM25 +
entity graph + temporal), Reciprocal Rank Fusion (RRF) at k=60, type
filter, deduplication, and final ranking — and returns the top limit
results.
Parameters
querystrrequiredNatural-language search string. The server's query-rewrite stage normalises and (optionally) expands this before retrieval.
limitintoptionalMaximum number of memories to return. The server clamps to
[1, 100]. Renamed from top_k in v0.3.0; the wire field on
POST /v1/search is also now limit. The server still accepts the
legacy top_k and topK aliases for unmigrated clients.
memory_typestroptionalFilter by memory category. One of 'fact', 'preference', 'event',
'entity', 'relation'. Sent as filters.memory_type on the wire.
min_confidencefloatoptionalDrop results whose confidence is below this threshold. Range
[0.0, 1.0]. Sent as filters.min_confidence. Useful when you only
want high-certainty memories influencing the agent's response.
Wire-shape
The Python SDK preserves snake_case field names on the wire:
{
"query": "UI preferences",
"limit": 5,
"filters": {
"memory_type": "preference",
"min_confidence": 0.6
}
}The filters block is omitted entirely when neither memory_type nor
min_confidence is provided.
Response shape
{
"results": [
{
"id": "m_01HEX…",
"content": "User prefers dark mode.",
"memory_type": "preference",
"confidence": 0.92,
"score": 0.81,
"tags": ["ui"],
"created_at": "2026-04-30T12:34:56Z",
…
}
],
"trace_id": "trace_…"
}Each hit carries the canonical Memory shape (see Types)
plus a score field — the RRF-fused relevance score in [0, 1]. The
trace_id ties the read to a span in the server's tracing pipeline.
Examples
from recall import Recall
recall = Recall(url='https://api.recall.arc-labs.ai', api_key='rcall_…')
hits = recall.memories.search(
query='What does the user think about the UI?',
limit=5,
memory_type='preference',
min_confidence=0.7,
)
for hit in hits['results']:
print(f"{hit['confidence']:.2f} {hit['content']}")import asyncio
from recall import AsyncRecall
async def main():
async with AsyncRecall(
url='https://api.recall.arc-labs.ai',
api_key='rcall_…',
) as recall:
hits = await recall.memories.search(
query='What does the user think about the UI?',
limit=5,
memory_type='preference',
min_confidence=0.7,
)
for hit in hits['results']:
print(f"{hit['confidence']:.2f} {hit['content']}")
asyncio.run(main())Pagination
Search returns a relevance-ranked list — there is no stable cursor for
the next page. If you need to walk every memory matching a filter, use
recall.memories.list(...) with cursor= instead.
In practice, when an agent needs more results, increase limit rather
than paginating. The retrieval pipeline is optimised to surface the
single most relevant page in one shot; deeper "page 2" queries on a
relevance-ranked corpus rarely produce useful results.
Streaming variant
For real-time UI integration (showing pipeline stages, intermediate reranking, etc.) use the streaming variant on the pipelines resource:
for event in recall.pipelines.search_stream(query='UI preferences', limit=5):
print(event.event, event.data)See Streaming for the full event taxonomy. The
non-streaming search() returns the same final result set in one shot —
prefer it unless you need the intermediate stage events.
When to use search vs context.build
search() returns raw memory rows. context.build() returns a
prompt-ready string assembled from the top-N memories under a token
budget. Use search() when:
- You're building a custom prompt assembly (your own template, your own ordering).
- You want to display memories as evidence ("here's what we know about the user") rather than feed them to an LLM.
- You need filter-based queries (only
eventmemories from last week).
Use context.build() when:
- You just need to inject memory into an LLM prompt and want Recall to pick the right shape and length.
- You want token-budget enforcement.
- You want recipe-driven formatting (
'summary','bullet_points').
Type filter semantics
The five memory types — fact, preference, event, entity,
relation — partition the memory space. Filtering by type is exact match
(no prefix or wildcard). To search across multiple types, run the call
twice with different filters and merge client-side, or omit the filter to
search across all types.
The server's RRF fusion treats types uniformly during retrieval — a
high-scoring relation will outrank a low-scoring fact in the unfiltered
case. The type filter is applied after retrieval, so it doesn't change
which candidates are surfaced; it only narrows the returned set.
Confidence threshold
min_confidence is a hard cutoff. The server drops every memory whose
confidence field is below the threshold before returning. Reasonable
defaults:
0.5— exclude only the very low-confidence noise.0.7— keep only memories the extractor was reasonably sure about.0.9— only show ground-truth-grade memories. Recall's grounding guard caps confidence at 0.9 for LLM-extracted memories without explicit evidence spans, so this threshold effectively requires a grounded memory.
Errors
RecallAuthError401/403fatalRecallValidationError422fatalInvalid memory_type value, min_confidence outside [0,1], etc.
RecallRateLimitError429fatalRecallServerError5xxfatalMigration: top_k → limit
Pre-0.3.0 SDK calls used top_k. Update to limit:
hits = recall.memories.search(query='…', top_k=5)
# After (0.3.0+)
hits = recall.memories.search(query='…', limit=5)The server still accepts top_k and topK for unmigrated clients, so
old SDK code keeps working through the deprecation window. The Python
SDK's search() no longer accepts top_k= as a kwarg — passing it
will silently land in **_options and be ignored. Switch to limit.
Was this page helpful?