@andrzejchm/notion-cli
v0.13.0
Published
Read Notion pages and databases from the terminal. Built for AI agents and developers.
Maintainers
Readme
notion-cli
Read and write Notion pages from your terminal — built for AI coding agents and developers.
# Homebrew (recommended)
brew tap andrzejchm/notion-cli
brew install notion-cli
# npm (alternative)
npm install -g @andrzejchm/notion-cli
notion auth login # choose your auth methodFor AI Agents
Fetch and follow instructions from https://raw.githubusercontent.com/andrzejchm/notion-cli/main/docs/README.agents.md
Features
notion search— find any page or database by titlenotion read— render any page as full-fidelity markdownnotion db query— filter, sort, and select columns from any databasenotion append— append markdown content to an existing pagenotion create-page— create a new page with markdown body, prints URL to stdoutnotion comment— add a comment to a page, block, or discussion thread- Agent-friendly — plain text tables by default;
--jsonfor machine-readable output - Flexible auth — interactive setup or
NOTION_API_TOKENenv var - Accepts URLs — pass full Notion URLs anywhere an ID is expected
Examples
# Search for a page, then read it
PAGE_ID=$(notion search "Q1 Planning" | jq -r '.[0].id')
notion read "$PAGE_ID"
# Query a database with filters
notion db query "$DB_ID" --filter "Status=In Progress" --sort "Priority:asc"
# Get JSON output for scripting / AI agents
notion db query "$DB_ID" --filter "Status=Done" | jq '.[] | .properties.Title'
# Append markdown blocks to an existing page
notion append "$PAGE_ID" -m "## Summary\nGenerated by AI agent."
# Create a new page and capture its URL
URL=$(notion create-page --parent "$PAGE_ID" --title "Meeting Notes" -m "# Agenda\n- Item 1")
echo "Created: $URL"
# Pipe content to a new page
my-summarize-command | notion create-page --parent "$PAGE_ID" --title "Auto Summary"
# Add a comment to a page
notion comment "$PAGE_ID" -m "Reviewed and approved."
# Reply to an existing discussion thread
notion comment --reply-to "$DISCUSSION_ID" -m "Agreed, let's proceed."
# Comment on a specific block
notion comment --block "$BLOCK_ID" -m "This section needs revision."
# List everything your integration can access
notion lsCommands
| Command | Description |
|---------|-------------|
| notion auth login | Interactive auth setup — choose OAuth or integration token |
| notion auth logout | Remove a profile and all its credentials |
| notion auth status | Show current auth state |
| notion auth list | List all saved profiles |
| notion auth use <name> | Switch the active profile |
| notion search <query> | Search pages and databases by title (--sort asc\|desc) |
| notion ls | List all accessible pages and databases (--sort asc\|desc) |
| notion open <id\|url> | Open a page in your browser |
| notion read <id\|url> | Read a page as markdown |
| notion db create --parent <id\|url> --title <title> | Create a new database with property definitions |
| notion db update [options] <id\|url> | Update database schema (add/remove/rename properties, manage options) |
| notion db schema <id\|url> | Show database property schema and valid values |
| notion db query <id\|url> | Query database entries with filtering and sorting |
| notion db update-rows [options] <id\|url> | Batch update properties on filtered database rows |
| notion users | List workspace members |
| notion comments <id\|url> | Read page comments |
| notion comment [id\|url] -m <text> | Add a comment to a page, block, or thread |
| notion append <id\|url> -m <markdown> | Append markdown blocks to a page |
| notion attach <id\|url> <file> [files...] | Upload and attach file(s) to a page |
| notion edit-page <id\|url> --find <old> --replace <new> | Search-and-replace text on a page |
| notion edit-page <id\|url> -m <markdown> | Replace entire page content |
| notion create-page --parent <id\|url> --title <title> | Create a new page, prints URL |
| notion update <id\|url> --prop "Name=Value" | Update properties on a page |
| notion archive <id\|url> | Archive (trash) a page or database |
| notion delete-block <id\|url> | Delete a block by ID or URL |
| notion move <ids\|urls...> --to <id\|url> | Move pages to a new parent page |
| notion move <ids\|urls...> --to-db <id\|url> | Move pages to a database parent |
| notion completion bash\|zsh\|fish | Install shell tab completion |
notion attach flags
| Flag | Example | Description |
|------|---------|-------------|
| --caption <text> | --caption "My screenshot" | Caption for the file block(s) |
| --type <type> | --type image | Override auto-detected block type (image\|file\|pdf\|audio\|video) |
| --json | --json | Output JSON response |
notion append / notion create-page — --file flag
Both commands accept a repeatable --file <path> option to attach local files after the markdown content is written:
# Append markdown and attach a file
notion append "$PAGE_ID" -m "See attached screenshot:" --file screenshot.png
# Create a page with an attached PDF
notion create-page --parent "$PAGE_ID" --title "Report" --file report.pdf
# Attach multiple files
notion append "$PAGE_ID" --file image.png --file data.csvnotion search / notion ls flags
| Flag | Example | Description |
|------|---------|-------------|
| --sort | --sort desc | Sort by last edited time (asc or desc) |
| --type | --type page | Filter by object type (page or database) |
| --cursor | --cursor <cursor> | Pagination cursor from a previous --next hint |
| --json | --json | Force JSON output |
notion db create flags
| Flag | Example | Description |
|------|---------|-------------|
| --parent | --parent <id\|url> | Parent page ID or URL (required) |
| --title | --title "Tasks" | Database title (required) |
| --prop | --prop "Status:select:To Do,Done" | Property definition (repeatable) |
| --json | --json | Output full JSON response |
Property syntax: Name:type[:options]. Supported types: title, rich_text, number, select, multi_select, status, date, checkbox, url, email, phone_number, people, files, created_time, last_edited_time. If no title property is defined, Name:title is added automatically.
notion db query flags
| Flag | Example | Description |
|------|---------|-------------|
| --filter | --filter "Status=Done" | Filter by property value (repeatable) |
| --sort | --sort "Created:desc" | Sort by property (:asc or :desc) |
| --columns | --columns "Title,Status" | Only show specific columns |
| --json | --json | Force JSON output |
notion db update flags
| Flag | Example | Description |
|------|---------|-------------|
| --add-prop <definition> | --add-prop "Priority:number" | Add a new property (repeatable) |
| --remove-prop <name> | --remove-prop "Old Column" | Remove a property (repeatable) |
| --rename-prop <old:new> | --rename-prop "Status:Project Status" | Rename a property (repeatable) |
| --set-options <prop:opts> | --set-options "Priority:High,Medium,Low" | Replace all select/multi_select options |
| --title <title> | --title "New DB Name" | Update database title |
| --json | --json | Output full JSON response |
Property syntax for --add-prop: same as notion db create --prop — Name:type[:options].
notion db update-rows flags
| Flag | Example | Description |
|------|---------|-------------|
| --filter <expr> | --filter "Status=Done" | Filter rows to update (repeatable, same syntax as db query) |
| --prop <property=value> | --prop "Priority=High" | Property to update (repeatable, required) |
| --dry-run | --dry-run | Show matching rows without making changes |
| --json | --json | Output JSON array of results |
Output Modes
The CLI auto-detects your context:
| Context | Default output | Override |
|---------|----------------|----------|
| Terminal (TTY) | Formatted tables, colored | --json for raw JSON |
| Piped / agent | Plain text tables | --json for raw JSON |
notion read always outputs markdown — in terminal and when piped.
Authentication
Two authentication methods are available. If both are configured, OAuth takes precedence for API calls.
Start with the interactive setup:
notion auth login # choose OAuth or integration tokenTradeoff comparison
| Method | Best for | Write attribution | Requires |
|--------|----------|-------------------|----------|
| OAuth user login | Write-heavy workflows, personal use | Your Notion account | Browser (or --manual for headless) |
| Integration token | CI, Docker, automated agents | Integration bot | Token from notion.so/profile/integrations |
| NOTION_API_TOKEN env var | CI/Docker without config files | Integration bot | Token set in environment |
Priority: NOTION_API_TOKEN env var → OAuth token → integration token (first found wins)
OAuth user login
notion auth login # interactive selector — choose OAuth
notion auth login --manual # headless: prints URL, prompts to paste redirect
notion auth status # show current auth state
notion auth logout # remove profile and credentialsComments and pages are attributed to your actual Notion account. Access tokens expire after ~1 hour and are refreshed automatically.
Integration token
notion auth login # interactive selector — choose "Integration token"
# or: environment variable (CI, Docker, agents — no profile needed)
export NOTION_API_TOKEN=ntn_your_token_hereWorks everywhere (CI, headless, agents). Write operations are attributed to the integration bot.
You must manually connect the integration to each page (⋯ → Add connections).
Token format: starts with ntn_ (new) or secret_ (legacy integrations).
Get a token: notion.so/profile/integrations/internal
Profile management
notion auth list # list all profiles
notion auth use <name> # switch active profile
notion auth logout # remove a profile (interactive selector)
notion auth logout --profile <name> # remove specific profile directlyIntegration capabilities
Read-only commands (search, read, db query, etc.) need Read content only.
Write commands require additional capabilities — enable in your integration settings (notion.so/profile/integrations/internal → your integration → Capabilities):
| Command | Required capabilities |
|---------|----------------------|
| notion append | Read content, Insert content |
| notion attach | Read content, Insert content |
| notion create-page | Read content, Insert content |
| notion update | Read content, Update content |
| notion comment | Read content, Insert content, Read comments, Insert comments |
Troubleshooting
Page not found (404): Share the page with your integration — open the page → ⋯ → Add connections.
Unauthorized (401): Run notion auth login to reconfigure, or check your NOTION_API_TOKEN.
Search returns nothing: Search is title-only. The page must also be shared with your integration.
Empty database query: Run notion db schema <id> first to see valid property names and values.
notion comment returns "Insufficient permissions": Enable Read comments and Insert comments in your integration capabilities: notion.so/profile/integrations/internal → your integration → Capabilities.
notion append / notion create-page returns "Insufficient permissions": Enable Insert content in your integration capabilities.
Roadmap & Feature Parity
See docs/FEATURE-PARITY.md for a detailed comparison of this CLI's capabilities against the official Notion MCP server, with prioritized gaps and planned additions.
License
MIT © Andrzej Chmielewski
