When to use Notion API vs. MCP
Notion API (REST) is mandatory for batch operations; MCP (Model Context Protocol) is for single-page CRUD only.
The problem with MCP for batch work
Most Notion MCP implementations have critical limitations for scaled operations:
- Hard cap on results: semantic search limited to ~25 results per query with no pagination mechanism
- No full database scan: cannot iterate over all rows in a database reliably
- Whack-a-mole pattern: multiple sequential queries required to retrieve complete data, leading to repeated context loading and inefficient execution
- No file uploads: many MCP versions do not support file upload operations
Real-world impact: A task requiring batch standardization of 65+ rows may take 4+ separate API calls via MCP (each capped at 25 results, no pagination), whereas the same task via Notion REST API with proper pagination completes in a single pass.
Decision tree
| Operation |
Recommended Tool |
Why |
| Create 1 page, read 1 page, update single entry, inline comment/annotation |
MCP |
Simpler setup, no auth management overhead |
| Query entire database, batch update (≥3 rows), full scan/inventory, any operation requiring pagination |
Notion REST API |
Full pagination support, no result caps, complete control |
| File upload (bulk or single) |
Notion REST API |
MCP rarely supports this; REST API has official file upload endpoints |
| Database creation or replication of page structure |
Notion REST API |
Programmatic structure building requires full API control |
Using Notion REST API
Prerequisites
- Notion API Token — create at https://www.notion.com/my-integrations and grant permissions to databases/pages you need.
- Store securely — save the token in environment variables (e.g.,
NOTION_TOKEN) or a credential vault; never hardcode.
- Pagination awareness — Notion returns max 100 items per request; use
start_cursor and next_cursor for full retrieval.
Implementation pattern
The Notion REST API covers pages (CRUD), databases (query with pagination), blocks (children enumeration), users, and comments. Rate limits are typically 3 requests/second; include backoff for 429 responses. Check the official API reference for the current version.
Example: Query a database with pagination
import os
import requests
NOTION_TOKEN = os.environ.get("NOTION_TOKEN")
DATABASE_ID = "your-database-id"
headers = {
"Authorization": f"Bearer {NOTION_TOKEN}",
"Notion-Version": "2024-06-15"
}
def query_db_paginated(db_id):
"""Retrieve all pages from a Notion database with automatic pagination."""
all_pages = []
start_cursor = None
while True:
payload = {}
if start_cursor:
payload["start_cursor"] = start_cursor
response = requests.post(
f"https://api.notion.com/v1/databases/{db_id}/query",
headers=headers,
json=payload
)
response.raise_for_status()
data = response.json()
all_pages.extend(data["results"])
if not data.get("has_more"):
break
start_cursor = data.get("next_cursor")
return all_pages
# Fetch and process
pages = query_db_paginated(DATABASE_ID)
for page in pages:
title = page["properties"]["Name"]["title"][0]["plain_text"]
print(f"Page: {title}")
Building abstractions
For repeated operations, create helper functions:
- Property builders: encapsulate property object construction (
title, select, date, relation, etc.)
- Block builders: standardize common blocks (
paragraph, heading, code, etc.)
- Auto-pagination wrappers: handle cursor management transparently
- Error handling: implement backoff for rate limits (429), transient errors (5xx), and validation errors (400)
Example helper:
def update_page_property(page_id, property_name, property_value):
"""Update a single property on a Notion page with error handling."""
payload = {
"properties": {
property_name: property_value
}
}
response = requests.patch(
f"https://api.notion.com/v1/pages/{page_id}",
headers=headers,
json=payload
)
if response.status_code == 429:
# Rate limited; back off and retry
raise Exception("Rate limited — implement exponential backoff")
response.raise_for_status()
return response.json()
Anti-patterns to avoid
- Using semantic search MCP for exhaustive queries: Semantic MCP always returns capped results. Use REST API database query with filters instead.
- Hand-rolling pagination instead of using official SDKs/wrappers: If your team/org has a Notion wrapper library, use it; reinventing auth + cursor management introduces bugs.
- Treating MCP as a complete Notion client: It's a convenience tool for simple read-write, not a replacement for the full API when scale matters.
- Ignoring rate limits: Notion enforces 3 requests/second per integration. Batch operations without backoff will fail; include exponential backoff by default.
- Assuming all properties/blocks are created equal: Some property types (rich text, rollups, formulas) require specific payload structures; consult Notion's API reference.
Notion API reference
- Official docs: https://developers.notion.com/reference/intro
- Latest API version: 2024-06 (check for updates when upgrading)
- Common endpoints:
POST /v1/databases/{database_id}/query — database query with filters and sorts
GET /v1/databases/{database_id} — fetch database schema
PATCH /v1/pages/{page_id} — update page properties
POST /v1/pages/{page_id}/children — append blocks to a page
GET /v1/blocks/{block_id}/children — fetch children of a block with pagination
Why this matters for AI agents
When an AI agent is tasked with Notion data work:
- Single operation ≠ batch: The agent should auto-detect when a task crosses the ≥3-row threshold and proactively propose REST API instead of MCP.
- Pagination is non-optional: Declaring "found all rows" without checking
has_more: false or consuming all cursors is a common failure mode.
- Deterministic structure: The REST API response structure is stable and fully documented; the agent can build confident parsers and helpers.
- Scalability: As task scope grows (backlog sync, portfolio updates, data migrations), the API scales without architectural rework.
This decision pattern applies universally to AI agents working with Notion at scale, not just a single operator's workflow.
1---2name: notion-batch-pattern3description: Use Notion REST API (not MCP) for batch operations (≥3 rows). Applies to database queries, batch updates, scans, pagination, file uploads, database creation, and page replication. MCP is acceptable only for single-page CRUD operations.4---56## When to use Notion API vs. MCP78**Notion API (REST) is mandatory for batch operations; MCP (Model Context Protocol) is for single-page CRUD only.**910### The problem with MCP for batch work1112Most Notion MCP implementations have critical limitations for scaled operations:1314- **Hard cap on results**: semantic search limited to ~25 results per query with no pagination mechanism15- **No full database scan**: cannot iterate over all rows in a database reliably16- **Whack-a-mole pattern**: multiple sequential queries required to retrieve complete data, leading to repeated context loading and inefficient execution17- **No file uploads**: many MCP versions do not support file upload operations1819**Real-world impact**: A task requiring batch standardization of 65+ rows may take 4+ separate API calls via MCP (each capped at 25 results, no pagination), whereas the same task via Notion REST API with proper pagination completes in a single pass.2021### Decision tree2223| Operation | Recommended Tool | Why |24|-----------|---|---|25| Create 1 page, read 1 page, update single entry, inline comment/annotation | MCP | Simpler setup, no auth management overhead |26| Query entire database, batch update (≥3 rows), full scan/inventory, any operation requiring pagination | **Notion REST API** | Full pagination support, no result caps, complete control |27| File upload (bulk or single) | **Notion REST API** | MCP rarely supports this; REST API has official file upload endpoints |28| Database creation or replication of page structure | **Notion REST API** | Programmatic structure building requires full API control |2930## Using Notion REST API3132### Prerequisites33341. **Notion API Token** — create at https://www.notion.com/my-integrations and grant permissions to databases/pages you need.352. **Store securely** — save the token in environment variables (e.g., `NOTION_TOKEN`) or a credential vault; never hardcode.363. **Pagination awareness** — Notion returns max 100 items per request; use `start_cursor` and `next_cursor` for full retrieval.3738### Implementation pattern3940The Notion REST API covers pages (CRUD), databases (query with pagination), blocks (children enumeration), users, and comments. Rate limits are typically 3 requests/second; include backoff for 429 responses. Check the official API reference for the current version.4142**Example: Query a database with pagination**4344```python45import os46import requests4748NOTION_TOKEN = os.environ.get("NOTION_TOKEN")49DATABASE_ID = "your-database-id"5051headers = {52 "Authorization": f"Bearer {NOTION_TOKEN}",53 "Notion-Version": "2024-06-15"54}5556def query_db_paginated(db_id):57 """Retrieve all pages from a Notion database with automatic pagination."""58 all_pages = []59 start_cursor = None60 61 while True:62 payload = {}63 if start_cursor:64 payload["start_cursor"] = start_cursor65 66 response = requests.post(67 f"https://api.notion.com/v1/databases/{db_id}/query",68 headers=headers,69 json=payload70 )71 response.raise_for_status()72 data = response.json()73 74 all_pages.extend(data["results"])75 76 if not data.get("has_more"):77 break78 start_cursor = data.get("next_cursor")79 80 return all_pages8182# Fetch and process83pages = query_db_paginated(DATABASE_ID)84for page in pages:85 title = page["properties"]["Name"]["title"][0]["plain_text"]86 print(f"Page: {title}")87```8889### Building abstractions9091For repeated operations, create helper functions:9293- **Property builders**: encapsulate property object construction (`title`, `select`, `date`, `relation`, etc.)94- **Block builders**: standardize common blocks (`paragraph`, `heading`, `code`, etc.)95- **Auto-pagination wrappers**: handle cursor management transparently96- **Error handling**: implement backoff for rate limits (429), transient errors (5xx), and validation errors (400)9798Example helper:99```python100def update_page_property(page_id, property_name, property_value):101 """Update a single property on a Notion page with error handling."""102 payload = {103 "properties": {104 property_name: property_value105 }106 }107 108 response = requests.patch(109 f"https://api.notion.com/v1/pages/{page_id}",110 headers=headers,111 json=payload112 )113 114 if response.status_code == 429:115 # Rate limited; back off and retry116 raise Exception("Rate limited — implement exponential backoff")117 118 response.raise_for_status()119 return response.json()120```121122## Anti-patterns to avoid123124- **Using semantic search MCP for exhaustive queries**: Semantic MCP always returns capped results. Use REST API database query with filters instead.125- **Hand-rolling pagination instead of using official SDKs/wrappers**: If your team/org has a Notion wrapper library, use it; reinventing auth + cursor management introduces bugs.126- **Treating MCP as a complete Notion client**: It's a convenience tool for simple read-write, not a replacement for the full API when scale matters.127- **Ignoring rate limits**: Notion enforces 3 requests/second per integration. Batch operations without backoff will fail; include exponential backoff by default.128- **Assuming all properties/blocks are created equal**: Some property types (rich text, rollups, formulas) require specific payload structures; consult Notion's API reference.129130## Notion API reference131132- **Official docs**: https://developers.notion.com/reference/intro133- **Latest API version**: 2024-06 (check for updates when upgrading)134- **Common endpoints**:135 - `POST /v1/databases/{database_id}/query` — database query with filters and sorts136 - `GET /v1/databases/{database_id}` — fetch database schema137 - `PATCH /v1/pages/{page_id}` — update page properties138 - `POST /v1/pages/{page_id}/children` — append blocks to a page139 - `GET /v1/blocks/{block_id}/children` — fetch children of a block with pagination140141## Why this matters for AI agents142143When an AI agent is tasked with Notion data work:1441451. **Single operation ≠ batch**: The agent should auto-detect when a task crosses the ≥3-row threshold and proactively propose REST API instead of MCP.1462. **Pagination is non-optional**: Declaring "found all rows" without checking `has_more: false` or consuming all cursors is a common failure mode.1473. **Deterministic structure**: The REST API response structure is stable and fully documented; the agent can build confident parsers and helpers.1484. **Scalability**: As task scope grows (backlog sync, portfolio updates, data migrations), the API scales without architectural rework.149150This decision pattern applies universally to AI agents working with Notion at scale, not just a single operator's workflow.