A Python package that automatically finds and migrates to PostgreSQL all databases in a Notion workspace.
Project description
notion2pg_bulk
- Automatically discovers and migrates all databases in a workspace regardless of their views or position in the page structure
- Preserves database descriptions and property descriptions
- Supports relation properties using PostgreSQL arrays
- Select field support: Single and multi-select options stored in separate lookup tables with foreign key constraints
- Complies with Notion API limits (3 requests/second average)
Installation
pip install notion2pg-bulk
Setup and Usage
Requirements
- a Notion API key connected to all the databased that you wish to migrate (note: Notion now allows to connect databases ditectly from the integration configuration page, under the 'Access' tab. For quickly connecting all databases in a workspace, just tick all top level pages.
- PostgreSQL database connection URL
Interactive Mode
On (Default):
- Dispays detected databases and fields and asks for confirmation before running the migration
- Progress bar
- Shows post-migration notes
Off:
(--quiet flag in CLI or set interactive_mode=False in Python API)
.run()runs migration directly and silently
CLI
Options:
--notion-token: Notion integration token (or setNOTION_TOKENenv var)--database-url: PostgreSQL connection string (or setDATABASE_URLenv var)--quiet: Run in non-interactive mode (skip validation steps and progress bars)--extract-page-content: Extract free-form content from page bodies (slower migration)
Examples:
# Interactive mode (default)
notion2pg-bulk --notion-token "your_token" --database-url "postgresql://..."
# Non-interactive mode (skip validation steps)
notion2pg-bulk --notion-token "your_token" --database-url "postgresql://..." --quiet
# Extract page content (slower but includes free-form content)
notion2pg-bulk --notion-token "your_token" --database-url "postgresql://..." --extract-page-content
# Non-interactive with page content extraction
notion2pg-bulk --notion-token "your_token" --database-url "postgresql://..." --quiet --extract-page-content
Python API:
from notion2pg_bulk import NotionMigrator
import sqlalchemy as sa
engine = sa.create_engine('postgresql://user:password@localhost/dbname')
migrator = NotionMigrator(
notion_token="your_notion_integration_token",
db_connection=engine,
interactive_mode=True,
extract_page_content=True,
)
migrator.run()
Property Type Mapping
| Notion Property | PostgreSQL Type | Notes |
|---|---|---|
| Title | TEXT |
Primary identifier |
| Rich Text | TEXT |
Preserved as markdown with formatting |
| Number | NUMERIC |
|
| Select | TEXT |
Single option value + lookup table with foreign key |
| Multi-select | TEXT[] |
Array of option values + lookup table with check constraints |
| Date | TIMESTAMP |
|
| Checkbox | BOOLEAN |
|
| URL | TEXT |
|
TEXT |
||
| Phone | TEXT |
|
| Relation | TEXT[] |
Array of related page IDs |
| People | TEXT[] |
Array of user IDs |
| Files | TEXT[] |
Array of file URLs |
| Created time | TIMESTAMP |
|
| Created by | TEXT |
User ID |
| Last edited time | TIMESTAMP |
|
| Last edited by | TEXT |
User ID |
| Formula | - | ⚠️ Skipped (not supported by Notion API) |
| Rollup | - | ⚠️ Skipped (not supported by Notion API) |
Additional Page Content Mode
The migrator can optionally extract free-form content from database pages. Extracted content is stored in the additional_page_content column as plain text.
migrator = NotionMigrator(
notion_token=token,
db_connection=engine,
extract_page_content=True # Default is false
)
Supported blocks:
- Paragraph text
- Headings (all levels)
- Bulleted and numbered lists
- Code blocks
- To-do items
- Callouts
- Toggle blocks
- Dividers
- Embedded databases (ID reference only)
Unsupported blocks:
- Images and media files
- Complex block types (equations, embeds, etc.)
- Block formatting and styling
- Page hierarchies and relationships
Post-migration analysis: When page content extraction is enabled, the migrator provides a comprehensive summary of:
- Unsupported block types found and their locations (grouped by record)
- Embedded databases that were referenced in page content but not connected to the integration (with full database IDs)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file notion2pg_bulk-0.1.0.tar.gz.
File metadata
- Download URL: notion2pg_bulk-0.1.0.tar.gz
- Upload date:
- Size: 14.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
67bdc104fb3cb0eb28e06f1ac1791a70ac9184496e8f817299d8b36f91f743d9
|
|
| MD5 |
14f07fff3f7eeb64ea3c4fc837a349f5
|
|
| BLAKE2b-256 |
6a6b9240816f67d4b673ce499534265d719f209a8266ed0707bc1bac3cd4696d
|
Provenance
The following attestation bundles were made for notion2pg_bulk-0.1.0.tar.gz:
Publisher:
publish.yml on benvigano/notion2pg_bulk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
notion2pg_bulk-0.1.0.tar.gz -
Subject digest:
67bdc104fb3cb0eb28e06f1ac1791a70ac9184496e8f817299d8b36f91f743d9 - Sigstore transparency entry: 423317967
- Sigstore integration time:
-
Permalink:
benvigano/notion2pg_bulk@a3a3d3e24175ed9ed3f95f42a922d4aa49c7388a -
Branch / Tag:
refs/tags/0.1.0 - Owner: https://github.com/benvigano
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a3a3d3e24175ed9ed3f95f42a922d4aa49c7388a -
Trigger Event:
release
-
Statement type:
File details
Details for the file notion2pg_bulk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: notion2pg_bulk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ee328f707369ba6518e03219bd91010e35fc8a432e0fee628ec82c278d32b10
|
|
| MD5 |
9bca8672bbfec7fe4dd26449172b682a
|
|
| BLAKE2b-256 |
6a0fdef6d837160d39cd6ccc272ac4226ee9a9c1dcf1f5b795f9d20b02ac6216
|
Provenance
The following attestation bundles were made for notion2pg_bulk-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on benvigano/notion2pg_bulk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
notion2pg_bulk-0.1.0-py3-none-any.whl -
Subject digest:
9ee328f707369ba6518e03219bd91010e35fc8a432e0fee628ec82c278d32b10 - Sigstore transparency entry: 423317976
- Sigstore integration time:
-
Permalink:
benvigano/notion2pg_bulk@a3a3d3e24175ed9ed3f95f42a922d4aa49c7388a -
Branch / Tag:
refs/tags/0.1.0 - Owner: https://github.com/benvigano
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a3a3d3e24175ed9ed3f95f42a922d4aa49c7388a -
Trigger Event:
release
-
Statement type: