> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cognee.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Graph Visualization

> Step-by-step guide to rendering interactive knowledge graphs

A minimal guide to rendering your current knowledge graph to an interactive HTML file. Use it when you want to see what your memory actually contains — a bounded, readable subgraph by default, or the whole graph on demand.

## Before You Start

* Complete [Quickstart](/getting-started/quickstart) to understand basic operations
* Ensure you have [LLM Providers](/setup-configuration/llm-providers) and [Embedding Providers](/setup-configuration/embedding-providers) configured
* Read [Core Concepts Overview](/core-concepts/overview) for how Cognee builds knowledge graphs
* No data is required up front — the script ingests its own sample passages, but it starts with `cognee.forget(everything=True)`, which wipes all existing Cognee data; run it against a setup you can afford to reset

## Code in Action

```python theme={null}
import asyncio
import os

import cognee
from cognee import visualize_graph

ARTIFACTS = os.path.join(os.path.dirname(__file__), ".artifacts", "graph_visualization")
DATASET = "graph_visualization_guide"

TEXT = [
    "Python is a programming language. Guido van Rossum created Python.",
    "Django is a web framework written in Python.",
    "NLP is a subfield of AI. spaCy is an NLP library for Python.",
]


async def main():
    os.makedirs(ARTIFACTS, exist_ok=True)

    # Prune data and system metadata before running, only if we want "fresh" state.
    await cognee.forget(everything=True)

    await cognee.remember(TEXT, dataset_name=DATASET, self_improvement=False)

    # 1. Bare call: highest-degree nodes seed a representative bounded subgraph.
    await visualize_graph(os.path.join(ARTIFACTS, "default_degree_seeded.html"), dataset=DATASET)

    # 2. Query-seeded: the query's nearest vector hits become the seeds.
    await visualize_graph(
        os.path.join(ARTIFACTS, "query_seeded.html"),
        dataset=DATASET,
        query="What is Python used for?",
    )

    # 3. Whole graph, unbounded.
    await visualize_graph(os.path.join(ARTIFACTS, "full_graph.html"), dataset=DATASET, full=True)

    # Two more seeding options, if you already have node ids or a recall result:
    #   await visualize_graph("explicit_seeds.html", dataset=DATASET, seed_node_ids=[...])
    #
    #   result = await cognee.recall("What is Python?", datasets=[DATASET])
    #   await visualize_graph("recall_seeded.html", dataset=DATASET, recall_result=result)
    # The second seeds the view from the answer's provenance (used_graph_element_ids),
    # so you see the subgraph behind a specific answer.

    print(f"Wrote visualizations to {ARTIFACTS}")


if __name__ == "__main__":
    asyncio.run(main())
```

## What Just Happened

### Step 1: Create Your Knowledge Graph

```python theme={null}
# Prune data and system metadata before running, only if we want "fresh" state.
await cognee.forget(everything=True)

await cognee.remember(TEXT, dataset_name=DATASET, self_improvement=False)
```

This starts from a clean state, then uses `remember()` to ingest the passages into the `graph_visualization_guide` dataset and build the graph in one call. Every render below scopes itself to that same dataset.

### Step 2: Render the Default Bounded Subgraph

```python theme={null}
# 1. Bare call: highest-degree nodes seed a representative bounded subgraph.
await visualize_graph(os.path.join(ARTIFACTS, "default_degree_seeded.html"), dataset=DATASET)
```

A bare call no longer renders the whole graph: it seeds on the highest-degree nodes, expands their 2-hop neighborhood, and caps the result at 500 nodes. That keeps the HTML fast and readable no matter how large the graph grows.

### Step 3: Seed the View From a Query

```python theme={null}
# 2. Query-seeded: the query's nearest vector hits become the seeds.
await visualize_graph(
    os.path.join(ARTIFACTS, "query_seeded.html"),
    dataset=DATASET,
    query="What is Python used for?",
)
```

Passing `query` seeds the subgraph from that query's nearest vector hits instead, so the render shows the neighborhood the question actually lands in.

### Step 4: Render the Whole Graph

```python theme={null}
# 3. Whole graph, unbounded.
await visualize_graph(os.path.join(ARTIFACTS, "full_graph.html"), dataset=DATASET, full=True)
```

`full=True` restores the legacy unbounded render of every node and edge. The script writes all three files side by side under `.artifacts/graph_visualization/` so you can open them and compare the seeding modes.

## What Graph Visualization Shows

* Nodes (entities, types, chunks, summaries) with color coding
* Edges with labels and weights; edge weights control line thickness, and tooltips show extra edge properties
* Interactive features: drag nodes, zoom/pan, hover edges for details
* Output is static, self-contained HTML — open it in any modern browser or share it as an artifact

## Tabs

Every rendered HTML file opens with a tab bar of four views — **Graph**, **Schema**, **Memory**, and **Semantic** — all computed from the same graph payload:

| Tab          | What it shows                                                                 |
| ------------ | ----------------------------------------------------------------------------- |
| **Graph**    | Nodes and edges laid out by structure, with layout, label, and color controls |
| **Schema**   | A by-type summary: instance counts per semantic type and how types connect    |
| **Memory**   | A deterministic map of how the memory was built, plus the run timeline        |
| **Semantic** | Nodes placed by the 2-D projection of their embeddings                        |

For what each view shows in detail — layout modes, the label budget, search, the Schema tab's inspector and operations overlay, and the theme toggle — see [Reading the Visualization](/guides/reading-the-visualization).

## Advanced Usage

<Accordion title="Output location">
  ```python theme={null}
  from cognee import visualize_graph

  # Writes HTML to your home directory by default
  await visualize_graph()

  # Writes to the provided file path (created/overwritten)
  await visualize_graph("./my_graph.html")
  ```
</Accordion>

<Accordion title="Bounded subgraph by default">
  `visualize_graph()` renders a **bounded, relevant subgraph** by default instead of the entire graph: it picks a small set of seed nodes, expands their *k*-hop neighborhood, and caps the result at `max_nodes`. This keeps renders fast and readable on large graphs. Pass `full=True` to restore the legacy whole-graph render.

  Seeds are resolved by priority — the first of these that produces nodes wins:

  1. `seed_node_ids` — explicit node ids you pass.
  2. `recall_result` — a `recall()` or search result whose graph provenance (`used_graph_element_ids`) seeds the subgraph, i.e. "show me the subgraph behind this answer".
  3. `query` — a query string whose nearest vector hits (distance-ranked, nearest first) seed the subgraph.
  4. Highest-degree nodes — the fallback so a bare `visualize_graph()` call still shows a representative view.

  If none of these resolve any seeds, an empty graph is rendered.

  The new parameters are **keyword-only**, so existing positional calls keep working unchanged:

  ```python theme={null}
  from cognee import visualize_graph

  # Default: bounded subgraph around the highest-degree nodes
  await visualize_graph("./graph.html")

  # Seed the subgraph from a query
  await visualize_graph("./graph.html", query="natural language processing")

  # Show the subgraph behind a recall answer
  result = await cognee.recall("What does Alice know?")
  await visualize_graph("./graph.html", recall_result=result)

  # Legacy whole-graph render
  await visualize_graph("./graph.html", full=True)
  ```

  | Parameter                 | Default | Description                                                              |
  | ------------------------- | ------- | ------------------------------------------------------------------------ |
  | `full`                    | `False` | Render the entire graph (legacy behavior).                               |
  | `query`                   | `None`  | Query string; its nearest vector hits seed the subgraph.                 |
  | `seed_node_ids`           | `None`  | Explicit seed node ids for neighborhood expansion.                       |
  | `recall_result`           | `None`  | A recall/search result whose `used_graph_element_ids` seed the subgraph. |
  | `neighborhood_depth`      | `2`     | *k*-hop expansion depth around the seeds (must be ≥ 1).                  |
  | `neighborhood_seed_top_k` | `10`    | Maximum number of seed nodes (must be ≥ 1).                              |
  | `max_nodes`               | `500`   | Hard cap on rendered nodes after expansion (must be ≥ 1).                |

  When the neighborhood exceeds `max_nodes`, nodes are kept by hop distance from the seeds (seeds first) and edges survive only when both endpoints do, so no dangling edges remain.

  **Over HTTP.** `GET /api/v1/visualize` exposes the same controls as query params: `full`, `query`, `seed_node_ids`, `neighborhood_depth`, `neighborhood_seed_top_k`, and `max_nodes` (`recall_result` is Python-only). For example, `GET /api/v1/visualize?dataset_id=<id>&full=true` returns the whole-graph render. [`GET /api/v1/visualize/json`](/python-api/visualize) accepts the same controls and returns the payload behind that render instead of HTML.
</Accordion>

## Troubleshooting

<Accordion title="Empty graph after cognify">
  If `visualize_graph()` logs `No nodes found in the database` (or the HTML opens empty) even though `add()` and `cognify()` ran without errors, the most common causes are:

  * **Graph path mismatch.** With the default Ladybug backend, the graph is stored on disk under `<SYSTEM_ROOT_DIRECTORY>/databases/`. By default, `SYSTEM_ROOT_DIRECTORY` is an absolute `.cognee_system` path under Cognee's package root. In notebooks like Colab, it is safer to set explicit absolute paths before running `add()`, `cognify()`, and `visualize_graph()` so every step uses the same persisted location across cells and runtime changes:

    ```python theme={null}
    import cognee

    cognee.config.system_root_directory("/content/cognee_system")
    cognee.config.data_root_directory("/content/cognee_data")
    ```

  * **`cognify()` produced no nodes.** A run can finish "successfully" yet extract nothing — for example if no data was actually ingested, or graph extraction silently returned empty results (often a misconfigured or failing LLM/embedding provider). Don't rely on the absence of an error; verify the graph was populated.

  * **Data was pruned in between.** Calling `cognee.forget(everything=True)` (or `cognee.prune`) after `cognify()` clears the graph, so a later `visualize_graph()` sees nothing.

  ### Verify the graph was populated

  Before visualizing, query the graph engine directly. `get_graph_data()` returns a `(nodes, edges)` tuple, and `get_graph_metrics()` reports the node/edge counts:

  ```python theme={null}
  from cognee.infrastructure.databases.graph import get_graph_engine

  graph_engine = await get_graph_engine()

  nodes, edges = await graph_engine.get_graph_data()
  print(f"nodes={len(nodes)}, edges={len(edges)}")

  metrics = await graph_engine.get_graph_metrics()
  print(metrics)  # {'num_nodes': ..., 'num_edges': ..., ...}
  ```

  If `len(nodes)` is `0` here, the problem is upstream in `add()`/`cognify()` (or a path mismatch), not in visualization. A non-zero count from the same process that then reports `No nodes found` points to a path/config mismatch between steps.
</Accordion>

## Related Projections

Two companion projections summarize your memory without rendering every node — both run end-to-end without an LLM:

* [Schema Inventory](/guides/schema-inventory) — `get_schema_inventory()` summarizes the graph by semantic type: per-type counts, sample names, and relationship distribution.
* [Memory Provenance](/guides/memory-provenance) — `visualize_memory_provenance()` renders the ownership and data-flow story (Tenant → User → Agent → Dataset → file) from the relational database.

## Full Examples

Additional examples about Cognee are available on our [github](https://github.com/topoteretes/cognee/tree/main/examples/guides).

<Accordion title="Semantic memory map">
  ```python theme={null}
  import asyncio
  import os

  import cognee
  from cognee.api.v1.visualize.visualize import visualize_graph

  DEST = os.path.join(os.path.expanduser("~"), "semantic_memory_map.html")

  # A few short, deliberately multi-topic passages so distinct clusters emerge:
  # computing pioneers, jazz, and ocean science.
  TEXT = """
  Ada Lovelace worked with Charles Babbage on the Analytical Engine in London.
  Alan Turing formalized computation and broke ciphers at Bletchley Park.
  Grace Hopper built the first compiler and worked on the Harvard Mark I.

  Miles Davis recorded Kind of Blue, a landmark modal jazz album, in New York.
  John Coltrane played saxophone with the Miles Davis Quintet before A Love Supreme.
  Bill Evans, the pianist on Kind of Blue, shaped its impressionistic harmony.

  Marine biologists study coral reefs, which host a quarter of all ocean species.
  Rising sea temperatures cause coral bleaching, threatening reef ecosystems.
  Phytoplankton in the ocean produce a large share of the planet's oxygen.
  """


  async def main():
      await cognee.prune.prune_data()
      await cognee.prune.prune_system(metadata=True)

      await cognee.remember(TEXT, self_improvement=False)

      html = await visualize_graph(destination_file_path=DEST)

      has_semantic = 'data-view="semantic"' in html
      has_positions = "window._semanticPositions = null" not in html
      print(f"\nSaved: {DEST}")
      print(f"Semantic tab present:   {has_semantic}")
      print(f"Semantic positions set: {has_positions}")
      print("Open the file and click the Semantic tab (or append #semantic to the URL).")


  if __name__ == "__main__":
      asyncio.run(main())
  ```
</Accordion>

<Columns cols={3}>
  <Card title="Reading the Visualization" icon="eye" href="/guides/reading-the-visualization">
    What each tab shows, and which one to reach for.
  </Card>

  <Card title="Core Concepts Overview" icon="brain" href="/core-concepts/overview">
    Understand how Cognee builds and stores knowledge graphs.
  </Card>

  <Card title="Visualization Payloads" icon="braces" href="/python-api/visualize">
    The JSON payloads and HTTP endpoints behind the render.
  </Card>
</Columns>
