---
name: notion
description: Use when building integrations with Notion workspaces, creating
  pages and databases programmatically, syncing external data, building AI agent
  tools, receiving webhooks, or connecting AI assistants to Notion through MCP.
  Use for automations, data migrations, workspace management, and extending
  Notion with custom capabilities.
metadata:
  mintlify-proj: notion
  version: "1.0"
---

# Notion API Skill

## Product summary

The Notion API lets you build integrations that create pages, manage databases, sync external data, and extend Notion with custom tools. Send HTTPS requests to `https://api.notion.com` with a `Notion-Version` header (latest: `2026-03-11`) and an `Authorization: Bearer` token. Use the official JavaScript SDK (`@notionhq/client`), curl, or any HTTP client. Key resources: personal access tokens (PATs) for personal automations, internal connections for team tools, OAuth integrations for user-authorized apps, and Notion Workers for serverless syncs, webhooks, and agent tools. The CLI (`ntn`) scaffolds and deploys workers. See [API reference](https://developers.notion.com/reference/intro) for all endpoints.

## When to use

Reach for this skill when:
- **Creating or updating pages/databases** — building a form submission handler, syncing data into Notion, or automating page creation
- **Querying workspace content** — searching for pages, filtering database entries, or reading page properties
- **Building integrations** — connecting Notion to external services (Slack, GitHub, Stripe, etc.)
- **Deploying serverless functions** — syncing external data on a schedule, handling webhooks, or giving Notion AI custom tools
- **Connecting AI assistants** — using Notion MCP to let Claude, ChatGPT, or Gemini manage Notion workspaces
- **Managing agents programmatically** — querying Custom Agents, starting sessions, tracking usage, or updating agent settings
- **Handling real-time events** — subscribing to webhooks for page updates, database changes, or comments
- **Uploading files** — attaching images, PDFs, or other media to pages or databases

## Quick reference

### Authentication methods

| Method | Use case | Setup |
|--------|----------|-------|
| Personal Access Token (PAT) | Scripts, CLI, Workers acting as you | Create in Developer portal, set `NOTION_API_TOKEN` env var |
| Internal connection | Team-owned workspace tools | Create in Developer portal, grant content access separately |
| OAuth integration | Apps where each user authorizes | Create integration, implement OAuth flow, users select pages |
| Public integration | Apps for any workspace (Marketplace) | Create integration, pass review, users authorize via OAuth |

### Essential headers

```
Authorization: Bearer {token}
Notion-Version: 2026-03-11
Content-Type: application/json
```

### Core endpoints (by task)

| Task | Endpoint | Method |
|------|----------|--------|
| Create a page | `POST /v1/pages` | Send markdown or block structure |
| Retrieve a page | `GET /v1/pages/{page_id}` | Get page metadata and properties |
| Update page content | `PATCH /v1/pages/{page_id}/children` | Append, replace, or delete blocks |
| Query a data source | `POST /v1/data_sources/{data_source_id}/query` | Filter, sort, paginate pages |
| Create a database | `POST /v1/databases` | Define schema and parent |
| Retrieve a data source | `GET /v1/data_sources/{data_source_id}` | Get schema and properties |
| Search workspace | `POST /v1/search` | Find pages by title |
| Create a comment | `POST /v1/comments` | Add discussion to pages |
| Upload a file | `POST /v1/files` | Attach media to pages/databases |
| List webhooks | `GET /v1/webhooks` | Retrieve subscription metadata |

### CLI commands

```bash
# Authenticate
ntn login

# Make API requests
ntn api POST /v1/pages -d '{"markdown":"# Hello"}'
ntn api GET /v1/pages/{page_id}

# Query data sources
ntn datasources query {data_source_id} --filter '{"property":"Status","select":{"equals":"Done"}}'

# Workers
ntn workers new my-worker
ntn workers deploy
ntn workers env set API_KEY=secret
```

### Markdown to blocks conversion

Use the `markdown` field when creating/updating pages for simplicity:

```json
{
  "markdown": "# Heading\n\nParagraph text\n\n- Bullet item\n- Another item\n\n```javascript\ncode block\n```"
}
```

The API converts markdown to block structure automatically. Use `children` array for precise control over formatting, colors, and block types.

## Decision guidance

### When to use PAT vs internal connection vs OAuth

| Scenario | Use PAT | Use internal connection | Use OAuth |
|----------|---------|------------------------|-----------|
| Script running as you | ✓ | | |
| Team automation with shared access | | ✓ | |
| App where each user authorizes | | | ✓ |
| Marketplace app for any workspace | | | ✓ (public) |
| CI/CD pipeline | ✓ | | |
| Slack bot for your workspace | | ✓ | |

### When to use markdown vs block structure

| Scenario | Use markdown | Use children |
|----------|-------------|--------------|
| Simple text, headings, lists | ✓ | |
| Need precise formatting, colors, toggles | | ✓ |
| Embedding databases or custom blocks | | ✓ |
| Quick prototyping | ✓ | |
| Production content with styling | | ✓ |

### When to use Workers vs direct API calls

| Scenario | Use Workers | Use direct API |
|----------|------------|----------------|
| Scheduled syncs (every 30 min+) | ✓ | |
| Webhooks from external services | ✓ | |
| Custom agent tools | ✓ | |
| One-off scripts or CLI tasks | | ✓ |
| Real-time integrations | | ✓ |

## Workflow

### 1. Set up authentication
- **For personal use:** Create a PAT in Developer portal, export as `NOTION_API_TOKEN`
- **For team tools:** Create an internal connection, grant content access to pages/databases
- **For user-authorized apps:** Create an OAuth integration, implement authorization flow

### 2. Understand the data model
- **Pages** are documents with content blocks and properties
- **Databases** are containers; **data sources** hold the schema and pages
- **Properties** define columns (title, number, date, select, etc.)
- **Blocks** are content elements (heading, paragraph, list, image, etc.)
- Retrieve a data source to see its schema before creating pages

### 3. Make your first request
```bash
curl -X POST https://api.notion.com/v1/pages \
  -H "Authorization: Bearer $NOTION_API_TOKEN" \
  -H "Notion-Version: 2026-03-11" \
  -H "Content-Type: application/json" \
  -d '{"markdown":"# Hello\n\nThis is a test page"}'
```

### 4. Handle pagination
- Check `has_more` in response
- Pass `next_cursor` as `start_cursor` in next request
- Continue until `has_more` is false

### 5. Implement error handling
- Catch HTTP 429 (rate limited) — respect `Retry-After` header
- Catch HTTP 503 (service unavailable) — retry with exponential backoff
- Check `code` field in error response for programmatic handling
- Log `request_id` for support tickets

### 6. Deploy (if using Workers)
```bash
ntn workers deploy
# Notion hosts and runs your syncs, tools, and webhooks
```

## Common gotchas

- **Missing Notion-Version header** — Always include it; defaults to old behavior if omitted
- **Confusing database ID with data source ID** — After API v2025-09-03, databases contain data sources. Use data source ID to query pages
- **Forgetting to share content** — PATs use your permissions; internal connections need explicit content access; OAuth users select pages during authorization
- **Markdown vs blocks confusion** — Markdown is simpler but less flexible; blocks give precise control. Don't mix them in the same request
- **Pagination cursors are opaque** — Don't parse or validate them; pass `next_cursor` verbatim as `start_cursor`
- **Rate limits are per-connection, not per-token** — Multiple tokens on the same connection share the rate limit budget
- **Webhook events are aggregated** — `page.content_updated` may batch multiple edits; use non-aggregated events like `comment.created` for testing
- **File URLs expire** — Pre-signed URLs from file blocks expire after a short period; download immediately
- **Template application is async** — After creating a page from a template, wait for `page.content_updated` webhook before assuming content is populated
- **Workspace block limits** — Free workspaces have limits on total blocks; check `restricted_resource` errors
- **API version changes** — Always test after upgrading `Notion-Version`; new versions may change response shapes

## Verification checklist

Before submitting work:

- [ ] Token is set and not expired (test with `GET /v1/users/me`)
- [ ] `Notion-Version` header is included and matches your code's expectations
- [ ] Content (pages/databases) is shared with the connection or token
- [ ] Request body matches the endpoint schema (use `ntn api --help` or reference docs)
- [ ] Pagination is handled (check `has_more` and loop if needed)
- [ ] Error responses are caught and logged with `request_id`
- [ ] Rate limits are respected (implement backoff for 429 responses)
- [ ] Markdown or block structure is correct (test with simple example first)
- [ ] File uploads use multipart form data, not JSON
- [ ] Webhook subscriptions are verified (test with non-aggregated events first)
- [ ] Worker code is deployed and logs are checked (`ntn workers logs`)

## Resources

- **Comprehensive navigation:** [https://developers.notion.com/llms.txt](https://developers.notion.com/llms.txt)
- **API Reference:** [https://developers.notion.com/reference/intro](https://developers.notion.com/reference/intro)
- **Data APIs guide:** [https://developers.notion.com/guides/data-apis/working-with-databases](https://developers.notion.com/guides/data-apis/working-with-databases)
- **Workers guide:** [https://developers.notion.com/workers/get-started/overview](https://developers.notion.com/workers/get-started/overview)

---

> For additional documentation and navigation, see: https://developers.notion.com/llms.txt