Visualization payloads
visualize_graph() renders a self-contained HTML page. The
functions on this page return the same data as plain dictionaries instead, so an external UI
or dashboard can render it itself. Both paths run through the same authorized, bounded graph
read and the same preprocess() step, so they cannot drift on the data — only on how each
packages it.
These builders are not re-exported on the top-level
cognee module. Import them from
cognee.api.v1.visualize.visualize_graph_json()
visualize_graph(), so the same arguments
give you exactly the subgraph the HTML page would have shown — including the
bounded-subgraph defaults.
Parameters
bool
default:"True"
Embed the caller’s search and improve events in the payload as
search_events, scoped to dataset. Sessions attributed to no dataset are not included.list
default:"None"
Restrict embedded session events to these sessions. An explicit list is an intentional override, used as given and not narrowed to
dataset.Optional[User]
default:"None"
User context for dataset access. Falls back to the default user.
Optional[Union[str, UUID]]
default:"'main_dataset'"
Dataset name or id to read.
bool
default:"False"
Return the entire graph instead of a bounded subgraph.
Optional[str]
default:"None"
Query string whose nearest vector hits seed the subgraph.
Optional[List[str]]
default:"None"
Explicit seed node ids for neighborhood expansion.
Optional[Any]
default:"None"
A
recall() or search result whose graph provenance seeds the subgraph. Python-only — there is no HTTP equivalent.int
default:"2"
k-hop expansion depth around the seeds.
int
default:"10"
Maximum number of seed nodes.
int
default:"500"
Hard cap on nodes after expansion.
Returns
dict — every field of the renderer’s PreprocessedGraph snapshot, plus search_events:
Semantic positions are deliberately absent — see below.
visualize_semantic_json()
visualize_graph_json() to lay out the same
subgraph.
This is the one call that fetches embeddings and runs the PCA (or UMAP) projection over them,
bounded to SEMANTIC_NODE_CAP = 2000 nodes. The HTML render computes the layout on every
render; splitting it out here means a client that never opens a semantic view never pays for
it.
Returns
dict with semantic_positions and semantic_clusters. Both are null together — the
layout is best-effort, so no embeddings resolving or the projection failing yields
{"semantic_positions": null, "semantic_clusters": null} rather than an error.
build_brains_payload()
dataset argument: this is the
overview across brains, not one brain.
Returns
dict keyed by dataset id, each value {"name", "nodes", "links", "node_set_colors"} — not
the full visualize_graph_json() shape, since an overview does not need one dataset’s worth
of schema/memory/pipeline detail multiplied by every dataset. max_nodes is applied
independently per dataset; there is no larger combined cap.
build_brains_summary_payload()
build_brains_payload(): the same datasets — the authorization
union is shared, so both list exactly the same brains — but only what an overview shows
(name, sources, size, colors), built from relational metadata and the per-cognify-run count
cache instead of one bounded graph read per dataset. A cold cache pays one count query per
cognify run whose count is not cached yet; every call after that pays no graph reads at all.
Reach for build_brains_payload() only when the node and link arrays themselves are needed.
Returns
dict keyed by dataset id (as a string), each value
{"name", "source_names", "node_count", "node_set_colors"}:
get_live_events()
dataset_id both gates and scopes. It gates with the same read-permission check every other
visualization entry point runs, and it scopes the events: only the caller’s own sessions
attributed to that dataset contribute, matching the search_events already embedded in
visualize_graph_json() for the same dataset. A session attributed to no dataset — the plain
global default_session an unscoped or cross-dataset search runs in — contributes to no
dataset’s timeline, since it could belong to any dataset the caller has queried. Attribution
is per session, not per answered turn: a session id reused across datasets stays with the
first dataset it touched, so its later turns appear on that dataset’s timeline. Raises
PermissionDeniedError when the dataset does not exist or the caller cannot read it.
Returns
{"events": [...], "cursor": <ISO datetime string or None>}. Omit since on the first call
to get everything available, then pass the previous response’s cursor straight back as the
next since — the filter is strict (>, not >=), so no event is delivered twice. When
nothing new has happened the response echoes back the since you sent, or null if you sent
none.
stream_dataset_updates()
WS /api/v1/visualize/subscribe/{dataset_id} — sends a ready frame,
then pushes live_events, graph_grew and heartbeat frames until the client disconnects,
so a client can stop polling get_live_events() and refetching the graph payload entirely.
See Live dataset updates
for the frame shapes, cadences, and close codes.
Exposed for deployments that mount their own WebSocket route. It expects an already
accepted, already authorized connection — the caller owns accept and close, so a rejection
can carry a close code the client will actually see — and raises PermissionDeniedError when
read access to the dataset is lost while the stream is running (permission is re-checked on
every poll).
get_memory_provenance_payload()
get_memory_provenance_graph():
in multi-tenant deployments you must pass a scope, or the read spans every tenant.
Returns
dict in the same shape visualize_graph_json() returns (nodes, links, color_maps,
schema_graph, memory_map, and the rest) — not the raw (nodes, edges) tuple
get_memory_provenance_graph() returns.
Over HTTP
Each builder above has an HTTP endpoint in front of it.GET /api/v1/visualize/json,
GET /api/v1/visualize/semantic and GET /api/v1/schema/provenance/json are JSON siblings of
existing HTML endpoints; /brains, /brains-summary and /live-events have no HTML
equivalent. POST /api/v1/visualize/multi remains HTML-only, and stream_dataset_updates()
fronts a WebSocket route rather than a GET.
All of them require authentication. The routes that take a
dataset_id enforce the same read
permission as GET /api/v1/visualize; /brains and /brains-summary take none and simply
return the datasets the caller can read. The WebSocket route also accepts the API key or
bearer token as a ?token= query parameter, since a browser cannot set headers on a
WebSocket handshake — mind that the handshake URL, query string included, shows up in the
default access logs of common reverse proxies.
/json and /semantic accept the same query params as GET /api/v1/visualize —
dataset_id (required), full, query, seed_node_ids, neighborhood_depth,
neighborhood_seed_top_k and max_nodes — with the defaults listed above. Pass identical
arguments to both to get a graph and its semantic layout for the same subgraph.
Errors
JSON error bodies carry a fixed message and never the exception text — full detail is server-logged instead.- 403 —
/live-eventsonly, when the caller lacks read permission on the dataset (or it does not exist). - 409 — the payload could not be built. Note that a semantic layout that cannot be
computed is not an error:
/semanticanswers 200 with both fieldsnull, and only returns 409 if the underlying graph fetch itself fails.
See also
- Graph Visualization — rendering the same data to an interactive HTML file
- Reading the Visualization — what each tab of that file shows, and which one to reach for
- Memory Provenance — the ownership and data-flow projection behind
/schema/provenance - Schema Inventory — a per-type summary of the graph, also available over HTTP