Install any skill in seconds. Free to start, no credit card required.
Get Started Free →This skill should be used when the user asks to "search Notion", "find in Notion", "search my Notion workspace", "create Notion page", "make a Notion page", "update Notion page", "edit Notion page", "query Notion database", "get Notion database", "read Notion page", "get page content from Notion", "list Notion pages", or mentions Notion integration, Notion workspace, or Notion API access.
.claude/skills/aiskillstore-notion-api/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 170% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 477% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 239% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 120% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 556% | 0% |
This skill enables interaction with Notion workspaces through the Notion REST API. Use curl and jq for direct REST calls, or write ad-hoc scripts as appropriate for the task.
NOTION_API_TOKEN is available in the environmentIMPORTANT: Never display, log, or send NOTION_API_TOKEN anywhere except in the Authorization header. Confirm its existence, ask if missing, use it in requests—but never echo or expose it.
All requests require these headers:
bash-H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" \ -H "Content-Type: application/json"
Test the API key by retrieving the bot user:
bashcurl -s "https://api.notion.com/v1/users/me" \ -H "Authorization: Bearer $NOTION_API_TOKEN" \ -H "Notion-Version: 2025-09-03" | jq
https://api.notion.com2025-09-03 (required header)2020-08-12T02:12:33.231Z)snake_casenull instead of empty stringsRetry-After header| Type | Limit | |------|-------| | 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-02 | fail→fail | 11,370 | 8,723 | -23% | 1 | 1 | 0% | 441 | 5,236 | +1087% | 0 | 0 | — |
case-01 | fail→fail | 8,016 | 17,406 | +117% | 1 | 1 | 0% | 1,364 | 4,964 | +264% | 0 | 0 | — |
case-03 | fail→fail | 15,819 | 15,723 | -1% | 1 | 1 | 0% | 2,181 | 5,483 | +151% | 0 | 0 | — |
case-04 | pass→pass | 5,036 | 8,352 | +66% | 1 | 1 | 0% | 734 | 5,156 | +602% | 0 | 0 | — |
case-05 | fail→pass | 14,148 | 15,512 | +10% | 1 | 1 | 0% | 2,343 | 6,322 | +170% | 0 | 0 | — |
case-06 | pass→fail | 7,399 | 11,095 | +50% | 1 | 1 | 0% | 1,161 | 5,503 | +374% | 0 | 0 | — |
case-07 | fail→pass | 4,243 | 2,776 | -35% | 1 | 1 | 0% | 908 | 5,239 | +477% | 0 | 0 | — |
case-08 | fail→pass | 10,016 | 6,410 | -36% | 1 | 1 | 0% | 1,693 | 5,739 | +239% | 0 | 0 | — |
case-09 | pass→pass | 9,200 | 8,671 | -6% | 1 | 1 | 0% | 884 | 5,310 | +501% | 0 | 0 | — |
case-10 | pass→pass | 13,122 | 9,236 | -30% | 1 | 1 | 0% | 1,716 | 5,418 | +216% | 0 | 0 | — |
case-11 | fail→pass | 20,138 | 9,457 | -53% | 1 | 1 | 0% | 2,483 | 5,473 | +120% | 0 | 0 | — |
case-12 | pass→pass | 6,913 | 7,532 | +9% | 1 | 1 | 0% | 1,179 | 5,792 | +391% | 0 | 0 | — |
case-13 | pass→pass | 3,163 | 3,246 | +3% | 1 | 1 | 0% | 467 | 5,219 | +1018% | 0 | 0 | — |
case-14 | pass→pass | 3,799 | 2,474 | -35% | 1 | 1 | 0% | 561 | 5,033 | +797% | 0 | 0 | — |
case-15 | fail→pass | 9,677 | 5,950 | -39% | 1 | 1 | 0% | 871 | 5,712 | +556% | 0 | 0 | — |
case-16 | pass→pass | 5,920 | 9,766 | +65% | 1 | 1 | 0% | 1,029 | 5,668 | +451% | 0 | 0 | — |
case-17 | fail→pass | 15,254 | 9,875 | -35% | 1 | 1 | 0% | 1,800 | 5,635 | +213% | 0 | 0 | — |
case-18 | pass→pass | 13,195 | 4,295 | -67% | 1 | 1 | 0% | 1,713 | 5,464 | +219% | 0 | 0 | — |
case-19 | fail→pass | 5,003 | 12,069 | +141% | 1 | 1 | 0% | 863 | 6,226 | +621% | 0 | 0 | — |
case-20 | pass→pass | 7,072 | 9,735 | +38% | 1 | 1 | 0% | 1,190 | 5,440 | +357% | 0 | 0 | — |
case-21 | fail→pass | 8,066 | 9,607 | +19% | 1 | 1 | 0% | 1,478 | 5,568 | +277% | 0 | 0 | — |
case-22 | pass→pass | 7,752 | 3,227 | -58% | 1 | 1 | 0% | 1,317 | 5,371 | +308% | 0 | 0 | — |
case-23 | pass→pass | 8,302 | 5,012 | -40% | 1 | 1 | 0% | 1,544 | 5,603 | +263% | 0 | 0 | — |
case-24 | fail→pass | 18,540 | 9,102 | -51% | 1 | 1 | 0% | 1,988 | 5,275 | +165% | 0 | 0 | — |
case-25 | fail→pass | 10,127 | 12,972 | +28% | 1 | 1 | 0% | 1,805 | 6,132 | +240% | 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. 25 cases were attempted, and 22 counted toward the lift figure. The other 3 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +36 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
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.