Skip to content

Fragment256/noteshift

NoteShift (noteshift)

NoteShift exports Notion content to Obsidian-friendly Markdown with predictable filenames, link rewriting, and checkpoint/resume support.

CI PyPI Python Versions codecov

Why it exists

Teams migrating from Notion to Obsidian consistently report four pains:

  1. broken internal links after export
  2. inconsistent filenames and folder layout
  3. long exports failing midway without resume
  4. low confidence in migration correctness

NoteShift is focused on solving those pains first.

Current capabilities

  • Export a Notion page tree to Markdown
  • Export Notion data sources/databases through API layer
  • Rewrite internal links for Obsidian compatibility
  • Preserve and download attachments
  • Resume interrupted runs via checkpoint file
  • Emit migration report (migration_report.json + .md)
  • Optionally emit YAML frontmatter with Notion metadata in each exported page

Documentation

Installation

Install from PyPI

uv tool install noteshift
# or
pipx install noteshift

Install from source (development)

uv tool install .
uv sync --extra dev --extra test

Authentication

Set a Notion integration token in NOTION_TOKEN.

export NOTION_TOKEN="secret_xxx"

Basic usage

noteshift export \
  --page-id "<notion-page-id>" \
  --out ./export \
  --max-depth 2 \
  --overwrite

Frontmatter

Pass --frontmatter to include YAML frontmatter at the top of every exported markdown file:

noteshift export \
  --page-id "<notion-page-id>" \
  --out ./export \
  --frontmatter

Each index.md will begin with a block like:

---
notionId: "abc12345-..."
notionUrl: "https://www.notion.so/..."
createdAt: "2024-01-01T10:00:00.000Z"
updatedAt: "2024-06-15T14:30:00.000Z"
title: "My Page"
---

For pages that live inside a Notion database, supported property types are also included as additional keys (select, multi_select, date, checkbox, number, url, email, phone_number, rich_text). Property names are lowercased and spaces are replaced with underscores.

Frontmatter is off by default so existing exports are unaffected.

Output

A successful run writes:

  • Markdown files for exported pages
  • downloaded assets in the export tree
  • .checkpoint.json for resume
  • migration_report.json
  • migration_report.md

Development

uv sync --extra dev --extra test
uv run ruff format .
uv run ruff check .
uv run mypy src
uv run pytest --cov=noteshift --cov-report=term

Contract tests (pytest-vcr)

Contract tests are deterministic and replay HTTP traffic from sanitized cassettes:

uv run pytest -m contract

To re-record cassettes intentionally, set a real token in your environment and run:

VCR_RECORD_MODE=once uv run pytest -m contract

License

MIT

About

Reliable notion to obsidian exports

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages