Entities resource
List, fetch, traverse, and merge entities in the Recall knowledge graph from Python.
Recall extracts named entities from every conversation it ingests and
stores them as typed graph nodes (one of person, organisation,
product, place, concept). Memories of type relation connect those
entities into a queryable graph. The entities resource is the read and
maintenance surface for that graph.
list()
def list(self, limit: int = 20, offset: int = 0) -> Any: ...Page through the entities visible in the caller's scope.
limitintoptionaloffsetintoptionalpage = recall.entities.list(limit=50)
for ent in page['entities']:
print(ent['name'], ent['kind'])page = await recall.entities.list(limit=50)
for ent in page['entities']:
print(ent['name'], ent['kind'])The response is a paginated list — the exact wrapper shape (entities,
cursor, total) depends on the server version. Cursor-style pagination
is recommended for large namespaces.
When to use. Building a left-rail entity browser, exporting the graph, running maintenance scripts that touch every entity.
get()
def get(self, entity_id: str) -> Any: ...Fetch a single entity by ID. Returns the canonical entity row — id,
name, kind, aliases, scope, created_at, updated_at. For the
full neighborhood (relations, evidence memories) use get_graph.
ent = recall.entities.get('e_01HEX…')
print(ent['name'], ent['aliases'])ent = await recall.entities.get('e_01HEX…')
print(ent['name'], ent['aliases'])Raises RecallNotFoundError if the entity does not exist or the caller
is out of scope.
get_graph()
def get_graph(self, entity_id: str) -> Any: ...Return the entity along with all incoming and outgoing relations and the memories that evidence each relation. This is the primary read surface for graph traversal.
graph = recall.entities.get_graph('e_arc_labs')
print(graph['entity']['name'])
for edge in graph['edges']:
print(edge['predicate'], '->', edge['target']['name'])graph = await recall.entities.get_graph('e_arc_labs')
print(graph['entity']['name'])
for edge in graph['edges']:
print(edge['predicate'], '->', edge['target']['name'])The response carries:
entity— the focal entity row.edges— array of relations withsource,target,predicate,confidence,weight, andevidence_memory_ids.nodes— entities reachable in one hop (the focal entity plus its immediate neighbors).
When to use. Building a graph view of a single entity ("everything we know about X"), feeding the graph into an LLM for entity-aware reasoning, debugging dedupe/merge decisions.
merge()
def merge(
self,
source_id: str,
target_id: str,
*,
idempotency_key: Optional[str] = None,
) -> Any: ...Merge two entities — the source entity is folded into the target. Aliases and incoming/outgoing relations transfer; the source row is soft-deleted.
source_idstrrequiredEntity to be merged away. Will be soft-deleted.
target_idstrrequiredCanonical entity that absorbs the source's aliases and edges.
idempotency_keystroptionalOverride the auto-generated UUID for replay safety.
# Two entities for the same company — merge "Arc" into "Arc Labs".
recall.entities.merge(
source_id='e_arc',
target_id='e_arc_labs',
)await recall.entities.merge(
source_id='e_arc',
target_id='e_arc_labs',
)When to use. When dedupe missed a duplicate and you've manually
verified the two entities refer to the same real-world thing. Merge is
asymmetric — pick the canonical one as target_id.
Merge is destructive in the sense that the source ID becomes unreachable through the entity graph going forward. The row remains for audit purposes but new memories written against the source name resolve to the target. Test merges in a non-production namespace first.
Errors
RecallAuthError401/403fatalRecallNotFoundError404fatalEntity ID does not exist or out of scope. On merge, raised when
either source or target is missing.
RecallValidationError422fatalsource_id == target_id, or the entities have incompatible kinds.
RecallRateLimitError429fatalEntity types
The kind field is a closed enum:
personstrorganisationstrproductstrplacestrconceptstrCatch-all for everything else. The extractor falls back to concept
when the LLM produces an unrecognised classification.
See Types for the EntityKind Literal alias and the
full Entity TypedDict.
Filtering by kind
list() does not currently accept a kind= filter — call list() and
filter client-side, or use recall.memories.search(query=…, memory_type='entity') for semantic-driven entity discovery.
Search via memories.search
To find entities matching a query, search memories of type entity:
hits = recall.memories.search(query='startup CTOs', memory_type='entity')
for hit in hits['results']:
# hit['subject'] is the EntityId of the matched entity
ent = recall.entities.get(hit['subject'])This is faster than scanning every entity client-side because it leverages the same retrieval pipeline used for memory search.
Was this page helpful?