--- 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.json __/ page.json ... databases/ __/ database.json __/ 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 ` 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) |