Memories resource
Write, search, get, list, create, update, delete, feedback, and forget memories with full sync and async parity.
Every method below is documented once with both sync and async variants in
tabs. The kwargs, return shapes, and error classes are identical between
the two paths — only the await keyword differs.
write()
def write(
self,
messages: list[dict[str, str]],
**options: Any,
) -> Any: ...Send conversation turns through the seven-stage write pipeline (extract, classify, ground, dedupe, embed, persist, index) and receive the IDs of the typed memories that landed.
Parameters
messageslist[dict[str, str]]requiredA list of {role, content} dicts. The SDK rewrites each into a typed
Turn ({id, role, content}) by auto-generating id as
'turn-{index}'. Roles must be 'user' or 'assistant'.
idempotency_keystroptionalOptional UUID-style key. When supplied, the server's idempotency middleware deduplicates retries of the same logical operation. Auto- generated by the transport when omitted.
Returns
A dict with the IDs of memories created in this call. The exact shape
depends on the server version; commonly {'memoryIds': [...], 'count': int}.
Examples
result = recall.memories.write(messages=[
{'role': 'user', 'content': 'I prefer dark mode.'},
{'role': 'assistant', 'content': 'Saved.'},
])result = await recall.memories.write(messages=[
{'role': 'user', 'content': 'I prefer dark mode.'},
{'role': 'assistant', 'content': 'Saved.'},
])When to use. Whenever you have raw conversation turns and want Recall to extract typed memories from them. The pipeline does the heavy lifting: LLM-based extraction, dedupe against existing memories, and indexing.
search()
def search(
self,
query: str,
limit: int = 10,
*,
memory_type: Optional[str] = None,
min_confidence: Optional[float] = None,
) -> Any: ...Hybrid retrieval — semantic + BM25 + entity graph + temporal — fused via RRF. Returns a relevance-ranked list, not a paginated cursor.
Parameters
querystrrequiredNatural-language search string.
limitintoptionalMaximum number of results. Server clamps to [1, 100]. Renamed from
top_k in 0.3.0 — the wire field is also limit.
memory_typestroptionalFilter by type — 'fact', 'preference', 'event', 'entity', or
'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.
Examples
hits = recall.memories.search(
query='UI preferences',
limit=5,
memory_type='preference',
min_confidence=0.6,
)
for h in hits['results']:
print(h['content'], h['confidence'])hits = await recall.memories.search(
query='UI preferences',
limit=5,
memory_type='preference',
min_confidence=0.6,
)See the dedicated Search page for the full filter shape and response payload.
get()
def get(self, memory_id: str) -> Any: ...Fetch a single memory by its MemoryId.
memory = recall.memories.get('m_01HEX…')memory = await recall.memories.get('m_01HEX…')Raises RecallNotFoundError if the memory does not exist or is not in the
caller's scope.
list()
def list(
self,
memory_type: Optional[str] = None,
limit: int = 20,
offset: int = 0,
cursor: Optional[str] = None,
) -> Any: ...Browse memories with cursor or offset pagination.
memory_typestroptionallimitintoptionaloffsetintoptionalSkip the first N rows. Use cursor instead for stable pagination on
high-write namespaces.
cursorstroptionalOpaque cursor returned by the previous page's response.
page = recall.memories.list(memory_type='fact', limit=50)
while page.get('cursor'):
page = recall.memories.list(memory_type='fact', limit=50, cursor=page['cursor'])page = await recall.memories.list(memory_type='fact', limit=50)
while page.get('cursor'):
page = await recall.memories.list(memory_type='fact', limit=50, cursor=page['cursor'])create()
def create(
self,
content: str,
memory_type: str = 'fact',
tags: Optional[list[str]] = None,
confidence: Optional[float] = None,
*,
idempotency_key: Optional[str] = None,
) -> Any: ...Create a memory directly, bypassing the LLM extraction pipeline. Use sparingly — pre-extracted memories skip the dedupe and grounding guards.
contentstrrequiredmemory_typestroptionalOne of fact, preference, event, entity, relation.
tagslist[str]optionalconfidencefloatoptional[0.0, 1.0].idempotency_keystroptionalrecall.memories.create(
content='User onboarded on 2026-05-01.',
memory_type='event',
tags=['onboarding'],
confidence=1.0,
)await recall.memories.create(
content='User onboarded on 2026-05-01.',
memory_type='event',
tags=['onboarding'],
confidence=1.0,
)When to use. For deterministic facts you already have in structured
form — onboarding events, billing transitions, system-generated audit
notes. For anything you'd want the LLM to extract, use write().
update()
def update(
self,
memory_id: str,
content: Optional[str] = None,
confidence: Optional[float] = None,
add_tags: Optional[list[str]] = None,
) -> Any: ...Partially update a memory. Only provided fields are sent on the wire.
recall.memories.update(
'm_01HEX…',
confidence=0.95,
add_tags=['verified'],
)await recall.memories.update(
'm_01HEX…',
confidence=0.95,
add_tags=['verified'],
)add_tags is additive — the server merges the list into the existing
tags array.
delete()
def delete(self, memory_id: str) -> Any: ...Soft-delete a single memory. The row remains in the database with
deleted_at set and is hidden from search.
recall.memories.delete('m_01HEX…')await recall.memories.delete('m_01HEX…')For batch deletes or hard deletes, use forget(). Note: DELETE does not
carry an Idempotency-Key — REST DELETE is already idempotent at the
semantic level.
feedback()
def feedback(
self,
memory_id: str,
signal: str,
replacement: Optional[str] = None,
duplicate_of: Optional[str] = None,
reason: Optional[str] = None,
*,
idempotency_key: Optional[str] = None,
) -> Any: ...Send a correction signal about a memory. See Feedback and forget for full details.
memory_idstrrequiredsignalstrrequiredOne of 'correct', 'duplicate', 'irrelevant'.
replacementstroptionalsignal='correct'.duplicate_ofstroptionalsignal='duplicate'.reasonstroptionalrecall.memories.feedback(
memory_id='m_01HEX…',
signal='correct',
replacement='User prefers light mode.',
)await recall.memories.feedback(
memory_id='m_01HEX…',
signal='correct',
replacement='User prefers light mode.',
)forget()
def forget(
self,
ids: Optional[list[str]] = None,
hard: bool = False,
*,
idempotency_key: Optional[str] = None,
) -> Any: ...Soft-delete (or hard-delete) a batch of memories.
recall.memories.forget(ids=['m_01HEX…', 'm_01HEY…'])
recall.memories.forget(ids=['m_old1', 'm_old2'], hard=True)await recall.memories.forget(ids=['m_01HEX…', 'm_01HEY…'])
await recall.memories.forget(ids=['m_old1', 'm_old2'], hard=True)hard=True triggers retention pruning — the row is removed from the
database. Reserve hard delete for GDPR-style erasure or namespace cleanup.
Errors
Every memory method can raise the following:
RecallAuthError401/403fatalAPI key missing, expired, or revoked.
RecallNotFoundError404fatalMemory ID does not exist or is out of scope.
RecallValidationError422fatalInvalid memory type, malformed body, etc.
RecallRateLimitError429fatalRate limit exceeded. Inspect .retry_after_ms.
RecallServerError5xxfatalServer-side failure. Retried automatically up to max_retries.
Was this page helpful?