Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
terrylica avatar

Notion Sdk

  • 124 installs
  • 62 repo stars
  • Updated August 3, 2026
  • terrylica/cc-skills

Use notion-sdk for development tasks

About

notion-sdk: A skill for development. This provides functionality for development workflows.

  • notion-sdk

Notion Sdk by the numbers

  • 124 all-time installs (skills.sh)
  • +1 installs in the week ending Jul 27, 2026 (Skillselion tracking)
  • Ranked #2,790 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/terrylica/cc-skills --skill notion-sdk

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs124
repo stars62
Last updatedAugust 3, 2026
Repositoryterrylica/cc-skills

What it does

Use notion-sdk for development tasks

Files

SKILL.mdMarkdownGitHub ↗

Notion SDK Skill

Control Notion programmatically using the official notion-client Python SDK. See PyPI for current version.

Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.

When to Use This Skill

Use this skill when:

  • Creating pages or databases in Notion via API
  • Querying Notion databases programmatically
  • Adding blocks (text, code, headings) to Notion pages
  • Automating Notion workflows with Python
  • Integrating external data sources with Notion

Preflight: Token Collection

Before any Notion API operation, collect the integration token:

AskUserQuestion(questions=[{
    "question": "Please provide your Notion Integration Token (starts with ntn_ or secret_)",
    "header": "Notion Token",
    "options": [
        {"label": "I have a token ready", "description": "Token from notion.so/my-integrations"},
        {"label": "Need to create one", "description": "Go to notion.so/my-integrations → New integration"}
    ],
    "multiSelect": false
}])

After user provides token:

1. Validate format (must start with ntn_ or secret_) 2. Test with validate_token() from scripts/notion_wrapper.py 3. Remind user: Each page/database must be shared with the integration

Quick Start

1. Create a Page in Database

from notion_client import Client
from scripts.create_page import (
    create_database_page,
    title_property,
    status_property,
    date_property,
)

client = Client(auth="ntn_...")
page = create_database_page(
    client,
    data_source_id="abc123...",  # Database ID
    properties={
        "Name": title_property("My New Task"),
        "Status": status_property("In Progress"),
        "Due Date": date_property("2025-12-31"),
    }
)
print(f"Created: {page['url']}")

2. Add Content Blocks

from scripts.add_blocks import (
    append_blocks,
    heading,
    paragraph,
    bullet,
    code_block,
    callout,
)

blocks = [
    heading("Overview", level=2),
    paragraph("This page was created via the Notion API."),
    callout("Remember to share the page with your integration!", emoji="⚠️"),
    heading("Tasks", level=3),
    bullet("First task"),
    bullet("Second task"),
    code_block("print('Hello, Notion!')", language="python"),
]
append_blocks(client, page["id"], blocks)

3. Query Database

from scripts.query_database import (
    query_data_source,
    checkbox_filter,
    status_filter,
    and_filter,
    sort_by_property,
)

# Find incomplete high-priority items
results = query_data_source(
    client,
    data_source_id="abc123...",
    filter_obj=and_filter(
        checkbox_filter("Done", False),
        status_filter("Priority", "High")
    ),
    sorts=[sort_by_property("Due Date", "ascending")]
)
for page in results:
    title = page["properties"]["Name"]["title"][0]["plain_text"]
    print(f"- {title}")

Available Scripts

ScriptPurpose
notion_wrapper.pyClient setup, token validation, retry wrapper
create_page.pyCreate pages, property builders
add_blocks.pyAppend blocks, block type builders
query_database.pyQuery, filter, sort, search

References

  • Property Types - All 24 property types with examples
  • Block Types - All block types with structures
  • Rich Text - Formatting, links, mentions
  • Pagination - Handling large result sets

Important Constraints

Rate Limits

  • 3 requests/second average (burst tolerated briefly)
  • Use api_call_with_retry() for automatic rate limit handling
  • 429 responses include Retry-After header

Authentication Model

  • Page-level sharing required (not workspace-wide)
  • User must explicitly add integration to each page/database:
  • Page → ... menu → Connections → Add connection → Select integration

API Version (v2.6.0+)

  • Uses data_source_id instead of database_id for multi-source databases
  • Legacy database_id still works for simple databases
  • Scripts handle both patterns automatically

Operations NOT Supported

  • Workspace settings modification
  • User permissions management
  • Template creation/management
  • Billing/subscription access

API Behavior Patterns

Insights discovered through integration testing (test citations for verification).

Rate Limiting & Retry Logic

api_call_with_retry() handles transient failures automatically:

Error TypeBehaviorWait Strategy
429 Rate LimitedRetriesRespects Retry-After header (default 1s)
500 Server ErrorRetriesExponential backoff: 1s, 2s, 4s
Auth/ValidationFails immediatelyNo retry

_Citation: test_client.py::TestRetryLogic (lines 146-193)_

Read-After-Write Consistency

Newly created blocks may not be immediately queryable. Add 0.5s minimum delay:

append_blocks(client, page_id, blocks)
time.sleep(0.5)  # Eventual consistency delay
children = client.blocks.children.list(page_id)

_Citation: test_integration.py::TestBlockAppend::test_retrieve_appended_blocks (line 298)_

v2.6.0 API Migration

Old PatternNew Pattern (v2.6.0+)
client.databases.query()client.data_sources.query()
filter: {"value": "database"}filter: {"value": "data_source"}

_Citation: test_integration.py::TestDatabaseQuery (line 110)_

Archive-Only Deletion

Pages cannot be permanently deleted via API - only archived (moved to trash):

client.pages.update(page_id, archived=True)  # Trash, not delete

_Citation: test_integration.py cleanup fixture (lines 72-76)_

Edge Cases & Validation

Property Builder Edge Cases

InputBehaviorValid?
Empty string ""Creates empty contentYes
Empty array []Clears multi-select/relationsYes
None for numberClears property valueYes
Zero 0Valid number (not falsy)Yes
Negative -42Valid numberYes
Unicode/emojiFully preservedYes

_Citation: test_property_builders.py::TestPropertyBuildersEdgeCases (lines 302-341)_

Input Validation Responsibility

Builders are intentionally permissive - validation happens at API level:

PropertyBuilder AcceptsAPI Validates
DateAny stringISO 8601 only
URLAny stringValid URL format
CheckboxTruthy valuesBoolean expected

Best Practice: Validate in your application before building properties.

_Citation: test_property_builders.py::TestPropertyBuildersInvalidInputs (lines 347-376)_

Token Validation

  • Case-sensitive: Only lowercase ntn_ and secret_ valid
  • Format check happens before API call (saves unnecessary requests)
  • Empty/whitespace tokens rejected immediately

_Citation: test_client.py::TestClientEdgeCases (lines 196-224)_

Query & Filter Patterns

Compound Filter Composition

# Empty compound (matches all)
and_filter()  # {"and": []}

# Deep nesting supported
and_filter(
    or_filter(filter_a, filter_b),
    and_filter(filter_c, filter_d)
)

_Citation: test_filter_builders.py::TestFilterEdgeCases (lines 323-360)_

Filter Limitations

Filters don't exclude NULL properties - check in Python:

if row["properties"]["Rating"]["number"] is not None:
    # Process non-null values

_Citation: test_integration.py::TestDatabaseQuery::test_query_database_with_filter (lines 120-135)_

Pagination Invariants

Conditionhas_morenext_cursor
More results existTruePresent, non-None
No more resultsFalseMay be absent/None

Always check has_more before using next_cursor.

_Citation: test_integration.py::TestDatabaseQuery::test_query_database_with_pagination (lines 137-151)_

Error Handling

from notion_client import APIResponseError, APIErrorCode

try:
    result = client.pages.create(...)
except APIResponseError as e:
    if e.code == APIErrorCode.ObjectNotFound:
        print("Page/database not found or not shared with integration")
    elif e.code == APIErrorCode.Unauthorized:
        print("Token invalid or expired")
    elif e.code == APIErrorCode.RateLimited:
        print(f"Rate limited. Retry after {e.additional_data.get('retry_after')}s")
    else:
        raise

Installation

uv pip install notion-client  # v2.6+ required for data_source support

Or use PEP 723 inline dependencies (scripts include them).

---

Troubleshooting

IssueCauseSolution
Object not foundPage not shared with integrationShare page: ... menu → Connections → Add integration
UnauthorizedToken invalid or expiredGenerate new token at notion.so/my-integrations
Rate limited (429)Too many requestsUse api_call_with_retry() for automatic handling
Empty results from queryFilter matches nothingVerify filter syntax and property names
Block not found after createEventual consistency delayAdd 0.5s delay after write before read
Invalid property typeWrong builder usedCheck property type in database schema
Token format rejectedWrong prefix (case-sensitive)Token must start with ntn_ or secret_ (lowercase)
Data source ID not workingOld API versionUpgrade notion-client to latest version

Post-Execution Reflection

After this skill completes, check before closing:

1. Did the command succeed? — If not, fix the instruction or error table that caused the failure. 2. Did parameters or output change? — If the underlying tool's interface drifted, update Usage examples and Parameters table to match. 3. Was a workaround needed? — If you had to improvise (different flags, extra steps), update this SKILL.md so the next invocation doesn't need the same workaround.

Only update if the issue is real and reproducible — not speculative.

Related skills

Backend & APIsbackendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.