A command-line interface for Notion, designed for AI agents and power users. Built on top of the ultimate-notion Python library.
Author: Christian Chen (christianchen01@gmail.com)
- AI-Agent Friendly: All commands support
--jsonoutput for machine parsing - Complete CRUD Operations: Create, read, update, archive pages and databases
- Rich Content Support: Markdown parsing with support for headings, lists, code blocks, and todos
- Self-Documenting: Auto-generated SKILLS.md for easy agent discovery
- Authentication: Flexible token management via environment variables or config files
pip install git+https://github.com/chen-gdp/notion-cli.gitOr install with uv (recommended for development):
uv pip install git+https://github.com/chen-gdp/notion-cli.gitSet your Notion token as an environment variable:
export NOTION_TOKEN="secret_xxxxxxxxxxxxx"Or configure via the CLI:
notion auth setup
# Enter your Notion integration token when promptednotion auth status# Search for pages and databases
notion search "meeting"
# Get page details
notion get <page_id>
# Query database
notion db query <database_id>This project uses uv for fast Python package management.
curl -LsSf https://astral.sh/uv/install.sh | sh# Clone the repository
git clone https://github.com/chen-gdp/notion-cli.git
cd notion-cli
# Create virtual environment and install dependencies
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
uv pip install -e ".[dev]"
# Or use uv sync (if using uv's lock file)
uv sync --dev# Run all tests with uv
uv run pytest tests/unit/ -v
# Run with coverage
uv run pytest tests/unit/ --cov
# Run integration tests (requires NOTION_TOKEN)
NOTION_TOKEN=xxx uv run pytest tests/integration/ -v# Format code
uv run ruff format .
# Run linter
uv run ruff check .
# Type checking
uv run mypy src/notion_cli# Check authentication status
notion auth status
# Interactive setup
notion auth setup
# Remove saved token
notion auth logout# Search all content
notion search "project"
# Filter by type
notion search "tasks" --type database
notion search "notes" --type page
# Limit results
notion search "test" --limit 10
# Get by ID
notion get <page_id>
notion get <database_id># Get database schema
notion db get <database_id>
# Query entries
notion db query <database_id>
# Query with filter
notion db query <database_id> --filter "Status=Done"
# Query with limit
notion db query <database_id> --limit 50
# Insert new entry
notion db insert <database_id> --title "New Task" --properties "Status=Open,Priority=High"# Create a page
notion page create --parent <parent_id> --title "New Page"
# Create in database
notion page create --parent <database_id> --title "New Entry" --properties "Status=Open"
# Append content (supports markdown)
notion page append <page_id> --content "## Heading\n\n- Item 1\n- Item 2"
# Update page
notion page update <page_id> --title "Updated Title"
notion page update <page_id> --properties "Status=Done"
# Archive page
notion page archive <page_id>
# Unarchive page
notion page archive <page_id> --unarchive# List all available commands
notion skills list
# Show detailed help for a command
notion skills show search
notion skills show page_createThe page append command supports rich markdown:
notion page append <page_id> --content "## Meeting Notes
### Attendees
- Alice
- Bob
- Carol
### Action Items
- [ ] Review Q1 roadmap
- [ ] Schedule follow-up
- [x] Send meeting recap
### Code Example
\`\`\`python
def hello():
return 'world'
\`\`\`
For questions, contact the team."Supported Markdown:
- Headings:
#,##,### - Bullet lists:
- item - Todo lists:
- [ ] task,- [x] done - Code blocks:
```language\ncode\n``` - Paragraphs: Plain text
All commands support --json for programmatic use:
# Get structured data
notion search "project" --json
# Output format:
{
"success": true,
"data": [
{
"id": "abc123...",
"title": "Project Alpha",
"type": "page",
"url": "https://notion.so/...",
"last_edited_time": "2024-03-20T14:30:00Z"
}
],
"error": null
}
# Errors are also structured:
{
"success": false,
"data": null,
"error": {
"code": "NOT_FOUND",
"message": "Page not found",
"details": {"id": "invalid-id"}
}
}The CLI stores configuration in ~/.config/notion-cli/config.json:
{
"token": "secret_xxxxxxxxxxxxx"
}Priority order for token:
NOTION_TOKENenvironment variable~/.config/notion-cli/config.json
# Find tasks database
DB_ID=$(notion search "Tasks" --type database --json | jq -r '.data[0].id')
# Create new task
notion db insert "$DB_ID" --title "Fix bug #123" --properties "Status=Open,Priority=High"
# Query open tasks
notion db query "$DB_ID" --filter "Status=Open"
# Mark as done
TASK_ID=$(notion db query "$DB_ID" --filter "Title=Fix bug #123" --json | jq -r '.data[0].id')
notion page update "$TASK_ID" --properties "Status=Done"# Create documentation page
PAGE_ID=$(notion page create --parent <wiki_id> --title "API Guide" --json | jq -r '.data.id')
# Add structured content
notion page append "$PAGE_ID" --content "## Overview
API endpoints:
- GET /users
- POST /users
- DELETE /users/:id
\`\`\`bash
curl https://api.example.com/users
\`\`\`"# Search existing research
notion search "competitor analysis" --json
# Create new research page
PAGE_ID=$(notion page create --parent <research_db> --title "Q2 Competitor Analysis" --json | jq -r '.data.id')
# Add findings incrementally
notion page append "$PAGE_ID" --content "## Competitor A
- Strengths: Fast, reliable
- Weaknesses: Expensive"
notion page append "$PAGE_ID" --content "## Competitor B
- Strengths: Free tier
- Weaknesses: Limited features"| Variable | Description | Required |
|---|---|---|
NOTION_TOKEN |
Notion integration token | Yes (or via notion auth setup) |
# Check if token is set
notion auth status
# Verify token is valid
notion search "test" --jsonIf you hit rate limits, the CLI will return:
{
"success": false,
"error": {
"code": "RATE_LIMIT",
"message": "Too many requests. Retry after 60s."
}
}| Error Code | Cause | Solution |
|---|---|---|
AUTH_ERROR |
No token configured | Set NOTION_TOKEN or run notion auth setup |
NOT_FOUND |
Page/DB doesn't exist | Check the ID is correct |
VALIDATION_ERROR |
Invalid input | Check required parameters |
# Unit tests with uv
uv run pytest tests/unit/ -v
# Integration tests (requires NOTION_TOKEN)
NOTION_TOKEN=xxx uv run pytest tests/integration/ -vnotion-cli/
├── src/notion_cli/
│ ├── cli.py # Main entry point
│ ├── commands/ # Command implementations
│ │ ├── auth.py
│ │ ├── database.py
│ │ ├── get.py
│ │ ├── page.py
│ │ ├── search.py
│ │ └── skills.py
│ └── core/ # Core utilities
│ ├── config.py
│ ├── errors.py
│ ├── markdown.py
│ ├── output.py
│ ├── session.py
│ └── skills_registry.py
├── tests/
│ ├── unit/ # Unit tests
│ └── integration/ # Integration tests
├── pyproject.toml # UV/pip configuration
└── README.md
MIT License - See LICENSE file for details.
- Fork the repository
- Create a feature branch
- Write tests for your changes
- Submit a pull request
- Open an issue on GitHub
- Check the Notion API documentation
- Review ultimate-notion docs
Author: Christian Chen (christianchen01@gmail.com)
License: MIT
Built with: uv + ultimate-notion