Skip to main content

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

Project description

Notionary logo: dark mode shows a white logo, light mode shows a black logo.

The Modern Notion API for Python & AI Agents

PyPI version Python Version License: MIT Downloads Documentation Notion API

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


Why Notionary?

  • AI-friendly – Composable APIs that drop cleanly into agent workflows
  • Smart discovery – Find pages/databases by title with fuzzy matching (no ID spelunking)
  • Markdown content – Read & write page content as Markdown via the Notion Markdown API
  • Async-first – Modern Python with full async / await
  • Round-trip content – Read a page as Markdown, transform, write back
  • Full coverage – Pages, databases, data sources, file uploads, users, workspace search

Installation

pip install notionary

Set up your Notion integration and configure your token:

export NOTION_API_KEY=your_integration_key

Quick Start

All access goes through the Notionary client:

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())

Architecture Overview

flowchart TD
  N[Notionary] --> PG[pages]
  N --> DB[databases]
  N --> DS[data_sources]
  N --> FU[file_uploads]
  N --> USR[users]
  N --> WS[workspace]

  PG --> P[Page]
  DB --> D[Database]
  DS --> S[DataSource]
  S --> P

The Notionary client exposes namespace objects – each mapping to a Notion API area. Content operations use the Notion Markdown API directly.


Core Concepts

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()
    await page.append("## New Section")
    await page.replace("# Replaced content")
    await page.clear()

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

    # Properties
    await page.properties.set_property("Status", "Done")

    # Comments
    await page.comment("Review completed")

    # Lifecycle
    await page.lock()
    await page.trash()

Notion API Reference: Pages · Markdown

Databases

async with Notionary() as notion:
    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

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

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

    # 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
    result = await notion.file_uploads.upload_from_bytes(
        content=image_bytes,
        filename="chart.png",
    )

    # List 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")

Workspace Search

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/databases by name
  • Fuzzy matching for approximate searches
  • No more hunting for IDs or URLs

Markdown Content API

  • Read page content as Markdown
  • Append, replace, or clear content
  • Powered by the official Notion Markdown API

Modern Python

  • Full async/await support
  • Type hints throughout
  • Pydantic models for API responses

Round-Trip Editing

  • Read existing content as Markdown
  • Edit and modify
  • Write back to Notion seamlessly

AI-Ready Architecture

  • Predictable models enable prompt chaining
  • Ideal for autonomous content generation
  • Clean namespace-based API

Complete Coverage

  • Pages, databases, data sources
  • File uploads with automatic handling
  • Users and workspace search

MCP Server

Notionary ships a built-in Model Context Protocol server so AI agents can manage your Notion workspace out of the box.

pip install notionary[mcp]

Use with the OpenAI Agents SDK

import asyncio
import sys

from agents import Agent, Runner
from agents.mcp import MCPServerStdio


async def main():
    async with MCPServerStdio(
        name="Notionary",
        params={
            "command": sys.executable,
            "args": ["-m", "notionary.mcp.server"],
        },
    ) as server:
        agent = Agent(
            name="Notion Assistant",
            instructions="You help users manage their Notion workspace.",
            mcp_servers=[server],
        )

        result = await Runner.run(agent, "Search my workspace and list what you find.")
        print(result.final_output)

asyncio.run(main())

Use with Claude Desktop / Claude Code

Add to your MCP config:

{
  "mcpServers": {
    "notionary": {
      "command": "notionary-mcp",
      "env": {
        "NOTION_API_KEY": "your_integration_key"
      }
    }
  }
}

Available Tools

Area Tools
Workspace search_workspace
Pages list_pages, find_page, get_page_content, get_page_comments, update_page, append_to_page, replace_page_content, clear_page, comment_on_page, rename_page, set_page_property, trash_page, restore_page, lock_page, unlock_page
Data Sources list_data_sources, find_data_source, get_data_source_schema, create_page_in_data_source, update_data_source, list_data_source_templates, trash_data_source, restore_data_source
Databases list_databases, find_database, create_database, update_database, trash_database, restore_database
Users list_users, search_users, get_me

Full Documentation

mathisarends.github.io/notionary – Complete API reference with auto-generated docs from source code


Contributing

We welcome contributions from the community! Whether you're:

  • Fixing bugs - Help improve stability and reliability
  • Adding features - Extend functionality for new use cases
  • Improving docs - Make the library more accessible
  • Sharing examples - Show creative applications and patterns

Check our Contributing Guide to get started.


Ready to revolutionize your Notion workflows?

📖 Read the Docs · 💻 Browse Examples

Built with ❤️ for Python developers and AI agents

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

notionary-0.6.1.tar.gz (17.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

notionary-0.6.1-py3-none-any.whl (75.2 kB view details)

Uploaded Python 3

File details

Details for the file notionary-0.6.1.tar.gz.

File metadata

  • Download URL: notionary-0.6.1.tar.gz
  • Upload date:
  • Size: 17.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.2

File hashes

Hashes for notionary-0.6.1.tar.gz
Algorithm Hash digest
SHA256 42d85a701648a6d5f727417f067a8d7e810a6167b9d1c275427154d0b133cd19
MD5 453bbec9e7f514050ea2e5aa72c5bed9
BLAKE2b-256 c86cbc2785cba1f4d905a4eab360acd445a454e73d6f880b193b4055e1936e5f

See more details on using hashes here.

File details

Details for the file notionary-0.6.1-py3-none-any.whl.

File metadata

  • Download URL: notionary-0.6.1-py3-none-any.whl
  • Upload date:
  • Size: 75.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.2

File hashes

Hashes for notionary-0.6.1-py3-none-any.whl
Algorithm Hash digest
SHA256 cf6afee8b33f307f871fcbf1638150b61d8d5c4ac32a33cd58ca2eee017db6bf
MD5 0fb4bb3863888b471839bb51863327e4
BLAKE2b-256 75001cf3843443c26c43b9dc308a45e489b6070c552e4141950ec1cbfdecb01c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page