| name | using-notion-cli |
| description | Use when reading or writing Notion pages, searching a Notion workspace, querying or creating Notion databases, updating database schemas, batch-updating database rows, appending or editing page content, creating pages, updating page properties, moving pages, attaching files, adding comments, deleting blocks, or archiving pages and databases — via the `notion` CLI tool in the terminal. |
Overview - skill version 0.13.0
notion is a CLI tool for reading and writing Notion content from the terminal or agent workflows. Use it any time you need to interact with Notion: read pages, search, query databases, create and update database schemas, batch-update rows, append or edit content, create pages, update properties, move pages, post comments, attach files, delete blocks, or archive pages and databases.
Version check: Run notion --version. If your installed version is older than 0.13.0, update with npm install -g @andrzejchm/notion-cli and refresh this skill with notion skill.
Setup
Install once:
npm install -g @andrzejchm/notion-cli
notion auth login
Or set env var (preferred for CI/agents):
export NOTION_API_TOKEN=ntn_your_token_here
Get token: https://www.notion.so/profile/integrations/internal
Pages must be shared with your integration: open page → ⋯ → Add connections.
Integration capabilities (set at notion.so/profile/integrations/internal → your integration → Capabilities):
- Read-only commands: Read content only
notion append, notion append --after, notion create-page (page parent): also need Insert content
notion create-page --parent <db>: also need Insert content + database must be shared with integration
notion edit-page: also need Update content + Insert content
notion attach: also need Insert content
notion comment: also need Read comments + Insert comments
notion db update: also need Update content
notion db update-rows: also need Read content + Update content
notion delete-block: also need Update content
Authentication
Two auth methods are available. If both are configured, OAuth takes precedence.
| Method | Command | Attribution | Notes |
|---|
| Interactive setup | notion auth login | — | Guides you to choose; TTY required |
| OAuth user login | select "OAuth user login" in notion auth login | Your Notion account | Browser required; --manual for headless |
| Integration token | select "Integration token" in notion auth login | Integration bot | Works in CI/headless; must connect integration to pages |
notion auth login
notion auth login --manual
notion auth status
notion auth logout
notion auth logout --profile <name>
notion auth list
notion auth use <name>
notion --profile <name> <command>
Headless/CI agents: Use NOTION_API_TOKEN=<token> env var — overrides all config, no TTY needed.
Output Modes
| Context | Default | Override |
|---|
| Terminal (TTY) | Formatted tables | --json |
| Piped / agent | Plain text tables | --json |
notion read always outputs markdown — in terminal and when piped.
Pipe any command to get JSON:
notion search "query" | jq '.[0].id'
notion ls | jq '.[] | select(.type == "database")'
Commands
Search & Discovery
notion search "query"
notion search "query" --type page
notion search "query" --type database
notion search "query" --sort asc
notion ls
notion ls --sort desc
notion users
notion comments <id|url>
notion open <id|url>
Reading Pages
notion read <id|url>
notion read <id|url> --json
Database Operations
notion db schema <id|url>
notion db query <id|url>
notion db query <id|url> --filter "Status=Done"
notion db query <id|url> --sort "Created:desc"
notion db query <id|url> --columns "Title,Status"
notion db query <id|url> --json | jq '.[] | .properties'
notion db create --parent <page-id|url> --title "My Database"
notion db create --parent <page-id|url> --title "Tasks" \
--prop "Status:select:To Do,In Progress,Done" \
--prop "Priority:select:High,Medium,Low" \
--prop "Due:date:" \
--prop "Notes:rich_text:"
Creating Databases with Properties
Use --prop "Name:type[:options]" (repeatable) to define columns when creating a database.
notion db create --parent "$PAGE_ID" --title "Project Tracker" \
--prop "Status:select:To Do,In Progress,Done" \
--prop "Priority:select:High,Medium,Low" \
--prop "Due:date:" \
--prop "Notes:rich_text:"
notion db create --parent "$PAGE_ID" --title "Simple List"
Supported property types: title, rich_text, number, select, multi_select, status, date, checkbox, url, email, phone_number, people, files, created_time, last_edited_time.
Important: After creating a database, use notion search "DB Title" --type database to find the database ID for subsequent operations. The ID returned by db create is a URL-based ID that may differ from the API-accessible database ID.
Updating Database Schema
notion db update <id|url> --add-prop "Priority:number"
notion db update <id|url> --add-prop "Severity:select:Low,Medium,High"
notion db update <id|url> --remove-prop "Old Column"
notion db update <id|url> --rename-prop "Status:Project Status"
notion db update <id|url> --set-options "Priority:P1,P2,P3"
notion db update <id|url> --title "New Database Title"
notion db update <id|url> --add-prop "URL:url" --remove-prop "Notes"
Batch Updating Database Rows
notion db update-rows <id|url> --filter "Status=Open" --prop "Priority=P3"
notion db update-rows <id|url> --prop "Status=Closed"
notion db update-rows <id|url> --filter "Status=Done" --prop "Priority=Low" --dry-run
notion db update-rows <id|url> --filter "Category=Bug" --prop "Status=Done" --json
Write Operations
notion append <id|url> -m "## Heading\nParagraph text"
notion append <id|url> -m "$(cat notes.md)"
notion append <id|url> --file screenshot.png
notion append <id|url> -m "See results:" --file chart.png
notion append <id|url> --file a.png --file b.pdf
notion create-page --parent <page-id|url> --title "Title"
notion create-page --parent <page-id|url> --title "Title" -m "# Hello"
echo "# Content" | notion create-page --parent <page-id|url> --title "Title"
notion create-page --parent <page-id|url> --title "Report" --file report.pdf
notion create-page --parent <page-id|url> --title "Notes" -m "# Agenda" --file slides.pdf --file notes.txt
notion create-page --parent <db-id|url> --title "New Task"
notion create-page --parent <db-id|url> --title "Task" --prop "Status=To Do" --prop "Priority=High"
notion create-page --parent <db-id|url> --title "Task" --prop "Due=2026-04-01" -m "# Details"
notion create-page --parent <id|url> --title "Page" --icon "🚀"
notion create-page --parent <id|url> --title "Page" --icon ./logo.png
notion create-page --parent <id|url> --title "Page" --cover "https://example.com/img.jpg"
notion create-page --parent <id|url> --title "Page" --cover ./banner.jpg
URL=$(notion create-page --parent <id|url> --title "Summary" -m "...")
notion comment <id|url> -m "Reviewed and approved."
notion comment <id|url> -m "Reply" --reply-to <discussion-id>
notion comment <id|url> -m "Note" --block <block-id>
notion archive <id|url>
notion delete-block <id|url>
notion move <ids|urls...> --to <id|url>
notion move <ids|urls...> --to-db <id|url>
File Attachments
Upload local files to Notion and attach them as blocks (image, file, PDF, audio, video). Block type is auto-detected from file extension. Files ≤20 MB upload in one request; larger files are chunked automatically.
notion attach <id|url> screenshot.png
notion attach <id|url> report.pdf data.csv image.png
notion attach <id|url> diagram.png --caption "Architecture diagram"
notion attach <id|url> file.svg --type image
| Flag | Description |
|---|
--caption <text> | Caption for the file block(s) |
--type <type> | Override block type (image|file|pdf|audio|video) |
--json | Output JSON response |
The --file <path> flag (repeatable) is also available on notion append and notion create-page for inline file attachment alongside markdown content.
Updating Page Properties
notion update <id|url> --prop "Status=Done"
notion update <id|url> --prop "Status=Done" --prop "Priority=1"
notion update <id|url> --title "New Title"
notion update <id|url> --prop "Due=2026-04-01"
notion update <id|url> --prop "Tags=bug,urgent"
notion update <id|url> --prop "Done=true"
notion update <id|url> --prop "Status="
Supported types: title, rich_text, select, status, multi_select, number, checkbox, url, email, phone_number, date, files.
notion update <id|url> --prop "Attachments=./report.pdf"
notion update <id|url> --prop "Attachments=https://example.com/file.pdf"
notion update <id|url> --prop "Attachments=./a.pdf,./b.png"
Note: Setting a files property replaces all existing files (Notion API behavior). To keep existing files, re-include them in the value.
Surgical Editing
Search-and-replace specific text, replace the full page, or insert content at a specific location.
notion edit-page <id|url> --find "old text" --replace "new text"
notion edit-page <id|url> --find "old1" --replace "new1" --find "old2" --replace "new2"
notion edit-page <id|url> --find "TODO" --replace "DONE" --all
notion edit-page <id|url> -m "# Replacement\nNew full-page content"
cat updated-section.md | notion edit-page <id|url>
notion edit-page <id|url> -m "## Clean Slate" --allow-deleting-content
notion append <id|url> -m "New content" --after "## Status...end of status"
--range (deprecated): The --range flag still works for backward compatibility but uses the older replace_content_range API. Prefer --find/--replace for targeted edits.
ID Formats
All commands accept any of:
abc123def456789012345678901234ab (32-char hex)
abc123de-f456-7890-1234-5678901234ab (UUID)
https://www.notion.so/workspace/Page-Title-abc123 (full URL)
Agent Patterns
PAGE_ID=$(notion search "Meeting Notes" --type page | jq -r '.[0].id')
notion read "$PAGE_ID"
DB_ID=$(notion search "Project Tracker" --type database | jq -r '.[0].id')
notion db query "$DB_ID" --filter "Status=In Progress" | jq '.[] | .properties'
notion db schema "$DB_ID"
notion db query "$DB_ID" --filter "Status=Done"
notion read "$PAGE_ID" | grep -A 10 "## Action Items"
SUMMARY=$(notion read "$PAGE_ID" | your-summarize-command)
notion append "$PAGE_ID" -m "## AI Summary\n$SUMMARY"
URL=$(notion create-page --parent "$PAGE_ID" --title "Report $(date +%Y-%m-%d)" -m "# Report\n...")
echo "Created: $URL"
my-report-command | notion create-page --parent "$PAGE_ID" --title "Auto Report"
notion read "$PAGE_ID"
notion edit-page "$PAGE_ID" --find "Status: In Progress" --replace "Status: Done"
notion edit-page "$PAGE_ID" \
--find "Status: In Progress" --replace "Status: Done" \
--find "Blocked: yes" --replace "Blocked: none"
notion db create --parent "$PAGE_ID" --title "Sprint Tasks" \
--prop "Status:select:To Do,In Progress,Done" \
--prop "Priority:select:High,Medium,Low" \
--prop "Due:date:" --prop "Notes:rich_text:"
DB_ID=$(notion search "Sprint Tasks" --type database | jq -r '.[0].id')
notion db schema "$DB_ID"
notion create-page --parent "$DB_ID" --title "Fix login bug" \
--prop "Status=To Do" --prop "Priority=High" --prop "Due=2026-04-15"
notion append "$PAGE_ID" -m "## New Sub-section\nContent here" \
--after "## Existing Section...last line of section"
ARCHIVE_ID=$(notion search "Archive" --type page | jq -r '.[0].id')
notion move "$PAGE_ID" --to "$ARCHIVE_ID"
notion move page1-id page2-id --to-db "$DB_ID"
notion attach "$PAGE_ID" ./screenshot.png --caption "Current UI state"
notion create-page --parent "$PAGE_ID" --title "Build Report $(date +%Y-%m-%d)" \
-m "# Build Results\nAll tests passed." --file ./test-results.pdf --file ./coverage.png
notion append "$PAGE_ID" -m "## Data Analysis\nSee attached CSV:" --file ./export.csv
notion update "$PAGE_ID" --icon ./logo.png
notion db update "$DB_ID" --add-prop "Sprint:select:S1,S2,S3"
notion db update "$DB_ID" --set-options "Status:To Do,In Progress,Done"
notion db update-rows "$DB_ID" --filter "Status=In Progress" --prop "Status=Done"
notion db update-rows "$DB_ID" --filter "Priority=Low" --prop "Status=Archived" --dry-run
notion delete-block "$BLOCK_ID"
Troubleshooting
404 / page not found — Share the page with your integration: page → ⋯ → Add connections.
401 / unauthorized — Run notion auth login or set NOTION_API_TOKEN.
Search returns nothing — Search is title-only. Page must be shared with integration.
Empty db query — Run notion db schema <id> to see valid property names and values.
notion auth login requires TTY — Use NOTION_API_TOKEN env var in agents, or use notion auth login --manual for headless OAuth.
notion comment returns "Insufficient permissions" — Enable Read comments + Insert comments in integration capabilities: notion.so/profile/integrations/internal → your integration → Capabilities.
notion append / notion create-page returns "Insufficient permissions" — Enable Insert content in integration capabilities.
notion edit-page returns "Insufficient permissions" — Enable Update content in integration capabilities.
--find text not found — Run notion read <id> to see the exact page content. The --find value must match text on the page exactly.
--after selector not found — Run notion read <id> to see the exact page content. The selector must match real text: "start...end" with ~10 chars from the beginning and end of the target range.
notion db update fails with "Property not found" — Run notion db schema <id> to see exact property names. For --rename-prop and --set-options, the property must exist.
notion db update-rows updates 0 rows — Check that your --filter matches rows: run notion db query <id> --filter "..." first.
notion archive fails for a database — Try using the data source ID (from notion db schema <id> --json) instead of the database page ID.