Notion API fundamentals: authentication, curl patterns, property types, API version differences, and troubleshooting.
Scanned 9/10/2026
Install to Claude Code
npx -y skills add theheavenlyd3mon/hermes-profiles --skill notion-api-basics --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Notion Api Basics?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/theheavenlyd3mon-notion-api-basics-hermes-profiles)More formats (shields.io, HTML) on the badges page.
---
name: notion-api-basics
description: "Notion API fundamentals: authentication, curl patterns, property types, API version differences, and troubleshooting."
version: 1.0.0
author: hermes
license: MIT
platforms: [linux, macos, windows]
metadata:
hermes:
tags: [Notion, API, Authentication, Integration]
homepage: https://developers.notion.com
related_skills: [notion-databases, notion-pages, notion-blocks, notion-search]
prerequisites:
env_vars: [NOTION_API_KEY]
---
# Notion API Basics
Foundation skill for all Notion API operations. Covers authentication, the base curl pattern, property types, API version differences, and common troubleshooting.
## Prerequisites
1. Create an integration at https://notion.so/my-integrations
2. Copy the API key (starts with `ntn_` or `secret_`)
3. Store it in `~/.hermes/.env`:
```bash
echo 'NOTION_API_KEY=ntn_your_key_here' >> ~/.hermes/.env
```
4. **Important:** Share target pages/databases with your integration in Notion (click "..." → "Connect to" → your integration name)
5. Find a page/database ID: open it in Notion, copy the URL — the ID is the UUID segment (32 hex chars, dashes optional)
### Pitfall: Check the key isn't commented out
```bash
grep '^NOTION_API_KEY=' ~/.hermes/.env
```
If you see `# NOTION_API_KEY=...` (commented out), uncomment it:
```bash
sed -i '' 's/^# NOTION_API_KEY=/NOTION_API_KEY=/' ~/.hermes/.env
```
The `.env` file is write-protected from Hermes tools — use `sed` via terminal.
### Pitfall: Terminal sandbox points HOME elsewhere
When running inside a profile (e.g. Senna), `$HOME` resolves to `~/.hermes/profiles/senna/home` — NOT `~`. This means `source ~/.hermes/.env` fails. Always use the absolute path:
```bash
source ~/.hermes/.env
```
This applies to cron jobs and terminal() calls inside the profile sandbox.
## Authentication Options
| Method | What it is | Page sharing needed? | Workspace scope |
|--------|-----------|----------------------|-----------------|
| **Internal Integration** (recommended) | A bot with its own permissions | ✅ Share each page/database with the bot explicitly | One workspace |
| **PAT (Personal Access Token)** | Acts as *you* — has your existing permissions | ❌ No sharing needed — sees what you see | One user in one workspace |
| **OAuth 2.0** | For apps distributed to other people's workspaces | ❌ Users pick pages during install | Any workspace |
**Recommendation:** Start with an **Internal Integration** unless the user explicitly asks for a PAT.
## Base curl Pattern
All requests follow this pattern:
```bash
curl -s -X GET "https://api.notion.com/v1/..." \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" \
-H "Content-Type: application/json"
```
Key points:
- `Notion-Version` header is **required** — this skill uses `2025-09-03` (latest)
- Add `-s` to suppress curl progress bars (cleaner output)
- Pipe through `jq` for readable JSON: `| jq '.results[0].properties'`
- For quick debugging: `| head -c 500` or `| python3 -m json.tool | head -40`
## Property Types Reference
When creating or updating database entries (pages), properties follow these formats:
| Type | JSON Format |
|------|------------|
| **Title** | `{"title": [{"text": {"content": "..."}}]}` |
| **Rich text** | `{"rich_text": [{"text": {"content": "..."}}]}` |
| **Select** | `{"select": {"name": "Option"}}` |
| **Multi-select** | `{"multi_select": [{"name": "A"}, {"name": "B"}]}` |
| **Date** | `{"date": {"start": "2026-01-15", "end": null}}` |
| **Checkbox** | `{"checkbox": true}` |
| **Number** | `{"number": 42}` |
| **URL** | `{"url": "https://..."}` |
| **Email** | `{"email": "user@example.com"}` |
| **Relation** | `{"relation": [{"id": "page_id"}]}` |
## Key API Version Differences (2025-09-03 vs older)
- **Databases → Data Sources:** Use `/v1/data_sources/` endpoints for queries and retrieval
- **Two IDs per database:**
- `database_id` — used when creating pages (`parent: {"database_id": "..."}`)
- `data_source_id` — used when querying (`POST /v1/data_sources/{id}/query`)
- **Search finds data sources:** Results return `"object": "data_source"` with their `data_source_id`
- **Creating databases:** `POST /v1/databases` with `initial_data_source.properties` (NOT top-level `properties`)
## Notes
- Page/database IDs are UUIDs (with or without dashes)
- Rate limit: ~3 requests/second average (bursts allowed)
- The API cannot set database view filters — that's UI-only
- Use `is_inline: true` when creating data sources to embed them in pages
- **No `DELETE /v1/databases/{id}` endpoint** — to remove a database, use the Notion UI
## Troubleshooting
### `object_not_found` (404)
Almost always a **permissions issue**, not a missing page. The integration hasn't been granted access.
**Fix:** In Notion, open the page → "..." → "Connect to" → select your integration name.
### `validation_error` — property doesn't exist
The property name in your JSON doesn't match the schema. Fetch actual property names:
```bash
source ~/.hermes/.env
DS_ID=$(curl -s "https://api.notion.com/v1/databases/{database_id}" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" | jq -r '.data_sources[0].id')
curl -s "https://api.notion.com/v1/data_sources/$DS_ID" \
-H "Authorization: Bearer $NOTION_API_KEY" \
-H "Notion-Version: 2025-09-03" | jq '.properties | keys'
```
### Properties silently dropped during database creation
If you used `POST /v1/databases` with top-level `properties` (old API pattern), the properties are silently dropped — only a default `"Name"` title property exists. Fix by PATCHing the data source (see notion-databases skill).
## Load-bearing URLs
- API docs: https://developers.notion.com
- Integrations: https://notion.so/my-integrations
- Reference API: https://developers.notion.com/reference/intro
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!