Knowledge Graph API
Read entities and their relations across every document in a bucket
The Knowledge Graph API exposes the entity memory that bem builds as it
parses documents. Where the File System API's
find / open / xref ops let an agent walk entities one at a time,
the Knowledge Graph endpoints let you read the graph itself — the
entities (nodes) and the relations between them (edges) — either for a
single entity or as a slice across a whole bucket.
GET /v3/entities/{id}/relations
GET /v3/knowledge-graph
x-api-key: <your API key>Concepts
- Entity — a canonical thing bem has recognized across documents: an
organization, a person, a product, a location. Each entity has an
id(ent_…), acanonicalname, and atype. - Edge (relation) — a directed link between two entities, labeled
with a
relationTypesuch asemploysoracquired by. Every edge carries amentionCount(how many parsed mentions support it) and afirstSeenAttimestamp. - Entity type — the category of an entity (
organization,person,product,location, …). Used to filter the graph. - Bucket — a knowledge graph is scoped to a bucket (
bkt_…). There is one knowledge graph per bucket. Passbucketto read a single bucket's graph; omit it to read across all buckets in the account + environment.
These endpoints are the bulk-read counterpart to the File System memory
ops: use find / open / xref when an agent is exploring one entity
at a time, and the Knowledge Graph endpoints when you want the edges
themselves — for example to render a graph or to walk relations
programmatically.
GET /v3/entities/{id}/relations
Returns the inbound and outbound edges for a single entity.
| Param | Default | Notes |
|---|---|---|
direction | both | inbound, outbound, or both |
relationType | — | Exact-match filter on the relation label |
bucket | all buckets | bkt_…; absent → all buckets in the account + environment |
limit | 50 | Max 200 |
cursor | — | nextCursor from a previous response |
curl "https://api.bem.ai/v3/entities/ent_acme/relations?direction=both&limit=50" \
-H "x-api-key: $BEM_API_KEY"{
"inbound": [
{
"relationType": "owns",
"sourceEntity": { "id": "ent_globex", "canonical": "Globex Holdings", "type": "organization" },
"mentionCount": 3,
"firstSeenAt": "2026-05-20T12:00:00Z"
}
],
"outbound": [
{
"relationType": "employs",
"targetEntity": { "id": "ent_jane_doe", "canonical": "Jane Doe", "type": "person" },
"mentionCount": 1,
"firstSeenAt": "2026-05-21T08:30:00Z"
}
],
"nextCursor": "erl_1f3a9c"
}Inbound edges point at the entity (the far end is sourceEntity);
outbound edges point from it (the far end is targetEntity). Use
direction to fetch one side only — direction=outbound omits the
inbound array — and relationType to keep only edges with an exact
label, e.g. ?relationType=employs.
Alias resolution. If {id} is an entity that was later merged away,
the endpoint transparently resolves it to the surviving entity and
returns that entity's edges. You never get an empty result just because
you held onto an older, merged id.
GET /v3/knowledge-graph
Returns a slice of the graph — a set of nodes and the edges between them. Pagination is over edges (see below).
| Param | Default | Notes |
|---|---|---|
type | all types | Repeatable: ?type=organization&type=person |
search | — | Substring match on entity canonical name |
since | — | RFC3339; edges first seen on or after this time |
bucket | all buckets | bkt_…; absent → all buckets in the account + environment |
limit | 50 | Max 200 |
cursor | — | nextCursor from a previous response |
curl "https://api.bem.ai/v3/knowledge-graph?type=organization&type=person&search=acme&limit=50" \
-H "x-api-key: $BEM_API_KEY"{
"nodes": [
{ "id": "ent_acme", "canonical": "Acme Corporation", "type": "organization", "mentionCount": 12 }
],
"edges": [
{ "sourceId": "ent_acme", "targetId": "ent_jane_doe", "relationType": "employs", "mentionCount": 4 }
],
"nextCursor": "erl_8b2d0e"
}Filter semantics
type[] and search filter entities, not edges directly. An edge
appears in the response only when both of its endpoints survive the
filter. So ?type=organization&type=person returns organization↔person,
organization↔organization, and person↔person edges, but drops any edge
with an endpoint that is neither an organization nor a person. search
works the same way: both endpoints' canonical names are tested against
the substring.
By design in Phase 1, an entity that has no edges does not appear as a
node — the graph is built up from edges. (Use the File System API's
find op to enumerate entities regardless of whether they participate
in a relation.)
Edge pagination and node inclusion
Pagination is over edges. Each page returns up to limit edges, and the
nodes array contains both endpoints of every edge on that page —
even if the far endpoint's other edges fall on a later page. So a
single node can appear across multiple pages whenever its edges span a
page boundary; de-duplicate nodes by id if you are assembling the full
graph client-side.
Pagination patterns
Both endpoints paginate by cursor. Read the first page, then pass the
nextCursor you got back as the cursor of the next request:
# page 1
curl "https://api.bem.ai/v3/knowledge-graph?type=organization&limit=200" \
-H "x-api-key: $BEM_API_KEY"
# page 2 — feed nextCursor from page 1 back in as cursor
curl "https://api.bem.ai/v3/knowledge-graph?type=organization&limit=200&cursor=erl_8b2d0e" \
-H "x-api-key: $BEM_API_KEY"A missing or empty nextCursor means you have reached the last page.
limit defaults to 50 and is capped at 200; values above 200
are clamped.
The "find every document that mentions X" pattern — start from an
entity, walk its relations, and follow the surviving endpoints — is
already useful today via relations plus the File System API's xref.
It becomes considerably more powerful once synonym resolution lands in
Phase 4, when distinct surface forms of the same real-world thing
collapse into a single entity and its edges.