Skip to main content

A high-level Python SDK for the Notion API with developer experience in mind.

Project description

Better Notion

A pythonic, object-oriented SDK for the Notion API.

PyPI Python Tests Coverage


🚀 Quick Start

pip install better-notion
import asyncio
from better_notion import NotionAPI

async def main():
    async with NotionAPI(auth="secret_...") as api:
        # Get a page
        page = await api.pages.get("page_id")
        print(page.properties)

        # List pages in a database
        async for page in api.pages.iterate("database_id"):
            print(page.title)

asyncio.run(main())

✨ Why Better Notion?

  • 🎯 Object-Oriented: Work with entities, not JSON dicts
  • 🔋 Type-Safe: Property builders for compile-time safety
  • 📄 Paginated: Automatic cursor-based pagination
  • 🔍 Powerful Search: Search across your entire workspace
  • ⚡ Async-First: Built with asyncio and httpx for performance

📦 Installation

SDK Installation

# With pip
pip install better-notion

# With uv
uv pip install better-notion

CLI Installation

# Install with CLI support
pip install better-notion[cli]

# Or install everything
pip install better-notion[all]

The CLI provides a notion command for interacting with Notion from the terminal, designed primarily for AI agents.

CLI Status: ⚠️ Experimental - Under active development


💻 CLI Usage

The Better Notion CLI provides a command-line interface for interacting with Notion.

Installation

pip install better-notion[cli]

Basic Commands

# Show version
notion --version

# Check authentication status
notion auth status

# Show help
notion --help

Response Format

All CLI commands return JSON for programmatic parsing:

{
  "success": true,
  "data": {
    "id": "page_123",
    "title": "My Page"
  },
  "meta": {
    "version": "0.4.0",
    "timestamp": "2025-01-26T10:00:00Z",
    "rate_limit": {
      "remaining": 48,
      "reset_at": "2025-01-26T10:01:00Z"
    }
  }
}

Error Handling

Errors are returned with structured codes:

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Page not found",
    "retry": false
  },
  "meta": {...}
}

Exit Codes

  • 0 - Success
  • 1 - Generic error
  • 2 - Invalid input
  • 3 - Authentication error
  • 4 - Rate limit exceeded
  • 5 - Not found
  • 6 - Conflict

Note: The CLI is currently in experimental stage. See CLI Documentation for more details.


🔑 Authentication

Create an integration at notion.so/my-integrations to get your API token.

from better_notion import NotionAPI

api = NotionAPI(auth="secret_...")

📚 Features

Entities

All Notion objects are represented as Python entities:

  • Pages - Create, read, update, delete, archive
  • Blocks - Manipulate content blocks
  • Databases - Query and manage databases
  • Users - Retrieve user information

Property Builders

Type-safe builders for all Notion property types:

from better_notion._api.properties import Title, Select, Date, Checkbox

properties = {
    **Title("My Page").build(),
    **Select("Status", "In Progress").build(),
    **Date("Due Date", "2025-01-15").build(),
    **Checkbox("Done", False).build(),
}

Automatic Pagination

Memory-efficient iteration over large datasets:

async for page in api.pages.iterate("database_id"):
    # Processes pages lazily, not all at once
    process(page)

💡 Usage Examples

Creating a Page

from better_notion import NotionAPI
from better_notion._api.properties import Title, Text

async def create_page():
    async with NotionAPI(auth="secret_...") as api:
        page = await api.pages.create(
            parent={"database_id": "database_id"},
            properties={
                **Title("My New Page").build(),
                **Text("Notes", "Some notes").build(),
            }
        )
        print(f"Created page: {page.id}")

Updating a Page

from better_notion._api.properties import Select

async def update_page():
    async with NotionAPI(auth="secret_...") as api:
        page = await api.pages.get("page_id")

        # Update properties locally
        await page.update(
            **Select("Status", "Done").build()
        )

        # Save changes to Notion
        await page.save()

Working with Blocks

async def manipulate_blocks():
    async with NotionAPI(auth="secret_...") as api:
        page = await api.pages.get("page_id")

        # Get children blocks
        children = await page.blocks.children()

        # Append a new block
        await page.blocks.append(children=[
            {
                "object": "block",
                "type": "paragraph",
                "paragraph": {
                    "rich_text": [{"type": "text", "text": {"content": "New paragraph"}}]
                }
            }
        ])

Searching

async def search_pages():
    async with NotionAPI(auth="secret_...") as api:
        # Search for pages matching query
        async for result in api.search_iterate("my query"):
            if result["object"] == "page":
                print(result["properties"]["title"])

📖 API Reference

NotionAPI

The main client for interacting with Notion.

Methods:

  • search(query, *, filter, sort) - Search pages and blocks
  • search_iterate(query, *, filter, sort) - Iterate over search results

Collections:

  • api.pages - PageCollection
  • api.blocks - BlockCollection
  • api.databases - DatabaseCollection
  • api.users - UserCollection

Page

Properties:

  • id - Page ID
  • created_time - Creation datetime
  • last_edited_time - Last edited datetime
  • archived - Whether page is archived
  • properties - Page properties dict
  • blocks - BlockCollection for children

Methods:

  • await save() - Save changes to Notion
  • await delete() - Archive page
  • await reload() - Reload from Notion
  • await update(**kwargs) - Update properties locally

Block

Properties:

  • id - Block ID
  • type - Block type (paragraph, heading, etc.)
  • content - Block content

Methods:

  • await save() - Save changes to Notion
  • await delete() - Delete block
  • await reload() - Reload from Notion

Collections

PageCollection

  • await get(page_id) - Retrieve a page
  • await create(**kwargs) - Create a new page
  • await list(database_id, **kwargs) - List pages (first page)
  • iterate(database_id, **kwargs) - Iterate over all pages

BlockCollection

  • await get(block_id) - Retrieve a block
  • await children() - Get children blocks
  • await append(**kwargs) - Append new blocks

DatabaseCollection

  • await get(database_id) - Retrieve a database
  • await query(database_id, **kwargs) - Query a database
  • await create_page(database_id, **kwargs) - Create a page in database

UserCollection

  • await get(user_id) - Retrieve a user
  • await list() - List all users
  • await me() - Get current bot user

🤝 Contributing

Contributions are welcome! Please see CONTRIBUTING.md for details.

Development

# Clone the repository
git clone https://github.com/nesalia-inc/better-notion.git

# Install with dev dependencies
uv sync

# Run tests
uv run pytest

# Run with coverage
uv run pytest --cov=better_notion

📝 License

This project is licensed under the MIT License - see the LICENSE file for details.


🔗 Links


🙏 Acknowledgments

Built with ❤️ using:

Project details


Release history Release notifications | RSS feed

This version

2.1.6

Download files

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

Source Distribution

better_notion-2.1.6.tar.gz (232.0 kB view details)

Uploaded Source

Built Distribution

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

better_notion-2.1.6-py3-none-any.whl (233.4 kB view details)

Uploaded Python 3

File details

Details for the file better_notion-2.1.6.tar.gz.

File metadata

  • Download URL: better_notion-2.1.6.tar.gz
  • Upload date:
  • Size: 232.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for better_notion-2.1.6.tar.gz
Algorithm Hash digest
SHA256 0fcd5109ffbace6020ad34584ca96bb12af7f6dd14ff3377dd8d4938826eff86
MD5 551c923923e958a1dca24b8aad15f3da
BLAKE2b-256 0d928e5b677e3ec1bf36e173ecf87ba81f8edfff224132efe6d8aa5bb2e9306c

See more details on using hashes here.

File details

Details for the file better_notion-2.1.6-py3-none-any.whl.

File metadata

  • Download URL: better_notion-2.1.6-py3-none-any.whl
  • Upload date:
  • Size: 233.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for better_notion-2.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 29189170dabd1b77188ade02358ac090ecb7c46e860307f3d44aae740c76b000
MD5 bceacc1e1f4db8f472196ff2d4cb098a
BLAKE2b-256 70bad2f7efd372d8ce5cd994b2197aa18741093d30ee024483626c9e78900f34

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