# notion_to_md (Python)
Convert Notion pages to clean, readable Markdown. Python port of [notion-to-md](https://github.com/souvikinator/notion-to-md).
## Quick Start
1. Install the package:
```bash
pip install notion_to_md
```
2. Basic usage:
```python
from notion_client import AsyncClient
from notion_to_md import NotionToMarkdown
async def convert_page(notion_token: str, page_id: str) -> str:
# Initialize clients
notion = AsyncClient(auth=notion_token)
n2m = NotionToMarkdown(notion_client=notion)
# Convert page to markdown
md_blocks = await n2m.page_to_markdown(page_id)
md_string = await n2m.to_markdown_string(md_blocks)
return md_string['parent'] # Returns the main page content
# Usage
result = await convert_page("your-notion-token", "your-page-id")
print(result)
```
## Features
- 🎯 Supports all common Notion blocks (text, lists, tables, code, etc.)
- 📝 Preserves formatting (bold, italic, code, colors)
- 🖼️ Handles images (with optional base64 conversion)
- 📑 Supports nested content (child pages, synced blocks)
- ⚡ Modern async/await API
- 🔁 Built-in error handling and API retry logic
## Configuration
Control the converter's behavior with configuration options:
```python
from notion_to_md import NotionToMarkdown, ConfigurationOptions
n2m = NotionToMarkdown(
notion_client=notion,
config=ConfigurationOptions(
# Core Options
separate_child_page=False, # True to split child pages into separate files
parse_child_pages=True, # False to skip child pages entirely
# Image Handling
convert_images_to_base64=False, # True to embed images as base64
# API Behavior
api_retry_attempts=3, # Number of retries for failed API calls
api_rate_limit_delay=0.5, # Delay between API calls
max_concurrent_requests=5, # Max concurrent API requests
# Debugging
debug_mode=False # Enable detailed error tracking
)
)
```
## Advanced Usage
### 1. Handling Child Pages
```python
# Convert page with child pages as separate files
md_blocks = await n2m.page_to_markdown(page_id)
md_string = await n2m.to_markdown_string(md_blocks)
# Main page content
main_content = md_string['parent']
# Child pages (if separate_child_page=True)
for page_id, content in md_string.items():
if page_id != 'parent':
# Each child page content
print(f"Child page {page_id}: {content}")
```
### 2. Custom Block Transformers
Add custom handling for specific block types:
```python
def custom_code_block(block):
"""Custom transformer for code blocks"""
if block["type"] != "code":
return False # Let default handler process it
language = block["code"].get("language", "")
code = "".join(text["plain_text"] for text in block["code"].get("rich_text", []))
return f"```{language}\n{code}\n```"
# Register the transformer
n2m.set_custom_transformer("code", custom_code_block)
```
### 3. Error Handling and Debugging
Enable debug mode for detailed error tracking:
```python
n2m = NotionToMarkdown(
notion_client=notion,
config=ConfigurationOptions(debug_mode=True)
)
# Convert your page
md_blocks = await n2m.page_to_markdown(page_id)
# Get debug information
debug_info = n2m.get_debug_info()
if debug_info:
print(f"Success rate: {debug_info['success_rate']}")
print(f"Errors: {len(debug_info['errors'])}")
print(f"Unhandled types: {debug_info['unhandled_types']}")
```
## Complete Example
Here's a full example that exports a Notion page to a Markdown file with metadata:
```python
async def export_notion_page(notion_token: str, page_id: str, output_file: str):
notion = AsyncClient(auth=notion_token)
n2m = NotionToMarkdown(notion_client=notion)
try:
# Get page metadata
page = await notion.pages.retrieve(page_id=page_id)
title = page.get('properties', {}).get('title', {}).get('title', [{}])[0].get('plain_text', 'Untitled')
# Convert to markdown
md_blocks = await n2m.page_to_markdown(page_id)
md_string = await n2m.to_markdown_string(md_blocks)
# Add metadata
content = f"""---
title: {title}
notion_url: {page.get('url')}
last_edited: {page.get('last_edited_time')}
---
{md_string['parent']}"""
# Save to file
with open(output_file, 'w', encoding='utf-8') as f:
f.write(content)
print(f"✓ Exported: {title} -> {output_file}")
except Exception as e:
print(f"Error: {str(e)}")
```
## Supported Content
### Block Types
- Text (paragraphs, headings)
- Lists (bulleted, numbered, to-do)
- Media (images, videos, files)
- Embeds (bookmarks, PDFs)
- Structural (tables, columns, toggles)
- Interactive (equations, code blocks)
- Organizational (child pages, databases)
### Text Formatting
- Basic (bold, italic, strikethrough)
- Code (inline, blocks)
- Colors (text and background)
- Links
- Equations
## Known Limitations
Some Notion features currently have limited support:
- **Toggle Blocks**: Basic support only. Nested toggles and complex content within toggles may not render correctly.
- **Synced Blocks**: Limited support. Content is preserved but syncing behavior is not fully implemented.
- **Tables**: Basic support for simple tables. Complex formatting and column headers may not render correctly.
We're actively working on improving support for these features. For the most up-to-date status, please check our issues page.
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
[MIT](LICENSE)
Raw data
{
"_id": null,
"home_page": null,
"name": "python_notion_to_md",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "Emre Coklar <emre.coklar@gmail.com>",
"keywords": "api-client, converter, markdown, notion",
"author": null,
"author_email": "Emre Coklar <emre.coklar@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/96/d9/a211520d47a5b1f7978657fc1d19e8bfed528028f36cdaf8b328342d60b7/python_notion_to_md-0.1.6.tar.gz",
"platform": null,
"description": "# notion_to_md (Python)\n\nConvert Notion pages to clean, readable Markdown. Python port of [notion-to-md](https://github.com/souvikinator/notion-to-md).\n\n## Quick Start\n\n1. Install the package:\n```bash\npip install notion_to_md\n```\n\n2. Basic usage:\n```python\nfrom notion_client import AsyncClient\nfrom notion_to_md import NotionToMarkdown\n\nasync def convert_page(notion_token: str, page_id: str) -> str:\n # Initialize clients\n notion = AsyncClient(auth=notion_token)\n n2m = NotionToMarkdown(notion_client=notion)\n \n # Convert page to markdown\n md_blocks = await n2m.page_to_markdown(page_id)\n md_string = await n2m.to_markdown_string(md_blocks)\n \n return md_string['parent'] # Returns the main page content\n\n# Usage\nresult = await convert_page(\"your-notion-token\", \"your-page-id\")\nprint(result)\n```\n\n## Features\n\n- \ud83c\udfaf Supports all common Notion blocks (text, lists, tables, code, etc.)\n- \ud83d\udcdd Preserves formatting (bold, italic, code, colors)\n- \ud83d\uddbc\ufe0f Handles images (with optional base64 conversion)\n- \ud83d\udcd1 Supports nested content (child pages, synced blocks)\n- \u26a1 Modern async/await API\n- \ud83d\udd01 Built-in error handling and API retry logic\n\n## Configuration\n\nControl the converter's behavior with configuration options:\n\n```python\nfrom notion_to_md import NotionToMarkdown, ConfigurationOptions\n\nn2m = NotionToMarkdown(\n notion_client=notion,\n config=ConfigurationOptions(\n # Core Options\n separate_child_page=False, # True to split child pages into separate files\n parse_child_pages=True, # False to skip child pages entirely\n \n # Image Handling\n convert_images_to_base64=False, # True to embed images as base64\n \n # API Behavior\n api_retry_attempts=3, # Number of retries for failed API calls\n api_rate_limit_delay=0.5, # Delay between API calls\n max_concurrent_requests=5, # Max concurrent API requests\n \n # Debugging\n debug_mode=False # Enable detailed error tracking\n )\n)\n```\n\n## Advanced Usage\n\n### 1. Handling Child Pages\n\n```python\n# Convert page with child pages as separate files\nmd_blocks = await n2m.page_to_markdown(page_id)\nmd_string = await n2m.to_markdown_string(md_blocks)\n\n# Main page content\nmain_content = md_string['parent']\n\n# Child pages (if separate_child_page=True)\nfor page_id, content in md_string.items():\n if page_id != 'parent':\n # Each child page content\n print(f\"Child page {page_id}: {content}\")\n```\n\n### 2. Custom Block Transformers\n\nAdd custom handling for specific block types:\n\n```python\ndef custom_code_block(block):\n \"\"\"Custom transformer for code blocks\"\"\"\n if block[\"type\"] != \"code\":\n return False # Let default handler process it\n \n language = block[\"code\"].get(\"language\", \"\")\n code = \"\".join(text[\"plain_text\"] for text in block[\"code\"].get(\"rich_text\", []))\n return f\"```{language}\\n{code}\\n```\"\n\n# Register the transformer\nn2m.set_custom_transformer(\"code\", custom_code_block)\n```\n\n### 3. Error Handling and Debugging\n\nEnable debug mode for detailed error tracking:\n\n```python\nn2m = NotionToMarkdown(\n notion_client=notion,\n config=ConfigurationOptions(debug_mode=True)\n)\n\n# Convert your page\nmd_blocks = await n2m.page_to_markdown(page_id)\n\n# Get debug information\ndebug_info = n2m.get_debug_info()\nif debug_info:\n print(f\"Success rate: {debug_info['success_rate']}\")\n print(f\"Errors: {len(debug_info['errors'])}\")\n print(f\"Unhandled types: {debug_info['unhandled_types']}\")\n```\n\n## Complete Example\n\nHere's a full example that exports a Notion page to a Markdown file with metadata:\n\n```python\nasync def export_notion_page(notion_token: str, page_id: str, output_file: str):\n notion = AsyncClient(auth=notion_token)\n n2m = NotionToMarkdown(notion_client=notion)\n \n try:\n # Get page metadata\n page = await notion.pages.retrieve(page_id=page_id)\n title = page.get('properties', {}).get('title', {}).get('title', [{}])[0].get('plain_text', 'Untitled')\n \n # Convert to markdown\n md_blocks = await n2m.page_to_markdown(page_id)\n md_string = await n2m.to_markdown_string(md_blocks)\n \n # Add metadata\n content = f\"\"\"---\ntitle: {title}\nnotion_url: {page.get('url')}\nlast_edited: {page.get('last_edited_time')}\n---\n\n{md_string['parent']}\"\"\"\n \n # Save to file\n with open(output_file, 'w', encoding='utf-8') as f:\n f.write(content)\n \n print(f\"\u2713 Exported: {title} -> {output_file}\")\n \n except Exception as e:\n print(f\"Error: {str(e)}\")\n```\n\n## Supported Content\n\n### Block Types\n- Text (paragraphs, headings)\n- Lists (bulleted, numbered, to-do)\n- Media (images, videos, files)\n- Embeds (bookmarks, PDFs)\n- Structural (tables, columns, toggles)\n- Interactive (equations, code blocks)\n- Organizational (child pages, databases)\n\n### Text Formatting\n- Basic (bold, italic, strikethrough)\n- Code (inline, blocks)\n- Colors (text and background)\n- Links\n- Equations\n\n## Known Limitations\n\nSome Notion features currently have limited support:\n\n- **Toggle Blocks**: Basic support only. Nested toggles and complex content within toggles may not render correctly.\n- **Synced Blocks**: Limited support. Content is preserved but syncing behavior is not fully implemented.\n- **Tables**: Basic support for simple tables. Complex formatting and column headers may not render correctly.\n\nWe're actively working on improving support for these features. For the most up-to-date status, please check our issues page.\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\n[MIT](LICENSE)",
"bugtrack_url": null,
"license": "MIT",
"summary": "Convert Notion pages to Markdown - Python port of notion-to-md",
"version": "0.1.6",
"project_urls": null,
"split_keywords": [
"api-client",
" converter",
" markdown",
" notion"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "e54d206424881a2bef2a0bdab4b9728e2530c5fd0147199401940596cfc1243a",
"md5": "5e95939a8aaf139507667d0dc048105d",
"sha256": "03b91faa23cb8102ea16efa2f200130c45b90fdca55c7fbd8aa085cb52cae068"
},
"downloads": -1,
"filename": "python_notion_to_md-0.1.6-py3-none-any.whl",
"has_sig": false,
"md5_digest": "5e95939a8aaf139507667d0dc048105d",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 19012,
"upload_time": "2025-02-04T05:04:15",
"upload_time_iso_8601": "2025-02-04T05:04:15.850725Z",
"url": "https://files.pythonhosted.org/packages/e5/4d/206424881a2bef2a0bdab4b9728e2530c5fd0147199401940596cfc1243a/python_notion_to_md-0.1.6-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "96d9a211520d47a5b1f7978657fc1d19e8bfed528028f36cdaf8b328342d60b7",
"md5": "ad5ec4b03d34a79971903cd01ffd0918",
"sha256": "0c968a7d278139be115ccd1b152aace9e49669454c1029869fb6b31e415c22a6"
},
"downloads": -1,
"filename": "python_notion_to_md-0.1.6.tar.gz",
"has_sig": false,
"md5_digest": "ad5ec4b03d34a79971903cd01ffd0918",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 19942,
"upload_time": "2025-02-04T05:04:17",
"upload_time_iso_8601": "2025-02-04T05:04:17.708139Z",
"url": "https://files.pythonhosted.org/packages/96/d9/a211520d47a5b1f7978657fc1d19e8bfed528028f36cdaf8b328342d60b7/python_notion_to_md-0.1.6.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-02-04 05:04:17",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "python_notion_to_md"
}