Install any skill in seconds. Free to start, no credit card required.
Get Started Free →This skill provides comprehensive instructions for interacting with the Notion API via REST calls. This skill should be used whenever the user asks to interact with Notion, including reading, creating, updating, or deleting pages, databases, blocks, comments, or any other Notion content. The skill covers authentication, all available endpoints, pagination, error handling, and best practices.
.claude/skills/mkurman-notion-api/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 424% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 450% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 323% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 33% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 165% | 0% |
|-------| | Maximum block elements per payload | 1000 | | Maximum payload size | 500KB | | Rich text content | 2000 characters | | URLs | 2000 characters | | Equations | 1000 characters | | Email addresses | 200 characters | | Phone numbers | 200 characters | | Multi-select options | 100 items | | Relations | 100 related pages | | People mentions | 100 users | | Block arrays per request | 100 elements |
IMPORTANT: Before executing any operation that modifies or deletes data, ask the user for confirmation. This includes:
For a logical group of related operations, a single confirmation is sufficient.
Search across all accessible pages and databases:
bashcurl -s -X POST "https://api.notion.com/v1/search" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "query": "search term", "filter": {"property": "object", "value": "page"}, "sort": {"direction": "descending", "timestamp": "last_edited_time"}, "page_size": 100 }' | jq
Filter values: "page" or "data_source" (or omit for both)
bashcurl -s "https://api.notion.com/v1/pages/{page_id}" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" | jq
Note: This returns page properties, not content. For content, use "Retrieve block children" with the page ID.
bashcurl -s -X POST "https://api.notion.com/v1/pages" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "parent": {"page_id": "parent-page-id"}, "properties": { "title": { "title": [{"text": {"content": "Page Title"}}] } }, "children": [ { "object": "block", "type": "paragraph", "paragraph": { "rich_text": [{"type": "text", "text": {"content": "Paragraph content"}}] } } ] }' | jq
Parent options:
{"page_id": "..."} - Create under a page{"database_id": "..."} - Create in a database (legacy){"data_source_id": "..."} - Create in a data source (API v2025-09-03+)bashcurl -s -X PATCH "https://api.notion.com/v1/pages/{page_id}" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "properties": { "title": {"title": [{"text": {"content": "Updated Title"}}]} }, "icon": {"type": "emoji", "emoji": "📝"}, "archived": false }' | jq
Additional update options: cover, is_locked, in_trash
bashcurl -s -X PATCH "https://api.notion.com/v1/pages/{page_id}" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{"archived": true}' | jq
For properties with more than 25 references:
bashcurl -s "https://api.notion.com/v1/pages/{page_id}/properties/{property_id}" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" | jq
bashcurl -s "https://api.notion.com/v1/blocks/{block_id}/children?page_size=100" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" | jq
Use the page ID as block_id to get page content. Check has_children on each block for nested content.
bashcurl -s -X PATCH "https://api.notion.com/v1/blocks/{block_id}/children" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "children": [ { "object": "block", "type": "heading_2", "heading_2": { "rich_text": [{"type": "text", "text": {"content": "New Section"}}] } }, { "object": "block", "type": "paragraph", "paragraph": { "rich_text": [{"type": "text", "text": {"content": "Content here"}}] } } ] }' | jq
Maximum 100 blocks per request, up to 2 levels of nesting.
Position options in request body:
"position": {"type": "start"} - Insert at beginning"position": {"type": "after_block", "after_block": {"id": "block-id"}} - Insert after specific blockbashcurl -s "https://api.notion.com/v1/blocks/{block_id}" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" | jq
bashcurl -s -X PATCH "https://api.notion.com/v1/blocks/{block_id}" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "paragraph": { "rich_text": [{"type": "text", "text": {"content": "Updated content"}}] } }' | jq
The update replaces the entire value for the specified field.
bashcurl -s -X DELETE "https://api.notion.com/v1/blocks/{block_id}" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" | jq
Moves block to trash (can be restored).
bashcurl -s "https://api.notion.com/v1/databases/{database_id}" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" | jq
Returns database structure including data sources and properties.
bashcurl -s -X POST "https://api.notion.com/v1/databases/{database_id}/query" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "filter": { "property": "Status", "select": {"equals": "Done"} }, "sorts": [ {"property": "Created", "direction": "descending"} ], "page_size": 100 }' | jq
See references/filters-and-sorts.md for comprehensive filter and sort documentation.
bashcurl -s -X POST "https://api.notion.com/v1/databases" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "parent": {"page_id": "parent-page-id"}, "title": [{"type": "text", "text": {"content": "My Database"}}], "is_inline": true, "initial_data_source": { "properties": { "Name": {"title": {}}, "Status": { "select": { "options": [ {"name": "To Do", "color": "red"}, {"name": "In Progress", "color": "yellow"}, {"name": "Done", "color": "green"} ] } }, "Due Date": {"date": {}} } } }' | jq
bashcurl -s -X PATCH "https://api.notion.com/v1/databases/{database_id}" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "title": [{"text": {"content": "Updated Title"}}], "description": [{"text": {"content": "Database description"}}] }' | jq
Data sources are individual tables within a database. As of API version 2025-09-03, databases can contain multiple data sources.
bashcurl -s -X POST "https://api.notion.com/v1/data_sources" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "parent": {"type": "database_id", "database_id": "database-id"}, "title": [{"type": "text", "text": {"content": "New Data Source"}}], "properties": { "Name": {"title": {}}, "Description": {"rich_text": {}} } }' | jq
bashcurl -s "https://api.notion.com/v1/users?page_size=100" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" | jq
bashcurl -s "https://api.notion.com/v1/users/{user_id}" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" | jq
bashcurl -s "https://api.notion.com/v1/users/me" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" | jq
bashcurl -s "https://api.notion.com/v1/comments?block_id={block_id}&page_size=100" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" | jq
Use a page ID as block_id for page-level comments.
On a page:
bashcurl -s -X POST "https://api.notion.com/v1/comments" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "parent": {"page_id": "page-id"}, "rich_text": [{"type": "text", "text": {"content": "Comment content"}}] }' | jq
Reply to a discussion:
bashcurl -s -X POST "https://api.notion.com/v1/comments" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json" \ -d '{ "discussion_id": "discussion-id", "rich_text": [{"type": "text", "text": {"content": "Reply content"}}] }' | jq
Note: The API cannot start new inline discussion threads or edit/delete existing comments.
Paginated endpoints return:
has_more: Boolean indicating more results existnext_cursor: Cursor for the next pageresults: Array of itemsTo iterate through all results:
start_cursor)has_more in the responsetrue, extract next_cursor and include it as start_cursor in the next requesthas_more is falseExample request with cursor:
json{ "page_size": 100, "start_cursor": "v1%7C..." }
| HTTP Status | Code | Description | |-------------|------|-------------| | 400 | invalid_json | Request body is not valid JSON | | 400 | invalid_request_url | URL is malformed | | 400 | invalid_request | Request is not supported | | 400 | validation_error | Request body doesn't match expected schema | | 400 | missing_version | Missing Notion-Version header | | 401 | unauthorized | Invalid bearer token | | 403 | restricted_resource | Token lacks permission | | 404 | object_not_found | Resource doesn't exist or not shared with integration | | 409 | conflict_error | Data collision during transaction | | 429 | rate_limited | Rate limit exceeded (check Retry-After header) | | 500 | internal_server_error | Unexpected server error | | 503 | service_unavailable | Notion unavailable or 60s timeout exceeded | | 503 | database_connection_unavailable | Database unresponsive | | 504 | gateway_timeout | Request timeout |
has_more: Always handle pagination for list endpointsFor detailed documentation on specific topics, see:
references/block-types.md - All supported block types and their structuresreferences/property-types.md - Database property types and value formatsreferences/filters-and-sorts.md - Database query filter and sort syntaxreferences/rich-text.md - Rich text object structure and annotations| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 4,382 | 3,030 | -31% | 1 | 1 | 0% | 901 | 4,721 | +424% | 0 | 0 | — |
case-02 | fail→pass | 4,933 | 5,523 | +12% | 1 | 1 | 0% | 952 | 5,239 | +450% | 0 | 0 | — |
case-03 | fail→pass | 6,174 | 5,002 | -19% | 1 | 1 | 0% | 1,207 | 5,105 | +323% | 0 | 0 | — |
case-04 | fail→pass | 22,003 | 5,332 | -76% | 1 | 1 | 0% | 3,903 | 5,178 | +33% | 0 | 0 | — |
case-05 | fail→pass | 10,272 | 4,816 | -53% | 1 | 1 | 0% | 1,944 | 5,145 | +165% | 0 | 0 | — |
case-06 | pass→pass | 10,620 | 2,816 | -73% | 1 | 1 | 0% | 1,792 | 4,608 | +157% | 0 | 0 | — |
case-07 | pass→pass | 8,318 | 5,982 | -28% | 1 | 1 | 0% | 1,401 | 5,175 | +269% | 0 | 0 | — |
case-08 | fail→pass | 7,800 | 4,705 | -40% | 1 | 1 | 0% | 1,322 | 4,939 | +274% | 0 | 0 | — |
case-09 | pass→pass | 8,820 | 4,267 | -52% | 1 | 1 | 0% | 1,576 | 4,868 | +209% | 0 | 0 | — |
case-10 | pass→pass | 6,864 | 4,740 | -31% | 1 | 1 | 0% | 1,228 | 5,041 | +311% | 0 | 0 | — |
case-11 | fail→pass | 13,911 | 15,057 | +8% | 1 | 1 | 0% | 2,399 | 7,146 | +198% | 0 | 0 | — |
case-12 | fail→pass | 8,943 | 4,364 | -51% | 1 | 1 | 0% | 1,789 | 4,951 | +177% | 0 | 0 | — |
case-13 | pass→pass | 4,079 | 3,233 | -21% | 1 | 1 | 0% | 648 | 4,700 | +625% | 0 | 0 | — |
case-14 | pass→pass | 3,711 | 2,832 | -24% | 1 | 1 | 0% | 572 | 4,649 | +713% | 0 | 0 | — |
case-15 | pass→pass | 2,835 | 1,983 | -30% | 1 | 1 | 0% | 410 | 4,398 | +973% | 0 | 0 | — |
case-16 | pass→pass | 6,883 | 5,795 | -16% | 1 | 1 | 0% | 1,419 | 5,259 | +271% | 0 | 0 | — |
case-17 | fail→pass | 9,762 | 1,586 | -84% | 1 | 1 | 0% | 1,452 | 4,306 | +197% | 0 | 0 | — |
case-18 | pass→pass | 9,270 | 5,280 | -43% | 1 | 1 | 0% | 1,665 | 5,055 | +204% | 0 | 0 | — |
case-19 | pass→pass | 8,862 | 4,378 | -51% | 1 | 1 | 0% | 1,671 | 4,907 | +194% | 0 | 0 | — |
case-20 | pass→pass | 6,568 | 2,001 | -70% | 1 | 1 | 0% | 1,167 | 4,406 | +278% | 0 | 0 | — |
case-21 | pass→pass | 10,041 | 6,863 | -32% | 1 | 1 | 0% | 1,730 | 5,222 | +202% | 0 | 0 | — |
case-22 | pass→pass | 6,326 | 6,545 | +3% | 1 | 1 | 0% | 1,108 | 5,173 | +367% | 0 | 0 | — |
case-23 | fail→pass | 15,615 | 10,100 | -35% | 1 | 1 | 0% | 2,670 | 5,772 | +116% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +43 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.