A production-grade command-line interface for the Notion API. Built with TypeScript, featuring comprehensive type safety via Zod schemas, automatic retry logic with rate limiting, and multiple output formats.
- Pages β Create, read, update, archive, and restore pages with full property support
- Databases β Create and manage databases, query with filters and sorts
- Blocks β Append, update, and delete block content (paragraphs, headings, lists, code, and more)
- Search β Full-text search across pages and databases with filtering and sorting
- Users β List workspace members and retrieve user details
- Comments β Create and list comments on pages and blocks
Technical Highlights:
- JSON-first output with table and CSV formatting options
- Automatic retry on rate limits (429) with exponential backoff
- Runtime type validation using Zod schemas
- Comprehensive error handling with actionable messages
git clone https://github.com/yourusername/notion-cli.git
cd notion-cli
npm install
npm run build
npm link # Makes 'notion' command available globallynpm run dev -- <command> # Run without building- Go to My Integrations
- Click New integration
- Give it a name and select a workspace
- Copy the Internal Integration Secret (starts with
ntn_orsecret_)
Create a .env file in the project root:
cp .env.example .envEdit .env and add your API key:
NOTION_API_KEY=ntn_your_integration_secret_here
Or pass it directly via flag:
notion search --api-key ntn_your_key_hereIn Notion, open a page or database, click β’β’β’ β Connections β Add connection β select your integration.
npm install
npm run buildThen either link it globally:
npm link # Creates global 'notion' command
notion users meOr run directly without linking:
node dist/cli.js users menotion users meAll commands support these options:
| Option | Description |
|---|---|
--api-key <key> |
Override the API key from environment |
-f, --format <fmt> |
Output format: json, table, csv (default: table) |
--help |
Show help for any command |
# Search everything
notion search "meeting notes"
# Search only pages
notion search "quarterly report" --filter page
# Search only databases
notion search "tasks" --filter database
# Sort by last edited (ascending)
notion search --sort ascending --limit 10# Get a page by ID
notion pages get <page-id>
# Create a page in a database
notion pages create --parent <database-id> --title "New Task"
# Create a page with properties (JSON)
notion pages create --parent <database-id> --title "Bug Fix" \
--properties '{"Status": {"select": {"name": "In Progress"}}}'
# Update page properties
notion pages update <page-id> --properties '{"Status": {"select": {"name": "Done"}}}'
# Set page icon
notion pages update <page-id> --icon "π"
# Archive a page
notion pages archive <page-id>
# Restore an archived page
notion pages restore <page-id># Get database schema
notion databases get <database-id>
# Create a database
notion databases create --parent <page-id> --title "Project Tasks" \
--properties '{"Status": {"select": {"options": [{"name": "Todo"}, {"name": "Done"}]}}}'
# Query a database
notion databases query <database-id>
# Query with filter
notion databases query <database-id> \
--filter '{"property": "Status", "select": {"equals": "In Progress"}}'
# Query with sort
notion databases query <database-id> \
--sort '{"property": "Created", "direction": "descending"}'# Get a block
notion blocks get <block-id>
# Get block children (page content)
notion blocks children <page-id>
# Append blocks to a page
notion blocks append <page-id> --content '[
{"type": "paragraph", "paragraph": {"rich_text": [{"text": {"content": "Hello world"}}]}},
{"type": "heading_2", "heading_2": {"rich_text": [{"text": {"content": "Section Title"}}]}}
]'
# Update a block
notion blocks update <block-id> --content '{"paragraph": {"rich_text": [{"text": {"content": "Updated text"}}]}}'
# Delete a block
notion blocks delete <block-id># Get current bot user
notion users me
# List all users
notion users list
# Get a specific user
notion users get <user-id># List comments on a block/page
notion comments list --block <block-id>
# Create a comment on a page
notion comments create --page <page-id> --content "This looks great!"
# Reply to a discussion
notion comments reply --discussion <discussion-id> --content "Thanks for the feedback"notion search "project" --format tableββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββ
β Type β ID β Title β
ββββββββββββΌβββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββ€
β page β 12345678-1234-1234-1234-123456789abc β Project Plan β
β database β 87654321-4321-4321-4321-cba987654321 β Project Tasks β
ββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββ
notion pages get <page-id> --format jsonnotion databases query <database-id> --format csv > tasks.csv| Variable | Description |
|---|---|
NOTION_API_KEY |
Your Notion integration secret |
NOTION_DEBUG |
Set to true for verbose logging |
Create .env in the project root:
# Required
NOTION_API_KEY=ntn_your_secret_here
# Optional
NOTION_DEBUG=true- Node.js 18+
- npm 9+
git clone https://github.com/yourusername/notion-cli.git
cd notion-cli
npm install| Command | Description |
|---|---|
npm run dev |
Run CLI in development mode (tsx) |
npm run build |
Compile TypeScript to JavaScript |
npm test |
Run unit tests |
npm run test:watch |
Run tests in watch mode |
npm run test:integration |
Run integration tests (requires API key) |
npm run test:coverage |
Run tests with coverage report |
npm run lint |
Check code style |
npm run lint:fix |
Fix code style issues |
npm run format |
Format code with Prettier |
npm run type-check |
Type-check without emitting |
Unit tests (mocked, no API key required):
npm testIntegration tests (requires valid NOTION_API_KEY in .env):
npm run test:integrationIntegration tests create temporary pages and databases, test operations, then clean up automatically.
notion-cli/
βββ src/
β βββ api/
β β βββ client.ts # HTTP client with retry logic
β β βββ errors.ts # Error hierarchy
β β βββ types.ts # Zod schemas (30+ types)
β β βββ endpoints/ # API endpoint modules
β βββ commands/ # CLI command handlers
β βββ formatters/ # Output formatters (JSON, table, CSV)
β βββ utils/ # Config, validation, filter builders
βββ tests/
β βββ unit/ # Mocked tests (193 tests)
β βββ integration/ # Live API tests (22 tests)
βββ docs/ # API documentation
The CLI provides clear, actionable error messages:
Error: Page not found
The requested page does not exist or your integration doesn't have access.
Suggestions:
β’ Verify the page ID is correct
β’ Ensure the page is shared with your integration
β’ Check that the page hasn't been deleted
The Notion API has a rate limit of 3 requests per second. The CLI automatically:
- Retries on 429 (rate limit) responses
- Respects the
Retry-Afterheader - Uses exponential backoff (up to 3 retries)
This CLI targets the Notion API version 2022-06-28.
Contributions are welcome! Please ensure:
- All tests pass (
npm test && npm run test:integration) - Code is formatted (
npm run format) - No linting errors (
npm run lint) - Type-check passes (
npm run type-check)