Notion 2025 API Skill
This skill provides utilities for working with Notion databases and pages using the 2025-09-03 API format, which separates database_id (for creating pages) from data_source_id (for querying entries).
Quick Reference
Key Difference (2025-09-03):
database_id→ Use when creating NEW pages in a databasedata_source_id→ Use when QUERYING existing entries in a database
Common Operations
1. Query a Database
NOTION_KEY=$(cat ~/.openclaw/workspace/secrets/notion_api_key.txt)
DATA_SOURCE_ID="YOUR_DATA_SOURCE_ID"
curl -s -X POST "https://api.notion.com/v1/data_sources/$DATA_SOURCE_ID/query" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"filter": {"property": "Status", "select": {"equals": "Active"}},
"sorts": [{"property": "Last Updated", "direction": "descending"}]
}' | jq '.results[]'
2. Create a Database Entry
curl -s -X POST "https://api.notion.com/v1/pages" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"parent": {"database_id": "YOUR_DATABASE_ID"},
"properties": {
"Name": {"title": [{"text": {"content": "Entry Name"}}]},
"Status": {"select": {"name": "Active"}},
"Count": {"number": 42}
}
}' | jq '.id'
3. Update a Page
ENTRY_ID="YOUR_ENTRY_ID"
curl -s -X PATCH "https://api.notion.com/v1/pages/$ENTRY_ID" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"properties": {
"Status": {"select": {"name": "Complete"}},
"Description": {"rich_text": [{"text": {"content": "Updated content"}}]}
}
}' | jq '.properties'
4. Add Content Blocks to a Page
PAGE_ID="YOUR_PAGE_ID"
curl -s -X PATCH "https://api.notion.com/v1/blocks/$PAGE_ID/children" \
-H "Authorization: Bearer $NOTION_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"children": [
{
"object": "block",
"type": "heading_2",
"heading_2": {"rich_text": [{"text": {"content": "Section Title"}}]}
},
{
"object": "block",
"type": "paragraph",
"paragraph": {"rich_text": [{"text": {"content": "Paragraph content"}}]}
}
]
}' | jq '.object'
Property Types (2025-09-03)
When updating or creating entries, use these property formats:
| Type | Format | Example |
|---|---|---|
| Title | {"title": [{"text": {"content": "..."}}]} | {"title": [{"text": {"content": "My Title"}}]} |
| Rich Text | {"rich_text": [{"text": {"content": "..."}}]} | {"rich_text": [{"text": {"content": "Body text"}}]} |
| Select | {"select": {"name": "Option"}} | {"select": {"name": "Active"}} |
| Number | {"number": VALUE} | {"number": 42} |
| Date | {"date": {"start": "YYYY-MM-DD"}} | {"date": {"start": "2026-03-03"}} |
| Checkbox | {"checkbox": true/false} | {"checkbox": true} |
| URL | {"url": "https://..."} | {"url": "https://example.com"} |
| Status | {"status": {"name": "..."}} | {"status": {"name": "Active"}} |
Filter Syntax (for queries)
{
"filter": {
"and": [
{"property": "Status", "select": {"equals": "Active"}},
{"property": "Count", "number": {"greater_than": 5}}
]
}
}
Common filters:
{"select": {"equals": "Value"}}{"number": {"greater_than": N}}{"date": {"on_or_after": "2026-01-01"}}{"rich_text": {"contains": "keyword"}}
Bundled References
- API_REFERENCE.md — Complete endpoint documentation and property types
- EXAMPLES.md — Real-world examples and use cases
Setup
- Create integration at https://notion.so/my-integrations
- Copy API key
- Store in
~/.openclaw/workspace/secrets/notion_api_key.txt - Share target Notion pages/databases with your integration (Invite → select integration)
Security & Safety
Credentials
- API Key Storage: Stored in
~/.openclaw/workspace/secrets/notion_api_key.txt - Scope: Notion API access only (limited to Notion endpoints)
- No Elevation: This skill does not request elevated permissions
- Key Rotation: If key is compromised, revoke at https://notion.so/my-integrations
Input Validation ⚠️
IMPORTANT: The helper script uses JSON string concatenation. Only use with:
- Trusted input (your own data)
- IDs from Notion (database_id, page_id, data_source_id)
- Known property names from your Notion schema
❌ DO NOT pass untrusted user input directly to the script:
# UNSAFE - if user_input contains special chars/quotes
./notion_helper.sh create "$db_id" "$user_input"
✅ SAFE - Use proper escaping or JSON libraries:
# SAFE - properly escaped
title=$(echo "$user_input" | jq -Rs '.')
./notion_helper.sh create "$db_id" "$title"
External Dependencies
- curl — HTTP requests to Notion API
- jq — JSON parsing and formatting
- date — Date calculations (macOS/Linux)
All are standard utilities; verify they're available before use.
Important Notes
- API Version: Always use
Notion-Version: 2025-09-03 - IDs: database_id and data_source_id are different—use the correct one for your operation
- Permissions: Integration must be invited to pages/databases via Notion UI
- Rate limit: ~3 requests/second average
- Untrusted Input: Never pass unsanitized user input to JSON construction