
Notion Api
- 60.4k installs
- 279 repo stars
- Updated April 25, 2026
- intellectronica/agent-skills
notion-api is a reference agent skill that documents Notion API block types and JSON payload structures for developers building agents or automations that read and write Notion pages and databases.
Key points
- Complete reference for every Notion block type including paragraph, heading_1-3, quote, callout, to_do, toggle, code, im
- Documents all common block fields: id, parent, created_time, last_edited_time, has_children, archived, rich_text, color,
- Shows exact JSON shapes required for API create/update calls
Notion Api by the numbers
- 60,368 all-time installs (skills.sh)
- +2,760 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #14 of 4,386 Backend & APIs skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
What notion-api says it does
# Notion Block Types Reference This document provides comprehensive documentation for all supported block types in the Notion API. ## Block Structure Every block contains these common fields:
npx skills add https://github.com/intellectronica/agent-skills --skill notion-apiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 60.4k |
|---|---|
| repo stars | ★ 279 |
| Security audit | 2 / 3 scanners passed |
| Last updated | April 25, 2026 |
| Repository | intellectronica/agent-skills ↗ |
How do you structure Notion API block payloads correctly?
Generate accurate Notion API block payloads and understand response structures when building agents or automations that write to or read from Notion databases and pages
Who is it for?
Developers building Notion-integrated agents, automations, or backend jobs that create and update pages and database entries.
Skip if: Teams needing OAuth setup guides, Notion workspace admin, or non-Notion CMS integrations without API block manipulation.
When should I use this skill?
User builds Notion automations, asks about Notion block types, page append payloads, or database API response structures.
What you get
Valid Notion block JSON payloads, type-specific property maps, and accurate response parsing patterns
- Valid Notion block JSON payloads
- Block type reference mappings
Files
Notion API Skill
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.
Authentication
API Key Handling
1. Environment Variable: Check if NOTION_API_TOKEN is available in the environment 2. User-Provided Key: If the user provides an API key in context, use that instead 3. 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 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.
Request Headers
All requests require these headers:
-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:
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:
2025-09-03(required header) - 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
nullinstead of empty strings
Rate Limits
- Average: 3 requests per second per integration
- Bursts: Brief bursts above this limit are allowed
- Rate Limited Response: HTTP 429 with
Retry-Afterheader - 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:
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: "page" or "data_source" (or omit for both)
Pages
Retrieve a Page
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
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:
{"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+)
Update a Page
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: cover, is_locked, in_trash
Archive (Delete) a Page
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:
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
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 block_id to get page content. Check has_children on each block for nested content.
Append Block Children
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
"position": {"type": "start"}- Insert at beginning"position": {"type": "after_block", "after_block": {"id": "block-id"}}- Insert after specific block
Retrieve a Block
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
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
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
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
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 references/filters-and-sorts.md for comprehensive filter and sort documentation.
Create a Database
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
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
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
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
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)
curl -s "https://api.notion.com/v1/users/me" \
-H "Authorization: Bearer $NOTION_API_TOKEN" \
-H "Notion-Version: 2025-09-03" | jqComments
Retrieve Comments
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 block_id for page-level comments.
Create a Comment
On a page:
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:
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:
has_more: Boolean indicating more results existnext_cursor: Cursor for the next pageresults: Array of items
To iterate through all results:
1. Make the initial request (omit start_cursor) 2. Check has_more in the response 3. If true, extract next_cursor and include it as start_cursor in the next request 4. Repeat until has_more is false
Example request with cursor:
{
"page_size": 100,
"start_cursor": "v1%7C..."
}Error Handling
| 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 |
Best Practices
1. Store IDs: When creating pages/databases, store the returned IDs for future updates 2. Use Property IDs: Reference properties by ID rather than name for stability 3. Batch Operations: Aggregate multiple small operations into fewer requests 4. Respect Rate Limits: Implement exponential backoff for 429 responses 5. Check `has_more`: Always handle pagination for list endpoints 6. Validate Before Updates: Retrieve current state before making updates 7. Use Environment Variables: Never hardcode API keys 8. Handle Errors Gracefully: Check response status codes and error messages 9. Schema Size: Keep database schemas under 50KB for optimal performance 10. Properties Limit: Properties with >25 page references require separate retrieval
References
For 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
Notion Block Types Reference
This document provides comprehensive documentation for all supported block types in the Notion API.
Block Structure
Every block contains these common fields:
{
"object": "block",
"id": "uuid",
"type": "block_type",
"parent": {"type": "page_id", "page_id": "..."},
"created_time": "2024-01-01T00:00:00.000Z",
"last_edited_time": "2024-01-01T00:00:00.000Z",
"created_by": {"object": "user", "id": "..."},
"last_edited_by": {"object": "user", "id": "..."},
"archived": false,
"in_trash": false,
"has_children": false,
"{type}": { /* type-specific properties */ }
}Text Blocks
Paragraph
{
"type": "paragraph",
"paragraph": {
"rich_text": [{"type": "text", "text": {"content": "Paragraph text"}}],
"color": "default"
}
}Supports children (nested blocks).
Headings
{
"type": "heading_1",
"heading_1": {
"rich_text": [{"type": "text", "text": {"content": "Heading 1"}}],
"color": "default",
"is_toggleable": false
}
}Types: heading_1, heading_2, heading_3
When is_toggleable: true, headings can contain children.
Quote
{
"type": "quote",
"quote": {
"rich_text": [{"type": "text", "text": {"content": "Quote text"}}],
"color": "default"
}
}Supports children.
Callout
{
"type": "callout",
"callout": {
"rich_text": [{"type": "text", "text": {"content": "Callout text"}}],
"icon": {"type": "emoji", "emoji": "💡"},
"color": "gray_background"
}
}Supports children. Icon can be emoji or file.
Code
{
"type": "code",
"code": {
"rich_text": [{"type": "text", "text": {"content": "const x = 1;"}}],
"caption": [],
"language": "javascript"
}
}Supported languages: abap, arduino, bash, basic, c, clojure, coffeescript, cpp, csharp, css, dart, diff, docker, elixir, elm, erlang, flow, fortran, fsharp, gherkin, glsl, go, graphql, groovy, haskell, html, java, javascript, json, julia, kotlin, latex, less, lisp, livescript, lua, makefile, markdown, markup, matlab, mermaid, nix, objective-c, ocaml, pascal, perl, php, plain text, powershell, prolog, protobuf, python, r, reason, ruby, rust, sass, scala, scheme, scss, shell, sql, swift, typescript, vb.net, verilog, vhdl, visual basic, webassembly, xml, yaml, java/c/c++/c#
Equation
{
"type": "equation",
"equation": {
"expression": "E = mc^2"
}
}Uses LaTeX/KaTeX syntax.
List Blocks
Bulleted List Item
{
"type": "bulleted_list_item",
"bulleted_list_item": {
"rich_text": [{"type": "text", "text": {"content": "List item"}}],
"color": "default"
}
}Supports children (nested list items).
Numbered List Item
{
"type": "numbered_list_item",
"numbered_list_item": {
"rich_text": [{"type": "text", "text": {"content": "List item"}}],
"color": "default"
}
}Supports children.
To-Do
{
"type": "to_do",
"to_do": {
"rich_text": [{"type": "text", "text": {"content": "Task"}}],
"checked": false,
"color": "default"
}
}Supports children.
Toggle
{
"type": "toggle",
"toggle": {
"rich_text": [{"type": "text", "text": {"content": "Toggle header"}}],
"color": "default"
}
}Supports children (the toggle content).
Media Blocks
Image
{
"type": "image",
"image": {
"type": "external",
"external": {"url": "https://example.com/image.png"},
"caption": []
}
}Or for uploaded files:
{
"type": "image",
"image": {
"type": "file",
"file": {"url": "https://...", "expiry_time": "..."},
"caption": []
}
}Supported formats: .bmp, .gif, .heic, .jpeg, .jpg, .png, .svg, .tif, .tiff
Video
{
"type": "video",
"video": {
"type": "external",
"external": {"url": "https://www.youtube.com/watch?v=..."},
"caption": []
}
}Supported formats: .mp4, .mov, .avi, .mkv, .wmv, plus YouTube links
Audio
{
"type": "audio",
"audio": {
"type": "external",
"external": {"url": "https://example.com/audio.mp3"},
"caption": []
}
}Supported formats: .mp3, .wav, .ogg, .oga, .m4a
File
{
"type": "file",
"file": {
"type": "external",
"external": {"url": "https://example.com/doc.pdf"},
"caption": [],
"name": "Document.pdf"
}
}{
"type": "pdf",
"pdf": {
"type": "external",
"external": {"url": "https://example.com/document.pdf"},
"caption": []
}
}Bookmark
{
"type": "bookmark",
"bookmark": {
"url": "https://example.com",
"caption": []
}
}Embed
{
"type": "embed",
"embed": {
"url": "https://example.com/embed",
"caption": []
}
}Note: Embed rendering may differ from Notion app due to lack of iFramely integration.
Link Preview
{
"type": "link_preview",
"link_preview": {
"url": "https://example.com"
}
}Read-only - cannot be created via API.
Structural Blocks
Divider
{
"type": "divider",
"divider": {}
}Table of Contents
{
"type": "table_of_contents",
"table_of_contents": {
"color": "default"
}
}Breadcrumb
{
"type": "breadcrumb",
"breadcrumb": {}
}Column List and Column
{
"type": "column_list",
"column_list": {}
}Column list contains column children:
{
"type": "column",
"column": {}
}Columns contain actual content blocks. Note: Column list must have at least 2 columns.
Table
{
"type": "table",
"table": {
"table_width": 3,
"has_column_header": true,
"has_row_header": false
}
}Table contains table_row children:
{
"type": "table_row",
"table_row": {
"cells": [
[{"type": "text", "text": {"content": "Cell 1"}}],
[{"type": "text", "text": {"content": "Cell 2"}}],
[{"type": "text", "text": {"content": "Cell 3"}}]
]
}
}Note: table_width can only be set at creation, not updated later.
Special Blocks
Child Page
{
"type": "child_page",
"child_page": {
"title": "Page Title"
}
}Read-only - use Create Page endpoint to create pages.
Child Database
{
"type": "child_database",
"child_database": {
"title": "Database Title"
}
}Read-only - use Create Database endpoint to create databases.
Synced Block
Original synced block:
{
"type": "synced_block",
"synced_block": {
"synced_from": null
}
}Reference to synced block:
{
"type": "synced_block",
"synced_block": {
"synced_from": {
"type": "block_id",
"block_id": "original-block-id"
}
}
}Note: API doesn't support updating synced block content.
Template (Deprecated)
{
"type": "template",
"template": {
"rich_text": [{"type": "text", "text": {"content": "Template button"}}]
}
}Deprecated as of March 27, 2023 - use database templates instead.
Unsupported
{
"type": "unsupported",
"unsupported": {}
}Represents blocks not supported by the API.
Color Options
Available colors for blocks that support the color property:
defaultgray,brown,orange,yellow,green,blue,purple,pink,redgray_background,brown_background,orange_background,yellow_background,green_background,blue_background,purple_background,pink_background,red_background
Blocks That Support Children
The following block types can contain nested blocks:
paragraphbulleted_list_itemnumbered_list_itemto_dotogglequotecalloutcolumn_list(containscolumnchildren)column(contains content blocks)table(containstable_rowchildren)synced_blocktemplateheading_1,heading_2,heading_3(whenis_toggleable: true)child_page(conceptually, via separate API calls)child_database(conceptually, via separate API calls)
Notion Database Query Filters and Sorts
This document provides comprehensive documentation for filtering and sorting database queries in the Notion API.
Filter Structure
Filters are sent in the request body of database query requests:
curl -s -X POST "https://api.notion.com/v1/databases/{database_id}/query" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"filter": { /* filter object */ },
"sorts": [ /* sort array */ ],
"page_size": 100
}'Single Property Filters
Each filter object requires:
property: The property name or ID- A type-specific condition object
Text Filters (Rich Text, Title, URL, Email, Phone Number)
{
"property": "Name",
"rich_text": {
"equals": "exact match"
}
}Text conditions:
equals- Exact match (case-sensitive)does_not_equal- Not exact matchcontains- Contains substringdoes_not_contain- Does not contain substringstarts_with- Starts with stringends_with- Ends with stringis_empty- Value is empty (boolean: true)is_not_empty- Value is not empty (boolean: true)
Examples:
{"property": "Title", "title": {"contains": "Project"}}
{"property": "Website", "url": {"starts_with": "https://"}}
{"property": "Email", "email": {"is_not_empty": true}}
{"property": "Phone", "phone_number": {"contains": "+1"}}Number Filters
{
"property": "Price",
"number": {
"greater_than": 100
}
}Number conditions:
equals- Equal to numberdoes_not_equal- Not equal to numbergreater_than- Greater than numberless_than- Less than numbergreater_than_or_equal_to- Greater than or equalless_than_or_equal_to- Less than or equalis_empty- Value is empty (boolean: true)is_not_empty- Value is not empty (boolean: true)
Checkbox Filters
{
"property": "Complete",
"checkbox": {
"equals": true
}
}Checkbox conditions:
equals- Equal to boolean (true/false)does_not_equal- Not equal to boolean
Select Filters
{
"property": "Status",
"select": {
"equals": "Done"
}
}Select conditions:
equals- Matches option namedoes_not_equal- Does not match option nameis_empty- No selection (boolean: true)is_not_empty- Has selection (boolean: true)
Multi-Select Filters
{
"property": "Tags",
"multi_select": {
"contains": "Urgent"
}
}Multi-select conditions:
contains- Contains option namedoes_not_contain- Does not contain option nameis_empty- No selections (boolean: true)is_not_empty- Has selections (boolean: true)
Status Filters
{
"property": "Project Status",
"status": {
"equals": "In progress"
}
}Status conditions (same as select):
equals,does_not_equal,is_empty,is_not_empty
Date Filters
{
"property": "Due Date",
"date": {
"after": "2024-01-01"
}
}Date conditions with date values:
equals- Exact date matchbefore- Before dateafter- After dateon_or_before- On or before dateon_or_after- On or after date
Date conditions without values (boolean: true):
is_empty- No date setis_not_empty- Date is setpast_week- Within the past weekpast_month- Within the past monthpast_year- Within the past yearthis_week- Within current weeknext_week- Within next weeknext_month- Within next monthnext_year- Within next year
Date format: ISO 8601 (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS.sssZ)
Note: If no timezone is provided, defaults to UTC.
People Filters
{
"property": "Assignee",
"people": {
"contains": "user-uuid"
}
}People conditions:
contains- Contains user IDdoes_not_contain- Does not contain user IDis_empty- No people assigned (boolean: true)is_not_empty- Has people assigned (boolean: true)
Files Filters
{
"property": "Attachments",
"files": {
"is_not_empty": true
}
}Files conditions:
is_empty- No files (boolean: true)is_not_empty- Has files (boolean: true)
Relation Filters
{
"property": "Related Projects",
"relation": {
"contains": "page-uuid"
}
}Relation conditions:
contains- Contains related page IDdoes_not_contain- Does not contain related page IDis_empty- No relations (boolean: true)is_not_empty- Has relations (boolean: true)
Rollup Filters
Rollup filters depend on the rollup type:
For aggregated rollups (count, sum, etc.):
{
"property": "Task Count",
"rollup": {
"number": {
"greater_than": 5
}
}
}For "show original" rollups, use any, every, or none:
{
"property": "Task Statuses",
"rollup": {
"any": {
"select": {
"equals": "Done"
}
}
}
}Rollup conditions:
any- At least one item matchesevery- All items matchnone- No items match
Formula Filters
Formula filters depend on the formula result type:
{
"property": "Days Until Due",
"formula": {
"number": {
"less_than": 7
}
}
}{
"property": "Is Overdue",
"formula": {
"checkbox": {
"equals": true
}
}
}Timestamp Filters
Filter by creation or edit time without specifying a property:
{
"timestamp": "created_time",
"created_time": {
"after": "2024-01-01"
}
}{
"timestamp": "last_edited_time",
"last_edited_time": {
"past_week": {}
}
}Unique ID Filters
{
"property": "ID",
"unique_id": {
"equals": 42
}
}Unique ID conditions:
equals- Exact number matchdoes_not_equal- Not equal to numbergreater_than,less_than,greater_than_or_equal_to,less_than_or_equal_to
---
Compound Filters
Combine multiple filters using and or or:
AND Filter (All conditions must match)
{
"and": [
{"property": "Status", "select": {"equals": "In Progress"}},
{"property": "Priority", "select": {"equals": "High"}}
]
}OR Filter (Any condition must match)
{
"or": [
{"property": "Status", "select": {"equals": "Done"}},
{"property": "Status", "select": {"equals": "Archived"}}
]
}Nested Compound Filters
Note: Nesting is supported up to two levels deep.
{
"and": [
{"property": "Type", "select": {"equals": "Task"}},
{
"or": [
{"property": "Priority", "select": {"equals": "High"}},
{
"and": [
{"property": "Priority", "select": {"equals": "Medium"}},
{"property": "Due Date", "date": {"before": "2024-02-01"}}
]
}
]
}
]
}---
Sort Structure
Sorts are provided as an array. Earlier sorts take precedence over later ones.
Property Value Sort
{
"sorts": [
{
"property": "Due Date",
"direction": "ascending"
}
]
}Timestamp Sort
{
"sorts": [
{
"timestamp": "created_time",
"direction": "descending"
}
]
}Multiple Sorts
{
"sorts": [
{"property": "Priority", "direction": "descending"},
{"property": "Due Date", "direction": "ascending"},
{"timestamp": "created_time", "direction": "descending"}
]
}Sort directions:
ascending- A to Z, 0 to 9, oldest to newestdescending- Z to A, 9 to 0, newest to oldest
---
Complete Query Examples
Tasks due this week, high priority first
{
"filter": {
"and": [
{"property": "Due Date", "date": {"this_week": {}}},
{"property": "Status", "status": {"does_not_equal": "Done"}}
]
},
"sorts": [
{"property": "Priority", "direction": "descending"},
{"property": "Due Date", "direction": "ascending"}
],
"page_size": 50
}Recent items created by specific user
{
"filter": {
"and": [
{"timestamp": "created_time", "created_time": {"past_month": {}}},
{"property": "Created By", "people": {"contains": "user-uuid"}}
]
},
"sorts": [
{"timestamp": "created_time", "direction": "descending"}
]
}Items with specific tag OR high priority
{
"filter": {
"or": [
{"property": "Tags", "multi_select": {"contains": "Urgent"}},
{"property": "Priority", "select": {"equals": "High"}}
]
}
}Uncompleted tasks assigned to anyone
{
"filter": {
"and": [
{"property": "Assignee", "people": {"is_not_empty": true}},
{"property": "Complete", "checkbox": {"equals": false}}
]
}
}---
Filter Properties Parameter
Limit which properties are returned in the response:
curl -s -X POST "https://api.notion.com/v1/databases/{database_id}/query" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json" \
-d '{
"filter_properties": ["Name", "Status", "Due Date"]
}'Or using property IDs:
{
"filter_properties": ["title", "abc123", "xyz789"]
}---
Limitations
1. Nesting Depth: Compound filters support up to 2 levels of nesting 2. Relation Rollups: Formulas depending on relations with >25 references only evaluate 25 items 3. Multi-layer Rollups: Rollups of rollups may produce incorrect results 4. Case Sensitivity: Text comparisons are case-sensitive 5. Date Precision: Date comparisons use millisecond precision when times are included
Notion Property Types Reference
This document covers database property schemas and page property values in the Notion API.
Property Schema Objects (Database Columns)
Property schemas define the structure of database columns. Every database requires exactly one title property.
Title
{
"Name": {
"id": "title",
"type": "title",
"title": {}
}
}Required. One per database. Controls the title displayed at the top of pages.
Rich Text
{
"Description": {
"type": "rich_text",
"rich_text": {}
}
}Number
{
"Price": {
"type": "number",
"number": {
"format": "dollar"
}
}
}Format options:
- Numbers:
number,number_with_commas,percent - Currencies:
dollar,euro,pound,yen,ruble,rupee,won,yuan,real,lira,canadian_dollar,australian_dollar,singapore_dollar,hong_kong_dollar,new_zealand_dollar,krona,norwegian_krone,mexican_peso,rand,new_taiwan_dollar,danish_krone,zloty,baht,forint,koruna,shekel,chilean_peso,philippine_peso,dirham,colombian_peso,riyal,ringgit,leu,argentine_peso,uruguayan_peso,peruvian_sol
Select
{
"Status": {
"type": "select",
"select": {
"options": [
{"id": "uuid", "name": "To Do", "color": "red"},
{"id": "uuid", "name": "In Progress", "color": "yellow"},
{"id": "uuid", "name": "Done", "color": "green"}
]
}
}
}Color options: default, gray, brown, orange, yellow, green, blue, purple, pink, red
Multi-Select
{
"Tags": {
"type": "multi_select",
"multi_select": {
"options": [
{"id": "uuid", "name": "Tag1", "color": "blue"},
{"id": "uuid", "name": "Tag2", "color": "green"}
]
}
}
}Maximum 100 options.
Status
{
"Project Status": {
"type": "status",
"status": {
"options": [
{"id": "uuid", "name": "Not started", "color": "default"},
{"id": "uuid", "name": "In progress", "color": "blue"},
{"id": "uuid", "name": "Done", "color": "green"}
],
"groups": [
{"id": "uuid", "name": "To-do", "color": "gray", "option_ids": ["..."]},
{"id": "uuid", "name": "In progress", "color": "blue", "option_ids": ["..."]},
{"id": "uuid", "name": "Complete", "color": "green", "option_ids": ["..."]}
]
}
}
}Note: Creating new status properties via API is not supported.
Date
{
"Due Date": {
"type": "date",
"date": {}
}
}Checkbox
{
"Complete": {
"type": "checkbox",
"checkbox": {}
}
}URL
{
"Website": {
"type": "url",
"url": {}
}
}{
"Contact": {
"type": "email",
"email": {}
}
}Phone Number
{
"Phone": {
"type": "phone_number",
"phone_number": {}
}
}People
{
"Assignee": {
"type": "people",
"people": {}
}
}Files
{
"Attachments": {
"type": "files",
"files": {}
}
}Relation
Single-direction relation:
{
"Related Tasks": {
"type": "relation",
"relation": {
"database_id": "target-database-uuid",
"type": "single_property"
}
}
}Dual-direction relation:
{
"Related Tasks": {
"type": "relation",
"relation": {
"database_id": "target-database-uuid",
"type": "dual_property",
"dual_property": {
"synced_property_name": "Related Projects",
"synced_property_id": "..."
}
}
}
}Rollup
{
"Total Hours": {
"type": "rollup",
"rollup": {
"relation_property_name": "Tasks",
"relation_property_id": "...",
"rollup_property_name": "Hours",
"rollup_property_id": "...",
"function": "sum"
}
}
}Rollup functions:
- Aggregation:
count_all,count_values,count_unique_values,count_empty,count_not_empty,percent_empty,percent_not_empty - Numbers:
sum,average,median,min,max,range - Dates:
earliest_date,latest_date,date_range - Booleans:
checked,unchecked,percent_checked,percent_unchecked - Display:
show_original,show_unique
Formula
{
"Full Name": {
"type": "formula",
"formula": {
"expression": "prop(\"First Name\") + \" \" + prop(\"Last Name\")"
}
}
}Created Time (Read-Only)
{
"Created": {
"type": "created_time",
"created_time": {}
}
}Created By (Read-Only)
{
"Creator": {
"type": "created_by",
"created_by": {}
}
}Last Edited Time (Read-Only)
{
"Updated": {
"type": "last_edited_time",
"last_edited_time": {}
}
}Last Edited By (Read-Only)
{
"Editor": {
"type": "last_edited_by",
"last_edited_by": {}
}
}Unique ID (Read-Only)
{
"ID": {
"type": "unique_id",
"unique_id": {
"prefix": "PROJ"
}
}
}---
Property Value Objects (Page Properties)
Property values are used when creating or updating pages.
Title Value
{
"Name": {
"title": [
{"type": "text", "text": {"content": "Page Title"}}
]
}
}Rich Text Value
{
"Description": {
"rich_text": [
{"type": "text", "text": {"content": "Description text"}}
]
}
}Number Value
{
"Price": {
"number": 99.99
}
}Select Value
{
"Status": {
"select": {"name": "In Progress"}
}
}Or by ID:
{
"Status": {
"select": {"id": "option-uuid"}
}
}Multi-Select Value
{
"Tags": {
"multi_select": [
{"name": "Tag1"},
{"name": "Tag2"}
]
}
}Status Value
{
"Project Status": {
"status": {"name": "In progress"}
}
}Date Value
Single date:
{
"Due Date": {
"date": {
"start": "2024-12-31"
}
}
}Date with time:
{
"Meeting": {
"date": {
"start": "2024-12-31T14:00:00.000Z",
"time_zone": "America/New_York"
}
}
}Date range:
{
"Sprint": {
"date": {
"start": "2024-01-01",
"end": "2024-01-14"
}
}
}Checkbox Value
{
"Complete": {
"checkbox": true
}
}URL Value
{
"Website": {
"url": "https://example.com"
}
}Email Value
{
"Contact": {
"email": "user@example.com"
}
}Phone Number Value
{
"Phone": {
"phone_number": "+1-555-123-4567"
}
}People Value
{
"Assignee": {
"people": [
{"id": "user-uuid"}
]
}
}Maximum 100 users.
Files Value
External files:
{
"Attachments": {
"files": [
{
"name": "Document.pdf",
"type": "external",
"external": {"url": "https://example.com/doc.pdf"}
}
]
}
}Uploaded files (use file upload API first):
{
"Attachments": {
"files": [
{
"name": "Photo.jpg",
"type": "file",
"file": {"url": "https://..."}
}
]
}
}Note: Updating files property replaces all existing files.
Relation Value
{
"Related Tasks": {
"relation": [
{"id": "page-uuid-1"},
{"id": "page-uuid-2"}
]
}
}Maximum 100 related pages.
Rollup Value (Read-Only)
Rollup values are computed and cannot be set directly.
Response example:
{
"Total": {
"type": "rollup",
"rollup": {
"type": "number",
"number": 42,
"function": "sum"
}
}
}Formula Value (Read-Only)
Formula values are computed and cannot be set directly.
Response example:
{
"Full Name": {
"type": "formula",
"formula": {
"type": "string",
"string": "John Doe"
}
}
}Formula result types: string, number, boolean, date
Created Time Value (Read-Only)
{
"Created": {
"type": "created_time",
"created_time": "2024-01-01T00:00:00.000Z"
}
}Created By Value (Read-Only)
{
"Creator": {
"type": "created_by",
"created_by": {
"object": "user",
"id": "user-uuid"
}
}
}Last Edited Time Value (Read-Only)
{
"Updated": {
"type": "last_edited_time",
"last_edited_time": "2024-01-15T12:00:00.000Z"
}
}Last Edited By Value (Read-Only)
{
"Editor": {
"type": "last_edited_by",
"last_edited_by": {
"object": "user",
"id": "user-uuid"
}
}
}Unique ID Value (Read-Only)
{
"ID": {
"type": "unique_id",
"unique_id": {
"prefix": "PROJ",
"number": 42
}
}
}---
Important Notes
Property Value Limit
Property values in page objects have a 25 page reference limit. Properties containing more than 25 page references (in relation, people, or rollup properties) only display the first 25 in standard responses.
Use the "Retrieve a page property" endpoint to get the complete list:
curl -s "https://api.notion.com/v1/pages/{page_id}/properties/{property_id}" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03"Read-Only Properties
These properties cannot be set via API:
created_timecreated_bylast_edited_timelast_edited_byrollupformulaunique_id
Property References
Properties can be referenced by either:
- Name:
"Status"(may change if user renames) - ID:
"abc123"(stable, recommended for integrations)
Get property IDs by retrieving the database schema.
Null Values
To clear a property value, set it to null:
{
"Due Date": {
"date": null
}
}For rich text/title, use an empty array:
{
"Description": {
"rich_text": []
}
}Notion Rich Text Reference
This document provides comprehensive documentation for rich text objects in the Notion API.
Rich Text Array Structure
Rich text in Notion is represented as an array of rich text objects. Each block or property that supports formatted text uses this structure:
{
"rich_text": [
{
"type": "text",
"text": {"content": "Hello "},
"annotations": {"bold": false, "italic": false, "strikethrough": false, "underline": false, "code": false, "color": "default"},
"plain_text": "Hello ",
"href": null
},
{
"type": "text",
"text": {"content": "world", "link": {"url": "https://example.com"}},
"annotations": {"bold": true, "italic": false, "strikethrough": false, "underline": false, "code": false, "color": "default"},
"plain_text": "world",
"href": "https://example.com"
}
]
}Common Fields
All rich text objects contain:
| Field | Type | Description |
|---|---|---|
type | string | The type of rich text object |
annotations | object | Styling applied to the text |
plain_text | string | Plain text without formatting |
href | string or null | URL if the text is a link |
---
Rich Text Types
Text
Basic text content with optional link:
{
"type": "text",
"text": {
"content": "Link text",
"link": {"url": "https://example.com"}
},
"annotations": { /* ... */ },
"plain_text": "Link text",
"href": "https://example.com"
}Without link:
{
"type": "text",
"text": {
"content": "Plain text"
},
"annotations": { /* ... */ },
"plain_text": "Plain text",
"href": null
}Equation
Inline mathematical expressions using LaTeX/KaTeX:
{
"type": "equation",
"equation": {
"expression": "E = mc^2"
},
"annotations": { /* ... */ },
"plain_text": "E = mc^2",
"href": null
}Mention
References to other Notion objects or users.
User Mention
{
"type": "mention",
"mention": {
"type": "user",
"user": {
"object": "user",
"id": "user-uuid",
"name": "John Doe",
"avatar_url": "https://...",
"type": "person",
"person": {"email": "john@example.com"}
}
},
"annotations": { /* ... */ },
"plain_text": "@John Doe",
"href": "https://www.notion.so/user-uuid"
}Page Mention
{
"type": "mention",
"mention": {
"type": "page",
"page": {
"id": "page-uuid"
}
},
"annotations": { /* ... */ },
"plain_text": "Page Title",
"href": "https://www.notion.so/page-uuid"
}Note: If the integration doesn't have access to the mentioned page, only the ID is returned.
Database Mention
{
"type": "mention",
"mention": {
"type": "database",
"database": {
"id": "database-uuid"
}
},
"annotations": { /* ... */ },
"plain_text": "Database Title",
"href": "https://www.notion.so/database-uuid"
}Date Mention
{
"type": "mention",
"mention": {
"type": "date",
"date": {
"start": "2024-01-15",
"end": null,
"time_zone": null
}
},
"annotations": { /* ... */ },
"plain_text": "January 15, 2024",
"href": null
}With date range:
{
"type": "mention",
"mention": {
"type": "date",
"date": {
"start": "2024-01-15",
"end": "2024-01-20",
"time_zone": null
}
},
"annotations": { /* ... */ },
"plain_text": "January 15, 2024 → January 20, 2024",
"href": null
}Link Preview Mention
{
"type": "mention",
"mention": {
"type": "link_preview",
"link_preview": {
"url": "https://github.com/..."
}
},
"annotations": { /* ... */ },
"plain_text": "https://github.com/...",
"href": "https://github.com/..."
}Template Mention (Date)
Used in template blocks for dynamic dates:
{
"type": "mention",
"mention": {
"type": "template_mention",
"template_mention": {
"type": "template_mention_date",
"template_mention_date": "today"
}
},
"annotations": { /* ... */ },
"plain_text": "@today",
"href": null
}Template date options: today, now
Template Mention (User)
Used in template blocks for dynamic user references:
{
"type": "mention",
"mention": {
"type": "template_mention",
"template_mention": {
"type": "template_mention_user",
"template_mention_user": "me"
}
},
"annotations": { /* ... */ },
"plain_text": "@me",
"href": null
}---
Annotations
Annotations control text styling:
{
"annotations": {
"bold": false,
"italic": false,
"strikethrough": false,
"underline": false,
"code": false,
"color": "default"
}
}| Property | Type | Description |
|---|---|---|
bold | boolean | Bold text |
italic | boolean | Italic text |
strikethrough | boolean | Strikethrough text |
underline | boolean | Underlined text |
code | boolean | Inline code styling |
color | string | Text or background color |
Color Options
Text colors:
defaultgraybrownorangeyellowgreenbluepurplepinkred
Background colors:
gray_backgroundbrown_backgroundorange_backgroundyellow_backgroundgreen_backgroundblue_backgroundpurple_backgroundpink_backgroundred_background
---
Creating Rich Text
Simple Text
[
{
"type": "text",
"text": {"content": "Simple text"}
}
]Formatted Text
[
{
"type": "text",
"text": {"content": "Bold and italic"},
"annotations": {"bold": true, "italic": true}
}
]Mixed Formatting
[
{"type": "text", "text": {"content": "Normal "}},
{"type": "text", "text": {"content": "bold"}, "annotations": {"bold": true}},
{"type": "text", "text": {"content": " and "}},
{"type": "text", "text": {"content": "italic"}, "annotations": {"italic": true}},
{"type": "text", "text": {"content": " text."}}
]Text with Link
[
{"type": "text", "text": {"content": "Check out "}},
{
"type": "text",
"text": {"content": "this link", "link": {"url": "https://example.com"}}
},
{"type": "text", "text": {"content": " for more info."}}
]Code Styling
[
{"type": "text", "text": {"content": "Use the "}},
{"type": "text", "text": {"content": "console.log()"}, "annotations": {"code": true}},
{"type": "text", "text": {"content": " function."}}
]Colored Text
[
{
"type": "text",
"text": {"content": "Important!"},
"annotations": {"color": "red", "bold": true}
}
]With Background Color
[
{
"type": "text",
"text": {"content": "Highlighted text"},
"annotations": {"color": "yellow_background"}
}
]User Mention
[
{"type": "text", "text": {"content": "Assigned to "}},
{
"type": "mention",
"mention": {
"type": "user",
"user": {"id": "user-uuid"}
}
}
]Page Mention
[
{"type": "text", "text": {"content": "See also: "}},
{
"type": "mention",
"mention": {
"type": "page",
"page": {"id": "page-uuid"}
}
}
]Date Mention
[
{"type": "text", "text": {"content": "Due: "}},
{
"type": "mention",
"mention": {
"type": "date",
"date": {"start": "2024-12-31"}
}
}
]Inline Equation
[
{"type": "text", "text": {"content": "The formula is "}},
{"type": "equation", "equation": {"expression": "x = \\frac{-b \\pm \\sqrt{b^2-4ac}}{2a}"}},
{"type": "text", "text": {"content": "."}}
]---
Limits
| Type | Limit |
|---|---|
| Rich text content | 2000 characters per rich text object |
| Rich text array | 100 items max |
| Equations | 1000 characters |
---
Reading Rich Text
When processing rich text from API responses, you can:
1. Get plain text: Concatenate all plain_text values 2. Preserve formatting: Process each object and apply annotations 3. Extract links: Check href field or text.link.url
Example (JavaScript):
function getPlainText(richTextArray) {
return richTextArray.map(rt => rt.plain_text).join('');
}
function getLinks(richTextArray) {
return richTextArray
.filter(rt => rt.href)
.map(rt => ({text: rt.plain_text, url: rt.href}));
}Example (bash with jq):
# Get plain text
echo "$rich_text_json" | jq -r '[.[].plain_text] | join("")'
# Get all links
echo "$rich_text_json" | jq '[.[] | select(.href != null) | {text: .plain_text, url: .href}]'Related skills
How it compares
Use notion-api for block-level Notion API JSON reference during code generation, not for high-level product management workflows.
FAQ
What does the notion-api skill document?
notion-api documents Notion API block types, shared block fields, and type-specific JSON properties with examples such as paragraph blocks. Developers use it to generate accurate create, update, and append payloads for Notion pages and databases.
Which block fields are common across Notion types?
Notion blocks documented by notion-api share fields including object, id, type, parent, created_time, last_edited_time, created_by, last_edited_by, archived, in_trash, and has_children, plus a type-keyed properties object.
When should agents load notion-api?
Agents should load notion-api when a developer integrates Notion read or write operations and needs correct block JSON, response parsing, or database page structures without trial-and-error API errors.
Is Notion Api safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.