notion-api
Original:🇺🇸 English
Translated
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.
22.6kinstalls
Added on
NPX Install
npx skill4agent add intellectronica/agent-skills notion-apiTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →Notion API Skill
This skill enables interaction with Notion workspaces through the Notion REST API. Use and for direct REST calls, or write ad-hoc scripts as appropriate for the task.
curljqAuthentication
API Key Handling
- Environment Variable: Check if is available in the environment
NOTION_API_TOKEN - User-Provided Key: If the user provides an API key in context, use that instead
- No Key Available: If neither is available, use AskUserQuestion (or equivalent) to request the API key from the user
IMPORTANT: Never display, log, or send anywhere except in the header. Confirm its existence, ask if missing, use it in requests—but never echo or expose it.
NOTION_API_TOKENAuthorizationRequest Headers
All requests require these headers:
bash
-H "Authorization: Bearer $NOTION_API_TOKEN" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json"Verifying Authentication
Test the API key by retrieving the bot user:
bash
curl -s "https://api.notion.com/v1/users/me" \
-H "Authorization: Bearer $NOTION_API_TOKEN" \
-H "Notion-Version: 2025-09-03" | jqBase URL and Conventions
- Base URL:
https://api.notion.com - API Version: (required header)
2025-09-03 - Data Format: JSON for all request/response bodies
- IDs: UUIDv4 format (dashes optional in requests)
- Timestamps: ISO 8601 format ()
2020-08-12T02:12:33.231Z - Property Names:
snake_case - Empty Values: Use instead of empty strings
null
Rate Limits
- Average: 3 requests per second per integration
- Bursts: Brief bursts above this limit are allowed
- Rate Limited Response: HTTP 429 with header
Retry-After - Strategy: Implement exponential backoff when receiving 429 responses
Request Size Limits
| 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 |
Confirmation for Destructive Operations
IMPORTANT: Before executing any operation that modifies or deletes data, ask the user for confirmation. This includes:
- Updating pages or blocks
- Deleting/archiving pages or blocks
- Modifying database schemas
- Creating pages (if multiple or in batch)
- Any bulk operations
For a logical group of related operations, a single confirmation is sufficient.
Core API Endpoints
Search
Search across all accessible pages and databases:
bash
curl -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
}' | jqFilter values: or (or omit for both)
"page""data_source"Pages
Retrieve a Page
bash
curl -s "https://api.notion.com/v1/pages/{page_id}" \
-H "Authorization: Bearer $NOTION_API_TOKEN" \
-H "Notion-Version: 2025-09-03" | jqNote: This returns page properties, not content. For content, use "Retrieve block children" with the page ID.
Create a Page
bash
curl -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"}}]
}
}
]
}' | jqParent options:
- - Create under a page
{"page_id": "..."} - - Create in a database (legacy)
{"database_id": "..."} - - Create in a data source (API v2025-09-03+)
{"data_source_id": "..."}
Update a Page
bash
curl -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
}' | jqAdditional update options: , ,
coveris_lockedin_trashArchive (Delete) a Page
bash
curl -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}' | jqRetrieve a Page Property Item
For properties with more than 25 references:
bash
curl -s "https://api.notion.com/v1/pages/{page_id}/properties/{property_id}" \
-H "Authorization: Bearer $NOTION_API_TOKEN" \
-H "Notion-Version: 2025-09-03" | jqBlocks (Page Content)
Retrieve Block Children
bash
curl -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" | jqUse the page ID as to get page content. Check on each block for nested content.
block_idhas_childrenAppend Block Children
bash
curl -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"}}]
}
}
]
}' | jqMaximum 100 blocks per request, up to 2 levels of nesting.
Position options in request body:
- Default: appends to end
- - Insert at beginning
"position": {"type": "start"} - - Insert after specific block
"position": {"type": "after_block", "after_block": {"id": "block-id"}}
Retrieve a Block
bash
curl -s "https://api.notion.com/v1/blocks/{block_id}" \
-H "Authorization: Bearer $NOTION_API_TOKEN" \
-H "Notion-Version: 2025-09-03" | jqUpdate a Block
bash
curl -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"}}]
}
}' | jqThe update replaces the entire value for the specified field.
Delete a Block
bash
curl -s -X DELETE "https://api.notion.com/v1/blocks/{block_id}" \
-H "Authorization: Bearer $NOTION_API_TOKEN" \
-H "Notion-Version: 2025-09-03" | jqMoves block to trash (can be restored).
Databases
Retrieve a Database
bash
curl -s "https://api.notion.com/v1/databases/{database_id}" \
-H "Authorization: Bearer $NOTION_API_TOKEN" \
-H "Notion-Version: 2025-09-03" | jqReturns database structure including data sources and properties.
Query a Database
bash
curl -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
}' | jqSee for comprehensive filter and sort documentation.
references/filters-and-sorts.mdCreate a Database
bash
curl -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": {}}
}
}
}' | jqUpdate a Database
bash
curl -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"}}]
}' | jqData Sources (API v2025-09-03+)
Data sources are individual tables within a database. As of API version 2025-09-03, databases can contain multiple data sources.
Create a Data Source
bash
curl -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": {}}
}
}' | jqUsers
List All Users
bash
curl -s "https://api.notion.com/v1/users?page_size=100" \
-H "Authorization: Bearer $NOTION_API_TOKEN" \
-H "Notion-Version: 2025-09-03" | jqRetrieve a User
bash
curl -s "https://api.notion.com/v1/users/{user_id}" \
-H "Authorization: Bearer $NOTION_API_TOKEN" \
-H "Notion-Version: 2025-09-03" | jqRetrieve Bot User (Self)
bash
curl -s "https://api.notion.com/v1/users/me" \
-H "Authorization: Bearer $NOTION_API_TOKEN" \
-H "Notion-Version: 2025-09-03" | jqComments
Retrieve Comments
bash
curl -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" | jqUse a page ID as for page-level comments.
block_idCreate a Comment
On a page:
bash
curl -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"}}]
}' | jqReply to a discussion:
bash
curl -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"}}]
}' | jqNote: The API cannot start new inline discussion threads or edit/delete existing comments.
Pagination
Paginated endpoints return:
- : Boolean indicating more results exist
has_more - : Cursor for the next page
next_cursor - : Array of items
results
To iterate through all results:
- Make the initial request (omit )
start_cursor - Check in the response
has_more - If , extract
trueand include it asnext_cursorin the next requeststart_cursor - Repeat until is
has_morefalse
Example request with cursor:
json
{
"page_size": 100,
"start_cursor": "v1%7C..."
}Error Handling
| HTTP Status | Code | Description |
|---|---|---|
| 400 | | Request body is not valid JSON |
| 400 | | URL is malformed |
| 400 | | Request is not supported |
| 400 | | Request body doesn't match expected schema |
| 400 | | Missing Notion-Version header |
| 401 | | Invalid bearer token |
| 403 | | Token lacks permission |
| 404 | | Resource doesn't exist or not shared with integration |
| 409 | | Data collision during transaction |
| 429 | | Rate limit exceeded (check Retry-After header) |
| 500 | | Unexpected server error |
| 503 | | Notion unavailable or 60s timeout exceeded |
| 503 | | Database unresponsive |
| 504 | | Request timeout |
Best Practices
- Store IDs: When creating pages/databases, store the returned IDs for future updates
- Use Property IDs: Reference properties by ID rather than name for stability
- Batch Operations: Aggregate multiple small operations into fewer requests
- Respect Rate Limits: Implement exponential backoff for 429 responses
- Check : Always handle pagination for list endpoints
has_more - Validate Before Updates: Retrieve current state before making updates
- Use Environment Variables: Never hardcode API keys
- Handle Errors Gracefully: Check response status codes and error messages
- Schema Size: Keep database schemas under 50KB for optimal performance
- Properties Limit: Properties with >25 page references require separate retrieval
References
For detailed documentation on specific topics, see:
- - All supported block types and their structures
references/block-types.md - - Database property types and value formats
references/property-types.md - - Database query filter and sort syntax
references/filters-and-sorts.md - - Rich text object structure and annotations
references/rich-text.md