---
title: Notion
icon: book
description: Mount Notion pages as a virtual filesystem.
---
The Notion resource exposes a Notion workspace as a virtual filesystem
mounted at a prefix such as `/notion/`.

For API key setup, see [Notion Setup](/python/setup/notion).

## Config

```python
import os

from mirage import MountMode, Workspace
from mirage.resource.notion import NotionConfig, NotionResource

config = NotionConfig(api_key=os.environ["NOTION_API_KEY"])
resource = NotionResource(config=config)
ws = Workspace({"/notion": resource}, mode=MountMode.WRITE)
```

| Field      | Required | Default                        | Description             |
| ---------- | -------- | ------------------------------ | ----------------------- |
| `api_key`  | yes      |                                | Notion integration token |
| `base_url` | no       | `https://api.notion.com/v1`    | API base URL            |

## Filesystem Layout

```text
/notion/
  pages/
    <page-title>__<page-id>/
      page.json
      <child-page-title>__<child-id>/
        page.json
        ...
  databases/
    <database-title>__<database-id>/
      database.json
      <row-page-title>__<page-id>/
        page.json
        ...
```

Example:

```text
/notion/
  pages/
    Project-Roadmap__a1b2c3d4/
      page.json
      Q1-Goals__e5f6g7h8/
        page.json
      Q2-Goals__i9j0k1l2/
        page.json
    Meeting-Notes__m3n4o5p6/
      page.json
  databases/
    Tasks__4a3b21915e77/
      database.json
      Write-proposal__62212c5affe6/
        page.json
      Build-dashboards__f988c5a145ef/
        page.json
    Roadmap__2c4d6a7f5bf3/
      database.json
```

The `pages/` hierarchy mirrors Notion's standalone page tree. The
`databases/` hierarchy lists databases shared with the integration and
then the row pages returned by querying each database. Each database
directory contains `database.json` with the database metadata and its
typed property schema (the columns), but not the rows themselves; the
rows are the row-page directories alongside it, so `ls` the database
directory to enumerate them. Each page directory contains a `page.json`
file with the page metadata and content. Child pages appear as nested
directories.

`page.json` carries the page metadata (`page_id`, `title`, `url`,
timestamps, `parent_type`/`parent_id`, `created_by`/`last_edited_by`),
a `markdown` field with the page body rendered as Markdown, and the raw
`blocks`. Blocks with children embed them recursively under a
`children` key, and the Markdown renders nested blocks with
indentation.

`database.json` carries the database metadata (`database_id`, `title`,
`url`, timestamps, `parent`, `archived`, `is_inline`) and `properties`,
the database's typed column schema. It does not embed the rows; read a
row's `page.json` for its content, since a database row is itself a page
with `parent_type` of `database_id`.

### database.json

The database's identity plus its typed column schema (Notion's own
property objects), with no rows inline. `ls` the database directory to
enumerate row pages:

```json
{
  "database_id": "2c4d6a7f-5bf3-8036-bed2-d1c95826f76b",
  "title": "Item",
  "url": "https://www.notion.so/2c4d6a7f5bf38036bed2d1c95826f76b",
  "created_time": "2025-12-09T23:36:00.000Z",
  "last_edited_time": "2026-04-15T12:30:00.000Z",
  "parent": { "type": "workspace", "workspace": true },
  "archived": false,
  "is_inline": false,
  "properties": {
    "Name": { "id": "title", "type": "title", "title": {} },
    "Number": { "id": "e%5BNQ", "type": "number", "number": { "format": "number" } },
    "Date": { "id": "%3BgQq", "type": "date", "date": {} }
  }
}
```

To create a row, `notion-page-create` with a `database_id` parent and a
`properties` object whose keys match this schema.

### page.json

A page (including a database row). Notion blocks render to `markdown`
and stay available raw under `blocks`:

```json
{
  "page_id": "2c4d6a7f-5bf3-8022-826b-ed4700254ed2",
  "title": "item_0",
  "url": "https://www.notion.so/item_0-2c4d6a7f5bf38022826bed4700254ed2",
  "created_time": "2025-12-09T23:36:00.000Z",
  "last_edited_time": "2025-12-09T23:44:00.000Z",
  "parent_type": "database_id",
  "parent_id": "2c4d6a7f-5bf3-8036-bed2-d1c95826f76b",
  "archived": false,
  "created_by": "faa6ebe0-0686-466d-96b3-2480ddda715b",
  "last_edited_by": "faa6ebe0-0686-466d-96b3-2480ddda715b",
  "markdown": "",
  "blocks": []
}
```

## Cache

Uses `IndexCacheStore` for page metadata. No separate content
cache - file content caching is handled by the workspace `IOResult`
mechanism.

## Example

```python
import asyncio
import os

from dotenv import load_dotenv

from mirage import MountMode, Workspace
from mirage.resource.notion import NotionConfig, NotionResource

load_dotenv(".env.development")

config = NotionConfig(api_key=os.environ["NOTION_API_KEY"])
resource = NotionResource(config=config)


async def main():
    ws = Workspace({"/notion": resource}, mode=MountMode.WRITE)

    # List top-level pages
    r = await ws.execute("ls /notion/pages/")
    print(await r.stdout_str())

    # List shared databases and rows in a database
    r = await ws.execute("ls /notion/databases/")
    print(await r.stdout_str())

    # Read a page
    r = await ws.execute(
        'cat "/notion/pages/Project-Roadmap__a1b2c3d4/page.json"'
    )
    print(await r.stdout_str())

    # Search across all pages
    r = await ws.execute('grep "deadline" /notion/pages/')
    print(await r.stdout_str())

    # Tree view
    r = await ws.execute("tree -L 2 /notion/")
    print(await r.stdout_str())

    # Search pages with the Notion search API
    r = await ws.execute('notion-search --query "Roadmap" --limit 5')
    print(await r.stdout_str())

    # Create a new page
    r = await ws.execute(
        'notion-page-create --json \'{"parent":{"page_id":"a1b2c3d4"},'
        '"properties":{"title":[{"text":{"content":"New Page"}}]}}\''
    )
    print(await r.stdout_str())

    # Append content to an existing page
    r = await ws.execute(
        'notion-block-append --params \'{"block_id":"a1b2c3d4"}\''
        ' --json \'{"children":[{"type":"paragraph","paragraph":'
        '{"rich_text":[{"text":{"content":"Appended paragraph"}}]}}]}\''
    )
    print(await r.stdout_str())


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

## Shell Commands

Standard commands available on the mounted Notion tree:

| Command         | Notes                                      |
| --------------- | ------------------------------------------ |
| `ls`            | List pages and child pages                 |
| `cat`           | Read page.json content                     |
| `head` / `tail` | First/last N lines                         |
| `grep` / `rg`   | Search across pages                        |
| `jq`            | Query page JSON fields                     |
| `wc`            | Line/word/byte counts                      |
| `stat`          | File metadata                              |
| `find`          | Recursive search with `-name`, `-maxdepth` |
| `tree`          | Directory tree view                        |

Resource-specific commands:

### `notion-page-create`

Create a new page under a parent page. Prints the new page normalized as JSON.

| Option   | Required | Description                                                            |
| -------- | -------- | ---------------------------------------------------------------------- |
| `--json` | yes      | Page body with `parent` (required) and `properties`, per the Notion API |

In the TypeScript SDK this command takes `--parent <parent-path>` and
`--title "title"` instead of a raw `--json` body.

### `notion-block-append`

Append content blocks to an existing page. Prints the refreshed page as JSON.

| Option     | Required | Description                                       |
| ---------- | -------- | ------------------------------------------------- |
| `--params` | yes      | JSON object with the target `block_id` (page ID)  |
| `--json`   | yes      | JSON object with the `children` blocks to append  |

### `notion-comment-add`

Add a comment to a page. Prints the created comment as JSON.

| Option   | Required | Description                                          |
| -------- | -------- | ---------------------------------------------------- |
| `--json` | yes      | Comment body with `parent` (required) and `rich_text` |

### `notion-search`

Search pages with the Notion search API.

| Option    | Required | Description                  |
| --------- | -------- | ---------------------------- |
| `--query` | yes      | Search query                 |
| `--limit` | no       | Max results (default 20)     |
