Launch Week Day 5: Introducing Reachability for PHP.Learn More
Socket
Book a DemoSign in
Socket

notionary

Package Overview
Dependencies
Maintainers
1
Versions
73
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

notionary

Python library for programmatic Notion workspace management - databases, pages, and content with advanced Markdown support

pipPyPI
Version
0.5.0
Maintainers
1
Notionary logo

Notionary

PyPI version Python Version License: MIT Downloads Documentation Notion API

The Modern Notion API for Python & AI Agents

Transform complex Notion API interactions into simple, Pythonic code. Perfect for developers building AI agents, automation workflows, and dynamic content systems.

Why Notionary?

AI-friendlyComposable APIs that drop cleanly into agent workflows
Smart discoveryFind pages/databases by title with fuzzy matching — no ID spelunking
Markdown contentRead & write page content as Markdown via the Notion Markdown API
Async-firstModern Python with full async / await
Round-trip editingRead a page as Markdown, transform it, write it back
Full coveragePages, databases, data sources, file uploads, users, workspace search

Installation

pip install notionary

Set your Notion integration token:

export NOTION_API_KEY=your_integration_key

Quick Start

All access goes through the Notionary client, which exposes namespace objects — each mapping to a Notion API area.

import asyncio
from notionary import Notionary

async def main():
    async with Notionary() as notion:
        # Find a page by title (fuzzy matching)
        page = await notion.pages.find("Meeting Notes")
        print(page.title, page.url)

        # Read content as Markdown
        md = await page.get_markdown()
        print(md)

        # Append content
        await page.append("## Action Items\n- [ ] Review proposal")

        # Replace all content
        await page.replace("# Fresh Start\nThis page was rewritten.")

asyncio.run(main())

Core API

Pages

async with Notionary() as notion:
    # Lookup
    page = await notion.pages.find("Sprint Board")
    page = await notion.pages.from_id(page_uuid)

    # List & search
    pages = await notion.pages.list(query="roadmap")

Content (Markdown API)

    md = await page.get_markdown()       # read as Markdown
    await page.append("## New Section") # append blocks
    await page.replace("# Replaced")   # overwrite all content
    await page.clear()                  # remove all blocks

Metadata

    await page.rename("New Title")
    await page.set_icon("🚀")
    await page.set_cover("https://example.com/cover.png")
    await page.random_cover()

Properties

    # Set a single property (type-validated against the schema)
    await page.set_property("Status", "Done")
    await page.set_property("Due Date", "2025-12-31")
    await page.set_property("Priority", 3)
    await page.set_property("Archived", True)
    await page.set_property("Tags", ["backend", "urgent"])

    # Set multiple properties in one API call
    await page.set_properties({
        "Status": "In Progress",
        "Due Date": "2025-12-31",
        "Priority": 2,
    })

    # Inspect the property schema (types, current values, valid options)
    schema = await page.describe_properties()
    # schema["Status"] → PagePropertyDescription(type="status", current="Todo", options=["Todo", "In Progress", "Done"])

Supported property types: checkbox, date, email, multi_select, number, phone_number, rich_text, select, status, title, url, relation.

For relation properties on data-source pages, you can pass a page ID (UUID string) or a page title — the title is automatically resolved to an ID:

    # By page ID
    await page.set_property("Project", "abc123...")
    # By title (auto-resolved via the related data source)
    await page.set_property("Project", "Quarterly Review")

Comments & Lifecycle

    await page.comment("Review completed")
    await page.lock()
    await page.trash()

Notion API Reference: Pages · Markdown

Databases

async with Notionary() as notion:
    # Lookup
    db = await notion.databases.find("Tasks")
    db = await notion.databases.from_id(db_uuid)

    # Create
    db = await notion.databases.create(
        parent_page_id=page_uuid,
        title="New Database",
        icon_emoji="📊",
    )

    # Metadata
    await db.set_title("Project Tracker")
    await db.set_description("All current projects")
    await db.set_icon("📊")
    await db.lock()

Notion API Reference: Databases

Data Sources

Data sources represent queryable Notion databases with schema awareness — useful for building structured content pipelines.

async with Notionary() as notion:
    ds = await notion.data_sources.find("Engineering Backlog")

    # Schema introspection — property types, current options, and relation pages
    schema = await ds.describe_properties()
    # schema["Status"] → DataSourcePropertyDescription(type="status", options=["Todo", "In Progress", "Done"])
    # schema["Assignee"] → DataSourcePropertyDescription(type="relation", relation_options=[...])

    # Create a page inside the data source
    page = await ds.create_page(title="New Feature")

    # Query with filters
    results = await ds.query(filter={"property": "Status", "select": {"equals": "In Progress"}})

    # Metadata
    await ds.set_title("Sprint Board")
    await ds.set_icon("🧭")

Notion API Reference: Data Sources

File Uploads

from pathlib import Path

async with Notionary() as notion:
    # Upload from disk
    result = await notion.file_uploads.upload(Path("./report.pdf"))

    # Upload from bytes (e.g. generated images, in-memory content)
    result = await notion.file_uploads.upload_from_bytes(
        content=image_bytes,
        filename="chart.png",
    )

    # List previous uploads
    uploads = await notion.file_uploads.list()

Users

async with Notionary() as notion:
    all_users = await notion.users.list()
    people    = await notion.users.list(filter="person")
    bots      = await notion.users.list(filter="bot")
    me        = await notion.users.me()

    matches   = await notion.users.search("alex")
async with Notionary() as notion:
    results = await notion.workspace.search(query="roadmap")
    for r in results:
        print(type(r).__name__, r.title)

Key Features

Smart Discovery

  • Find pages and databases by human-readable name
  • Fuzzy matching handles typos and partial titles
  • No more hunting for opaque IDs or copying page URLs

Markdown Content API

  • Read any Notion page as clean Markdown
  • Append or replace blocks using Markdown syntax
  • Powered by the official Notion Markdown API

Round-Trip Editing

# Read → transform → write back
md = await page.get_markdown()
updated = md.replace("Draft", "Final")
await page.replace(updated)

Modern Python

  • Full async / await support throughout
  • Type hints on all public APIs
  • Pydantic models for all API responses
  • Context-manager client for clean resource handling

AI-Ready Architecture

  • Namespace-based API maps naturally to agent tool sets
  • Predictable response models enable prompt chaining
  • Works with LangChain, LlamaIndex, OpenAI Agents SDK, Claude, and custom agent runtimes

Contributing

Contributions are welcome — whether you're fixing bugs, adding features, improving docs, or sharing examples. Check the Contributing Guide to get started.

Documentation

mathisarends.github.io/notionary — Complete API reference auto-generated from source.

Built with ❤️ for Python developers and AI agents

FAQs

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts