Skip to content
Back to skills

Notion

ASecurity

Search, read, create and update pages and databases in your Notion workspace via the Notion API. Use when the user asks to find, read, or write Notion pages, notes, docs, or database rows.

  • 71 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added September 3, 2026
developmentpythongoshellapidatabase

Works with

  • cursor
  • api

Security analysis

A100/100

Scanned October 7, 2026

npx -y skills add intentic/intentic --skill notion --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Notion?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Notion
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/intentic-notion/badge)](https://www.skillsdirectory.com/skills/intentic-notion)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: notion
description: Search, read, create and update pages and databases in your Notion workspace via the Notion API. Use when the user asks to find, read, or write Notion pages, notes, docs, or database rows.
---

# Notion (connected)

Token in `$NOTION_TOKEN`. Base is `$NOTION_API_URL/v1` (it stands in for https://api.notion.com/v1), and every call
carries three headers, so define this helper once per shell: `notion <METHOD> <path> [-d '<json>']`:

```sh
notion() { local m="$1" p="$2"; shift 2; curl -s -X "$m" -H "Authorization: Bearer $NOTION_TOKEN" \
  -H "Notion-Version: 2022-06-28" -H "Content-Type: application/json" \
  "$NOTION_API_URL/v1/$p" "$@"; }
```

The integration only sees pages and databases someone shared with it (page `•••` → `Connections`); sub-pages
inherit. **A 404 (`object_not_found`) therefore usually means "not shared", not "does not exist"**: report it
that way and ask the user to share the page rather than concluding it is gone.

- Who am I: `notion GET users/me | jq '{name, workspace: .bot.workspace_name}'`
- Search, titles only, across everything shared: `notion POST search -d '{"query":"<QUERY>","page_size":20}' | jq -c '.results[] | {id, object, url}'` (add `"filter":{"property":"object","value":"page"}` (or `"value":"database"`) to narrow; an empty query lists everything shared)
- A page's title: `notion GET pages/<PAGE_ID> | jq -r '[.properties[] | select(.type=="title") | .title[].plain_text] | join("")'`
- Read a page's content: `notion GET "blocks/<PAGE_ID>/children?page_size=100" | jq -c '.results[] | {id, type, has_children, text: ([.[.type].rich_text[]?.plain_text] | join(""))}'`, a block with `has_children: true` (toggle, sub-page, column) needs its own `blocks/<BLOCK_ID>/children` call
- Create a page: `notion POST pages -d '{"parent":{"page_id":"<PAGE_ID>"},"properties":{"title":{"title":[{"text":{"content":"<TITLE>"}}]}},"children":[{"object":"block","type":"paragraph","paragraph":{"rich_text":[{"text":{"content":"..."}}]}}]}'`
- Append content to a page: `notion PATCH "blocks/<PAGE_ID>/children" -d '{"children":[...]}'`, same block shapes; useful types: `paragraph`, `heading_1`/`2`/`3`, `bulleted_list_item`, `numbered_list_item`, `to_do` (plus `"checked":false`), `quote`, `divider` (empty `{}`), `code` (plus `"language":"python"`)
- A database's schema, the property names and types rows must match: `notion GET databases/<DB_ID> | jq '.properties | map_values(.type)'`
- Query a database: `notion POST "databases/<DB_ID>/query" -d '{"page_size":100}' | jq -c '.results[] | {id, url, props: (.properties | map_values(.[.type]))}'`, filter/sort in the body, e.g. `{"filter":{"property":"Status","select":{"equals":"Done"}},"sorts":[{"timestamp":"last_edited_time","direction":"descending"}]}`
- Add a database row: `notion POST pages -d '{"parent":{"database_id":"<DB_ID>"},"properties":{"Name":{"title":[{"text":{"content":"..."}}]},"Status":{"select":{"name":"In progress"}}}}'`, property keys are the schema's names, exactly
- Update page properties: `notion PATCH pages/<PAGE_ID> -d '{"properties":{...}}'` · move to trash: `-d '{"archived":true}'`
- Comment on a page: `notion POST comments -d '{"parent":{"page_id":"<PAGE_ID>"},"rich_text":[{"text":{"content":"..."}}]}'` · read comments: `notion GET "comments?block_id=<PAGE_ID>"`

Notes: an id is the 32 hex chars at the end of any Notion URL, hyphenated or not, both accepted, so users can
paste links. Search matches **titles only**; to find something by body text, read the likely pages. Paginate
with `has_more`/`next_cursor` → `start_cursor` (query param on GETs, body field on POSTs). One append call takes
at most 100 blocks and one rich-text item at most 2000 characters: chunk long writes. Rate limit averages 3
requests/second; on 429 honour `Retry-After`. Errors come as JSON `{code, message}`: quote `message` when
reporting, and remember `object_not_found` most often means unshared.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.