Installs 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 with every re-scan.
[](https://www.skillsdirectory.com/skills/theheavenlyd3mon-notion-api-basics)
---
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/<profile>/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