Find why your productivity system keeps failing, then apply the smallest fix — capacity math, bottleneck routing, durable local notes.
Data & analysis
notion-sync
Try itBi-directional markdown and Notion sync with database queries, property updates, and change monitoring from the command line.
What it does
Push local markdown to Notion pages and pull edits back, with batched uploads, rate limiting, and state-based change detection. Search and query workspaces, run filtered database queries, and update properties such as select, multi_select, checkbox, date, url, and number across single or multiple pages. All scripts run on Node.js v18+, read the Notion integration token from a token file, stdin, or env var, and emit machine-readable JSON with --json.
When to use it
- Syncing draft markdown to a Notion page and pulling later edits back
- Querying and filtering Notion databases from the command line
- Monitoring a Notion page for changes via cron
- Batch-updating one property across many pages by filter or ID list
The skill document
Notion Sync
Bi-directional sync between markdown files and Notion pages, plus database management utilities for research tracking and project management.
Upgrading
From v2.0: Replace --token "ntn_..." with --token-file, --token-stdin, or NOTION_API_KEY env var. Bare --token is no longer accepted (credentials should never appear in process listings).
From v1.x: See v2.0 changelog for migration details.
Requirements
- Node.js v18 or later
- A Notion integration token (starts with
ntn_orsecret_)
Setup
-
Create a new integration (or use an existing one)
-
Copy the "Internal Integration Token"
-
Pass the token using one of these methods (priority order used by scripts):
Option A — Token file (recommended):
echo "ntn_your_token" > ~/.notion-token && chmod 600 ~/.notion-token node scripts/search-notion.js "query" --token-file ~/.notion-tokenOption B — Stdin pipe:
echo "$NOTION_API_KEY" | node scripts/search-notion.js "query" --token-stdinOption C — Environment variable:
export NOTION_API_KEY="ntn_your_token" node scripts/search-notion.js "query"Auto default: If
~/.notion-tokenexists, scripts use it automatically even without--token-file. -
Share your Notion pages/databases with the integration:
- Open the page/database in Notion
- Click "Share" → "Invite"
- Select your integration
JSON Output Mode
All scripts support a global --json flag.
- Suppresses progress logs written to stderr
- Keeps stdout machine-readable for automation
- Errors are emitted as JSON:
{ "error": "..." }
Example:
node scripts/query-database.js --limit 5 --json
Path Safety Mode
Scripts that read/write local files are restricted to the current working directory by default.
- Prevents accidental reads/writes outside the intended workspace
- Applies to:
md-to-notion.js,add-to-database.js,notion-to-md.js,watch-notion.js - Override intentionally with
--allow-unsafe-paths
Examples:
# Default (safe): path must be inside current workspace
node scripts/md-to-notion.js docs/draft.md "Draft"
# Intentional override (outside workspace)
node scripts/notion-to-md.js ~/Downloads/export.md --allow-unsafe-paths
Core Operations
1. Search Pages and Databases
Search across your Notion workspace by title or content.
node scripts/search-notion.js "" [--filter page|database] [--limit 10] [--json]
Examples:
# Search for newsletter-related pages
node scripts/search-notion.js "newsletter"
# Find only databases
node scripts/search-notion.js "research" --filter database
# Limit results
node scripts/search-notion.js "AI" --limit 5
Output:
[
{
"id": "page-id-here",
"object": "page",
"title": "Newsletter Draft",
"url": "https://notion.so/...",
"lastEdited": "2026-02-01T09:00:00.000Z"
}
]
2. Query Databases with Filters
Query database contents with advanced filters and sorting.
node scripts/query-database.js [--filter ] [--sort ] [--limit 10] [--json]
Examples:
# Get all items
node scripts/query-database.js xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Filter by Status = "Complete"
node scripts/query-database.js \
--filter '{"property": "Status", "select": {"equals": "Complete"}}'
# Filter by Tags containing "AI"
node scripts/query-database.js \
--filter '{"property": "Tags", "multi_select": {"contains": "AI"}}'
# Sort by Date descending
node scripts/query-database.js \
--sort '[{"property": "Date", "direction": "descending"}]'
# Combine filter + sort
node scripts/query-database.js \
--filter '{"property": "Status", "select": {"equals": "Complete"}}' \
--sort '[{"property": "Date", "direction": "descending"}]'
Common filter patterns:
- Select equals:
{"property": "Status", "select": {"equals": "Done"}} - Multi-select contains:
{"property": "Tags", "multi_select": {"contains": "AI"}} - Date after:
{"property": "Date", "date": {"after": "2024-01-01"}} - Checkbox is true:
{"property": "Published", "checkbox": {"equals": true}} - Number greater than:
{"property": "Count", "number": {"greater_than": 100}}
3. Update Page Properties
Update properties for database pages (status, tags, dates, etc.).
node scripts/update-page-properties.js [--type ] [--json]
Supported types: select, multi_select, checkbox, number, url, email, date, rich_text
Examples:
# Set status
node scripts/update-page-properties.js Status "Complete" --type select
# Add multiple tags
node scripts/update-page-properties.js Tags "AI,Leadership,Research" --type multi_select
# Set checkbox
node scripts/update-page-properties.js Published true --type checkbox
# Set date
node scripts/update-page-properties.js "Publish Date" "2024-02-01" --type date
# Set URL
node scripts/update-page-properties.js "Source URL" "https://example.com" --type url
# Set number
node scripts/update-page-properties.js "Word Count" 1200 --type number
4. Batch Update
Batch update a single property across multiple pages in one command.
Mode 1 — Query + Update:
node scripts/batch-update.js --filter '' [--type select] [--dry-run] [--limit 100]
Example:
node scripts/batch-update.js Status Review \
--filter '{"property":"Status","select":{"equals":"Draft"}}' \
--type select
Mode 2 — Page IDs from stdin:
echo "page-id-1\npage-id-2\npage-id-3" | \
node scripts/batch-update.js --stdin [--type select] [--dry-run]
Features:
--dry-run: prints pages that would be updated (with current property value) without writing--limit: max pages to process (default100)- Pagination in query mode (
has_more/next_cursor) up to limit - Rate-limit friendly updates (300ms between page updates)
- Progress and summary on stderr, JSON result array on stdout
5. Markdown → Notion Sync
Push markdown content to Notion with full formatting support.
node scripts/md-to-notion.js \
"" \
"" \
"" [--json] [--allow-unsafe-paths]
Example:
node scripts/md-to-notion.js \
"projects/newsletter-draft.md" \
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
"Newsletter Draft - Feb 2026"
Supported formatting:
- Headings (H1-H3)
- Bold/italic text
- Links
- Bullet lists
- Code blocks with syntax highlighting
- Horizontal dividers
- Paragraphs
Features:
- Batched uploads (100 blocks per request)
- Automatic rate limiting (350ms between batches)
- Rich text is automatically chunked to Notion's 2000-character limit (including bold/italic/link spans)
- Returns Notion page URL and ID
Output:
Parsed 294 blocks from markdown
✓ Created page: https://www.notion.so/[title-and-id]
✓ Appended 100 blocks (100-200)
✓ Appended 94 blocks (200-294)
✅ Successfully created Notion page!
6. Notion → Markdown Sync
Pull Notion page content and convert to markdown.
node scripts/notion-to-md.js [output-file] [--json] [--allow-unsafe-paths]
Example:
node scripts/notion-to-md.js \
"abc123-example-page-id-456def" \
"newsletter-updated.md"
Features:
- Converts Notion blocks to markdown
- Preserves formatting (headings, lists, code, quotes)
- Optional file output (writes to file or stdout)
7. Change Detection & Monitoring
Monitor Notion pages for edits and compare with local markdown files.
node scripts/watch-notion.js "" "" [--state-file ] [--json] [--allow-unsafe-paths]
Example:
node scripts/watch-notion.js \
"abc123-example-page-id-456def" \
"projects/newsletter-draft.md"
State tracking: By default maintains state in memory/notion-watch-state.json (relative to current working directory). You can override with --state-file (supports ~ expansion):
node scripts/watch-notion.js "" "" --state-file ~/.cache/notion-watch-state.json
Default state schema:
{
"pages": {
"": {
"lastEditedTime": "2026-01-30T08:57:00.000Z",
"lastChecked": "2026-01-31T19:41:54.000Z",
"title": "Your Page Title"
}
}
}
Output:
{
"pageId": "",
"title": "Your Page Title",
"lastEditedTime": "2026-01-30T08:57:00.000Z",
"hasChanges": false,
"localPath": "/path/to/your-draft.md",
"actions": ["✓ No changes since last check"]
}
Automated monitoring: Schedule periodic checks using cron, CI pipelines, or any task scheduler:
# Example: cron job every 2 hours during work hours
0 9-21/2 * * * cd /path/to/workspace && node scripts/watch-notion.js "" ""
The script outputs JSON — pipe it to any notification system when hasChanges is true.
8. Database Management
Add Markdown Content to Database
Add a markdown file as a new page in any Notion database.
node scripts/add-to-database.js "" [--json] [--allow-unsafe-paths]
Examples:
# Add research output
node scripts/add-to-database.js \
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
"Research Report - Feb 2026" \
projects/research-insights.md
# Add project notes
node scripts/add-to-database.js \
\
"Sprint Retrospective" \
docs/retro-2026-02.md
# Add meeting notes
node scripts/add-to-database.js \
\
"Weekly Team Sync" \
notes/sync-2026-02-06.md
Features:
- Creates database page with title property
- Converts markdown to Notion blocks (headings, paragraphs, dividers)
- Handles large files with batched uploads
- Returns page URL for immediate access
Note: Additional properties (Type, Tags, Status, etc.) must be set manually in Notion UI after creation.
Inspect Database Schema
node scripts/get-database-schema.js [--json]
Example output:
{
"object": "database",
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"title": [{"plain_text": "Ax Resources"}],
"properties": {
"Name": {"type": "title"},
"Type": {"type": "select"},
"Tags": {"type": "multi_select"}
}
}
Use when:
- Setting up new database integrations
- Debugging property names/types
- Understanding database structure
Archive Pages
node scripts/delete-notion-page.js [--json]
Note: This archives the page (sets archived: true), not permanent deletion.
Common Workflows
Collaborative Editing Workflow
-
Push local draft to Notion:
node scripts/md-to-notion.js draft.md "Draft Title" -
User edits in Notion (anywhere, any device)
-
Monitor for changes:
node scripts/watch-notion.js # Returns hasChanges: true when edited -
Pull updates back:
node scripts/notion-to-md.js draft-updated.md -
Repeat as needed (update same page, don't create v2/v3/etc.)
Research Output Tracking
-
Generate research locally (e.g., via sub-agent)
-
Sync to Notion database:
node scripts/add-research-to-db.js -
User adds metadata in Notion UI (Type, Tags, Status properties)
-
Access from anywhere via Notion web/mobile
Page ID Extraction
From Notion URL: https://notion.so/Page-Title-abc123-example-page-id-456def
Extract: abc123-example-page-id-456def (last part after title)
Or use the 32-char format: abc123examplepageid456def (hyphens optional)
Limitations
- Property updates: Database properties (Type, Tags, Status) must be added manually in Notion UI after page creation. API property updates can be temperamental with inline databases.
- Block limits: Very large markdown files (>1000 blocks) may take several minutes to sync due to rate limiting.
- Formatting: Some complex markdown (tables, nested lists >3 levels) may not convert perfectly.
Troubleshooting
"Could not find page" error:
- Ensure page/database is shared with your integration
- Check page ID format (32 chars, alphanumeric + hyphens)
"Module not found" error:
- Scripts use built-in Node.js https module (no npm install needed)
- Ensure running from the skill's directory (where scripts/ lives)
Rate limiting:
- Notion API has rate limits (~3 requests/second)
- Scripts handle this automatically with 350ms delays between batches
Resources
scripts/
Core Sync:
- md-to-notion.js - Markdown → Notion sync with full formatting
- notion-to-md.js - Notion → Markdown conversion
- watch-notion.js - Change detection and monitoring
Search & Query:
- search-notion.js - Search pages and databases by query
- query-database.js - Query databases with filters and sorting
- update-page-properties.js - Update database page properties
- batch-update.js - Batch update one property across many pages (query or stdin IDs)
Database Management:
- add-to-database.js - Add markdown files as database pages
- get-database-schema.js - Inspect database structure
- delete-notion-page.js - Archive pages
Utilities:
- notion-utils.js - Shared utilities (error handling, property formatting, API requests)
All scripts use only built-in Node.js modules (https, fs) - no external dependencies required.
references/
- database-patterns.md - Common database schemas and property patterns
Questions people ask
- What credentials do I need and how are they passed?
- A Notion internal integration token (starts with ntn_ or secret_). The scripts read it from a token file (default ~/.notion-token), a stdin pipe, or the NOTION_API_KEY environment variable — bare --token flags are no longer accepted to keep secrets out of process listings.
- Does it require anything beyond Node.js?
- Node.js v18 or later. The target pages and databases also have to be shared with the integration from Notion's Share menu, otherwise search, query, and update calls will fail with "Could not find page" errors.
- What are the known limits?
- Markdown files over roughly 1000 blocks sync slowly because of Notion's API rate limits (350ms between batches), and tables or lists nested deeper than three levels may not convert perfectly. Properties other than the title must be created manually in the Notion UI before the update script can set them.
Related skills
Join a video meeting as an AI bot with voice, avatar, and screenshare across four operating modes.
Post videos, photos, text, and documents to 10 social platforms through a single REST API call.
Stores durable facts in a categorized, plain-markdown vault on disk, alongside your agent's built-in memory.
Read and write Excel workbooks, worksheets, ranges, tables, and charts in OneDrive through Microsoft Graph with managed OAuth.
Query and manage Linear issues, projects, teams, cycles, labels, and comments through a managed OAuth GraphQL endpoint.
More from robansuini
Browse all skillsCurated collection of 25+ battle-tested prompts for engineering leaders — 1-on-1 prep, team health, incident retros, technical strategy, hiring, career devel...