SKILL DETAIL
notion-api
intellectronica/agent-skills/notion-api
This skill provides comprehensive instructions for interacting with Notion workspaces through the Notion REST API. It covers authentication, all available endpoints, pagination, error handling, and best practices. Use this skill to read, create, update, or delete pages, databases, blocks, comments, and any other Notion content. The skill includes detailed instructions for making direct REST calls using curl and jq, as well as guidance on handling API keys, rate limits, request size limits, and confirmation for destructive operations. It provides endpoint examples for search, pages, blocks, databases, data sources, users, and comments.
Installation
npx skills add https://github.com/intellectronica/agent-skills --skill notion-api
스킬 파일
SKILL.md
최근 동기화 · 2026. 8. 29.
references/block-types.md›
# 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:
```json
{
"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
```json
{
"type": "paragraph",
"paragraph": {
"rich_text": [{"type": "text", "text": {"content": "Paragraph text"}}],
"color": "default"
}
}
```
Supports children (nested blocks).
### Headings
```json
{
"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
```json
{
"type": "quote",
"quote": {
"rich_text": [{"type": "text", "text": {"content": "Quote text"}}],
"color": "default"
}
}
```
Supports children.
### Callout
```json
{
"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
```json
{
"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
```json
{
"type": "equation",
"equation": {
"expression": "E = mc^2"
}
}
```
Uses LaTeX/KaTeX syntax.
## List Blocks
### Bulleted List Item
```json
{
"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
```json
{
"type": "numbered_list_item",
"numbered_list_item": {
"rich_text": [{"type": "text", "text": {"content": "List item"}}],
"color": "default"
}
}
```
Supports children.
### To-Do
```json
{
"type": "to_do",
"to_do": {
"rich_text": [{"type": "text", "text": {"content": "Task"}}],
"checked": false,
"color": "default"
}
}
```
Supports children.
### Toggle
```json
{
"type": "toggle",
"toggle": {
"rich_text": [{"type": "text", "text": {"content": "Toggle header"}}],
"color": "default"
}
}
```
Supports children (the toggle content).
## Media Blocks
### Image
```json
{
"type": "image",
"image": {
"type": "external",
"external": {"url": "https://example.com/image.png"},
"caption": []
}
}
```
Or for uploaded files:
```json
{
"type": "image",
"image": {
"type": "file",
"file": {"url": "https://...", "expiry_time": "..."},
"caption": []
}
}
```
Supported formats: `.bmp`, `.gif`, `.heic`, `.jpeg`, `.jpg`, `.png`, `.svg`, `.tif`, `.tiff`
### Video
```json
{
"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
```json
{
"type": "audio",
"audio": {
"type": "external",
"external": {"url": "https://example.com/audio.mp3"},
"caption": []
}
}
```
Supported formats: `.mp3`, `.wav`, `.ogg`, `.oga`, `.m4a`
### File
```json
{
"type": "file",
"file": {
"type": "external",
"external": {"url": "https://example.com/doc.pdf"},
"caption": [],
"name": "Document.pdf"
}
}
```
### PDF
```json
{
"type": "pdf",
"pdf": {
"type": "external",
"external": {"url": "https://example.com/document.pdf"},
"caption": []
}
}
```
### Bookmark
```json
{
"type": "bookmark",
"bookmark": {
"url": "https://example.com",
"caption": []
}
}
```
### Embed
```json
{
"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
```json
{
"type": "link_preview",
"link_preview": {
"url": "https://example.com"
}
}
```
**Read-only** - cannot be created via API.
## Structural Blocks
### Divider
```json
{
"type": "divider",
"divider": {}
}
```
### Table of Contents
```json
{
"type": "table_of_contents",
"table_of_contents": {
"color": "default"
}
}
```
### Breadcrumb
```json
{
"type": "breadcrumb",
"breadcrumb": {}
}
```
### Column List and Column
```json
{
"type": "column_list",
"column_list": {}
}
```
Column list contains column children:
```json
{
"type": "column",
"column": {}
}
```
Columns contain actual content blocks. **Note**: Column list must have at least 2 columns.
### Table
```json
{
"type": "table",
"table": {
"table_width": 3,
"has_column_header": true,
"has_row_header": false
}
}
```
Table contains `table_row` children:
```json
{
"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
```json
{
"type": "child_page",
"child_page": {
"title": "Page Title"
}
}
```
**Read-only** - use Create Page endpoint to create pages.
### Child Database
```json
{
"type": "child_database",
"child_database": {
"title": "Database Title"
}
}
```
**Read-only** - use Create Database endpoint to create databases.
### Synced Block
Original synced block:
```json
{
"type": "synced_block",
"synced_block": {
"synced_from": null
}
}
```
Reference to synced block:
```json
{
"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)
```json
{
"type": "template",
"template": {
"rich_text": [{"type": "text", "text": {"content": "Template button"}}]
}
}
```
**Deprecated** as of March 27, 2023 - use database templates instead.
### Unsupported
```json
{
"type": "unsupported",
"unsupported": {}
}
```
Represents blocks not supported by the API.
## Color Options
Available colors for blocks that support the `color` property:
- `default`
- `gray`, `brown`, `orange`, `yellow`, `green`, `blue`, `purple`, `pink`, `red`
- `gray_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:
- `paragraph`
- `bulleted_list_item`
- `numbered_list_item`
- `to_do`
- `toggle`
- `quote`
- `callout`
- `column_list` (contains `column` children)
- `column` (contains content blocks)
- `table` (contains `table_row` children)
- `synced_block`
- `template`
- `heading_1`, `heading_2`, `heading_3` (when `is_toggleable: true`)
- `child_page` (conceptually, via separate API calls)
- `child_database` (conceptually, via separate API calls)
references/filters-and-sorts.md›
# 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:
```bash
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)
```json
{
"property": "Name",
"rich_text": {
"equals": "exact match"
}
}
```
Text conditions:
- `equals` - Exact match (case-sensitive)
- `does_not_equal` - Not exact match
- `contains` - Contains substring
- `does_not_contain` - Does not contain substring
- `starts_with` - Starts with string
- `ends_with` - Ends with string
- `is_empty` - Value is empty (boolean: true)
- `is_not_empty` - Value is not empty (boolean: true)
Examples:
```json
{"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
```json
{
"property": "Price",
"number": {
"greater_than": 100
}
}
```
Number conditions:
- `equals` - Equal to number
- `does_not_equal` - Not equal to number
- `greater_than` - Greater than number
- `less_than` - Less than number
- `greater_than_or_equal_to` - Greater than or equal
- `less_than_or_equal_to` - Less than or equal
- `is_empty` - Value is empty (boolean: true)
- `is_not_empty` - Value is not empty (boolean: true)
### Checkbox Filters
```json
{
"property": "Complete",
"checkbox": {
"equals": true
}
}
```
Checkbox conditions:
- `equals` - Equal to boolean (true/false)
- `does_not_equal` - Not equal to boolean
### Select Filters
```json
{
"property": "Status",
"select": {
"equals": "Done"
}
}
```
Select conditions:
- `equals` - Matches option name
- `does_not_equal` - Does not match option name
- `is_empty` - No selection (boolean: true)
- `is_not_empty` - Has selection (boolean: true)
### Multi-Select Filters
```json
{
"property": "Tags",
"multi_select": {
"contains": "Urgent"
}
}
```
Multi-select conditions:
- `contains` - Contains option name
- `does_not_contain` - Does not contain option name
- `is_empty` - No selections (boolean: true)
- `is_not_empty` - Has selections (boolean: true)
### Status Filters
```json
{
"property": "Project Status",
"status": {
"equals": "In progress"
}
}
```
Status conditions (same as select):
- `equals`, `does_not_equal`, `is_empty`, `is_not_empty`
### Date Filters
```json
{
"property": "Due Date",
"date": {
"after": "2024-01-01"
}
}
```
Date conditions with date values:
- `equals` - Exact date match
- `before` - Before date
- `after` - After date
- `on_or_before` - On or before date
- `on_or_after` - On or after date
Date conditions without values (boolean: true):
- `is_empty` - No date set
- `is_not_empty` - Date is set
- `past_week` - Within the past week
- `past_month` - Within the past month
- `past_year` - Within the past year
- `this_week` - Within current week
- `next_week` - Within next week
- `next_month` - Within next month
- `next_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
```json
{
"property": "Assignee",
"people": {
"contains": "user-uuid"
}
}
```
People conditions:
- `contains` - Contains user ID
- `does_not_contain` - Does not contain user ID
- `is_empty` - No people assigned (boolean: true)
- `is_not_empty` - Has people assigned (boolean: true)
### Files Filters
```json
{
"property": "Attachments",
"files": {
"is_not_empty": true
}
}
```
Files conditions:
- `is_empty` - No files (boolean: true)
- `is_not_empty` - Has files (boolean: true)
### Relation Filters
```json
{
"property": "Related Projects",
"relation": {
"contains": "page-uuid"
}
}
```
Relation conditions:
- `contains` - Contains related page ID
- `does_not_contain` - Does not contain related page ID
- `is_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.):
```json
{
"property": "Task Count",
"rollup": {
"number": {
"greater_than": 5
}
}
}
```
For "show original" rollups, use `any`, `every`, or `none`:
```json
{
"property": "Task Statuses",
"rollup": {
"any": {
"select": {
"equals": "Done"
}
}
}
}
```
Rollup conditions:
- `any` - At least one item matches
- `every` - All items match
- `none` - No items match
### Formula Filters
Formula filters depend on the formula result type:
```json
{
"property": "Days Until Due",
"formula": {
"number": {
"less_than": 7
}
}
}
```
```json
{
"property": "Is Overdue",
"formula": {
"checkbox": {
"equals": true
}
}
}
```
### Timestamp Filters
Filter by creation or edit time without specifying a property:
```json
{
"timestamp": "created_time",
"created_time": {
"after": "2024-01-01"
}
}
```
```json
{
"timestamp": "last_edited_time",
"last_edited_time": {
"past_week": {}
}
}
```
### Unique ID Filters
```json
{
"property": "ID",
"unique_id": {
"equals": 42
}
}
```
Unique ID conditions:
- `equals` - Exact number match
- `does_not_equal` - Not equal to number
- `greater_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)
```json
{
"and": [
{"property": "Status", "select": {"equals": "In Progress"}},
{"property": "Priority", "select": {"equals": "High"}}
]
}
```
### OR Filter (Any condition must match)
```json
{
"or": [
{"property": "Status", "select": {"equals": "Done"}},
{"property": "Status", "select": {"equals": "Archived"}}
]
}
```
### Nested Compound Filters
**Note**: Nesting is supported up to **two levels deep**.
```json
{
"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
```json
{
"sorts": [
{
"property": "Due Date",
"direction": "ascending"
}
]
}
```
### Timestamp Sort
```json
{
"sorts": [
{
"timestamp": "created_time",
"direction": "descending"
}
]
}
```
### Multiple Sorts
```json
{
"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 newest
- `descending` - Z to A, 9 to 0, newest to oldest
---
## Complete Query Examples
### Tasks due this week, high priority first
```json
{
"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
```json
{
"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
```json
{
"filter": {
"or": [
{"property": "Tags", "multi_select": {"contains": "Urgent"}},
{"property": "Priority", "select": {"equals": "High"}}
]
}
}
```
### Uncompleted tasks assigned to anyone
```json
{
"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:
```bash
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:
```json
{
"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
references/property-types.md›
# 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
```json
{
"Name": {
"id": "title",
"type": "title",
"title": {}
}
}
```
Required. One per database. Controls the title displayed at the top of pages.
### Rich Text
```json
{
"Description": {
"type": "rich_text",
"rich_text": {}
}
}
```
### Number
```json
{
"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
```json
{
"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
```json
{
"Tags": {
"type": "multi_select",
"multi_select": {
"options": [
{"id": "uuid", "name": "Tag1", "color": "blue"},
{"id": "uuid", "name": "Tag2", "color": "green"}
]
}
}
}
```
Maximum 100 options.
### Status
```json
{
"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
```json
{
"Due Date": {
"type": "date",
"date": {}
}
}
```
### Checkbox
```json
{
"Complete": {
"type": "checkbox",
"checkbox": {}
}
}
```
### URL
```json
{
"Website": {
"type": "url",
"url": {}
}
}
```
### Email
```json
{
"Contact": {
"type": "email",
"email": {}
}
}
```
### Phone Number
```json
{
"Phone": {
"type": "phone_number",
"phone_number": {}
}
}
```
### People
```json
{
"Assignee": {
"type": "people",
"people": {}
}
}
```
### Files
```json
{
"Attachments": {
"type": "files",
"files": {}
}
}
```
### Relation
Single-direction relation:
```json
{
"Related Tasks": {
"type": "relation",
"relation": {
"database_id": "target-database-uuid",
"type": "single_property"
}
}
}
```
Dual-direction relation:
```json
{
"Related Tasks": {
"type": "relation",
"relation": {
"database_id": "target-database-uuid",
"type": "dual_property",
"dual_property": {
"synced_property_name": "Related Projects",
"synced_property_id": "..."
}
}
}
}
```
### Rollup
```json
{
"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
```json
{
"Full Name": {
"type": "formula",
"formula": {
"expression": "prop(\"First Name\") + \" \" + prop(\"Last Name\")"
}
}
}
```
### Created Time (Read-Only)
```json
{
"Created": {
"type": "created_time",
"created_time": {}
}
}
```
### Created By (Read-Only)
```json
{
"Creator": {
"type": "created_by",
"created_by": {}
}
}
```
### Last Edited Time (Read-Only)
```json
{
"Updated": {
"type": "last_edited_time",
"last_edited_time": {}
}
}
```
### Last Edited By (Read-Only)
```json
{
"Editor": {
"type": "last_edited_by",
"last_edited_by": {}
}
}
```
### Unique ID (Read-Only)
```json
{
"ID": {
"type": "unique_id",
"unique_id": {
"prefix": "PROJ"
}
}
}
```
---
## Property Value Objects (Page Properties)
Property values are used when creating or updating pages.
### Title Value
```json
{
"Name": {
"title": [
{"type": "text", "text": {"content": "Page Title"}}
]
}
}
```
### Rich Text Value
```json
{
"Description": {
"rich_text": [
{"type": "text", "text": {"content": "Description text"}}
]
}
}
```
### Number Value
```json
{
"Price": {
"number": 99.99
}
}
```
### Select Value
```json
{
"Status": {
"select": {"name": "In Progress"}
}
}
```
Or by ID:
```json
{
"Status": {
"select": {"id": "option-uuid"}
}
}
```
### Multi-Select Value
```json
{
"Tags": {
"multi_select": [
{"name": "Tag1"},
{"name": "Tag2"}
]
}
}
```
### Status Value
```json
{
"Project Status": {
"status": {"name": "In progress"}
}
}
```
### Date Value
Single date:
```json
{
"Due Date": {
"date": {
"start": "2024-12-31"
}
}
}
```
Date with time:
```json
{
"Meeting": {
"date": {
"start": "2024-12-31T14:00:00.000Z",
"time_zone": "America/New_York"
}
}
}
```
Date range:
```json
{
"Sprint": {
"date": {
"start": "2024-01-01",
"end": "2024-01-14"
}
}
}
```
### Checkbox Value
```json
{
"Complete": {
"checkbox": true
}
}
```
### URL Value
```json
{
"Website": {
"url": "https://example.com"
}
}
```
### Email Value
```json
{
"Contact": {
"email": "[email protected]"
}
}
```
### Phone Number Value
```json
{
"Phone": {
"phone_number": "+1-555-123-4567"
}
}
```
### People Value
```json
{
"Assignee": {
"people": [
{"id": "user-uuid"}
]
}
}
```
Maximum 100 users.
### Files Value
External files:
```json
{
"Attachments": {
"files": [
{
"name": "Document.pdf",
"type": "external",
"external": {"url": "https://example.com/doc.pdf"}
}
]
}
}
```
Uploaded files (use file upload API first):
```json
{
"Attachments": {
"files": [
{
"name": "Photo.jpg",
"type": "file",
"file": {"url": "https://..."}
}
]
}
}
```
**Note**: Updating files property replaces all existing files.
### Relation Value
```json
{
"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:
```json
{
"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:
```json
{
"Full Name": {
"type": "formula",
"formula": {
"type": "string",
"string": "John Doe"
}
}
}
```
Formula result types: `string`, `number`, `boolean`, `date`
### Created Time Value (Read-Only)
```json
{
"Created": {
"type": "created_time",
"created_time": "2024-01-01T00:00:00.000Z"
}
}
```
### Created By Value (Read-Only)
```json
{
"Creator": {
"type": "created_by",
"created_by": {
"object": "user",
"id": "user-uuid"
}
}
}
```
### Last Edited Time Value (Read-Only)
```json
{
"Updated": {
"type": "last_edited_time",
"last_edited_time": "2024-01-15T12:00:00.000Z"
}
}
```
### Last Edited By Value (Read-Only)
```json
{
"Editor": {
"type": "last_edited_by",
"last_edited_by": {
"object": "user",
"id": "user-uuid"
}
}
}
```
### Unique ID Value (Read-Only)
```json
{
"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:
```bash
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_time`
- `created_by`
- `last_edited_time`
- `last_edited_by`
- `rollup`
- `formula`
- `unique_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`:
```json
{
"Due Date": {
"date": null
}
}
```
For rich text/title, use an empty array:
```json
{
"Description": {
"rich_text": []
}
}
```
references/rich-text.md›
# 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:
```json
{
"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:
```json
{
"type": "text",
"text": {
"content": "Link text",
"link": {"url": "https://example.com"}
},
"annotations": { /* ... */ },
"plain_text": "Link text",
"href": "https://example.com"
}
```
Without link:
```json
{
"type": "text",
"text": {
"content": "Plain text"
},
"annotations": { /* ... */ },
"plain_text": "Plain text",
"href": null
}
```
### Equation
Inline mathematical expressions using LaTeX/KaTeX:
```json
{
"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
```json
{
"type": "mention",
"mention": {
"type": "user",
"user": {
"object": "user",
"id": "user-uuid",
"name": "John Doe",
"avatar_url": "https://...",
"type": "person",
"person": {"email": "[email protected]"}
}
},
"annotations": { /* ... */ },
"plain_text": "@John Doe",
"href": "https://www.notion.so/user-uuid"
}
```
#### Page Mention
```json
{
"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
```json
{
"type": "mention",
"mention": {
"type": "database",
"database": {
"id": "database-uuid"
}
},
"annotations": { /* ... */ },
"plain_text": "Database Title",
"href": "https://www.notion.so/database-uuid"
}
```
#### Date Mention
```json
{
"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:
```json
{
"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
```json
{
"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:
```json
{
"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:
```json
{
"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:
```json
{
"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:
- `default`
- `gray`
- `brown`
- `orange`
- `yellow`
- `green`
- `blue`
- `purple`
- `pink`
- `red`
Background colors:
- `gray_background`
- `brown_background`
- `orange_background`
- `yellow_background`
- `green_background`
- `blue_background`
- `purple_background`
- `pink_background`
- `red_background`
---
## Creating Rich Text
### Simple Text
```json
[
{
"type": "text",
"text": {"content": "Simple text"}
}
]
```
### Formatted Text
```json
[
{
"type": "text",
"text": {"content": "Bold and italic"},
"annotations": {"bold": true, "italic": true}
}
]
```
### Mixed Formatting
```json
[
{"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
```json
[
{"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
```json
[
{"type": "text", "text": {"content": "Use the "}},
{"type": "text", "text": {"content": "console.log()"}, "annotations": {"code": true}},
{"type": "text", "text": {"content": " function."}}
]
```
### Colored Text
```json
[
{
"type": "text",
"text": {"content": "Important!"},
"annotations": {"color": "red", "bold": true}
}
]
```
### With Background Color
```json
[
{
"type": "text",
"text": {"content": "Highlighted text"},
"annotations": {"color": "yellow_background"}
}
]
```
### User Mention
```json
[
{"type": "text", "text": {"content": "Assigned to "}},
{
"type": "mention",
"mention": {
"type": "user",
"user": {"id": "user-uuid"}
}
}
]
```
### Page Mention
```json
[
{"type": "text", "text": {"content": "See also: "}},
{
"type": "mention",
"mention": {
"type": "page",
"page": {"id": "page-uuid"}
}
}
]
```
### Date Mention
```json
[
{"type": "text", "text": {"content": "Due: "}},
{
"type": "mention",
"mention": {
"type": "date",
"date": {"start": "2024-12-31"}
}
}
]
```
### Inline Equation
```json
[
{"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):
```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):
```bash
# 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}]'
```
SKILL.md›
---
name: notion-api
description: >
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.
---
# 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:
```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" | jq
```
## Base 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 `null` instead 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-After` header
- **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
}' | jq
```
Filter values: `"page"` or `"data_source"` (or omit for both)
### 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" | jq
```
**Note**: 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"}}]
}
}
]
}' | 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+)
#### 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
}' | jq
```
Additional update options: `cover`, `is_locked`, `in_trash`
#### Archive (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}' | jq
```
#### Retrieve 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" | jq
```
### Blocks (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" | jq
```
Use the page ID as `block_id` to get page content. Check `has_children` on each block for nested content.
#### Append 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"}}]
}
}
]
}' | jq
```
Maximum 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
```bash
curl -s "https://api.notion.com/v1/blocks/{block_id}" \
-H "Authorization: Bearer $NOTION_API_TOKEN" \
-H "Notion-Version: 2025-09-03" | jq
```
#### Update 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"}}]
}
}' | jq
```
The 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" | jq
```
Moves 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" | jq
```
Returns 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
}' | jq
```
See `references/filters-and-sorts.md` for comprehensive filter and sort documentation.
#### Create 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": {}}
}
}
}' | jq
```
#### Update 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"}}]
}' | jq
```
### Data 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": {}}
}
}' | jq
```
### Users
#### 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" | jq
```
#### Retrieve 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" | jq
```
#### Retrieve 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" | jq
```
### Comments
#### 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" | jq
```
Use a page ID as `block_id` for page-level comments.
#### Create 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"}}]
}' | jq
```
Reply 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"}}]
}' | jq
```
**Note**: The API cannot start new inline discussion threads or edit/delete existing comments.
## Pagination
Paginated endpoints return:
- `has_more`: Boolean indicating more results exist
- `next_cursor`: Cursor for the next page
- `results`: 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:
```json
{
"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 structures
- `references/property-types.md` - Database property types and value formats
- `references/filters-and-sorts.md` - Database query filter and sort syntax
- `references/rich-text.md` - Rich text object structure and annotations