Export Kobo Sage Advanced Notebooks to Obsidian-compatible Markdown
A command-line tool for extracting handwritten notes and sketches from the Kobo web portal and converting them to clean, organized Markdown files for use with Obsidian or any Markdown-based note-taking system.
- Kobo Integration: Seamlessly connects to your Kobo account via browser cookies (Firefox, Chrome, Chromium)
- Advanced Notebook Support: Extracts content from Kobo Sage "Advanced Notebooks" including handwritten text
- Text Extraction: Converts handwritten content to searchable text
- Markdown Conversion: Generates clean, properly formatted Markdown with YAML frontmatter
- Obsidian Integration:
- Vault-aware output paths
- Automatic wikilink suggestions based on existing notes
- Daily notes integration with customizable path patterns
- Tag suggestions from existing vault taxonomy
- Optional AI Enhancement:
- Text cleanup and formatting improvements
- Intelligent wikilink suggestions for proper nouns and concepts
- Tag recommendations based on content
- Support for Anthropic (Claude) and OpenAI APIs
- Batch Processing: Export single notebooks or all notebooks at once
- Flexible Output: Configure output paths, enable dry-run mode, interactive confirmation
- Rich CLI: Beautiful terminal output with progress indicators and tables
- Python: 3.11 or higher
- Kobo Account: An active Kobo account with notebooks created on Kobo Sage
- Browser: Firefox or Chrome/Chromium (for cookie extraction)
- Optional: Anthropic or OpenAI API key for AI enhancement features
Install the core package without AI features:
# Clone the repository
git clone https://github.com/lukepatrick/kobo-notebooks-markdown.git
cd kobo-notebooks-markdown
# Install with pip
pip install -e .To enable AI-powered text cleanup and wikilink suggestions:
pip install -e ".[ai]"This installs additional dependencies:
anthropic- For Claude API integrationopenai- For OpenAI API integration
For contributors and developers:
pip install -e ".[dev]"This includes testing and linting tools:
pytestandpytest-asyncio- Testing frameworkruff- Fast Python lintermypy- Static type checker
First, log in to kobo.com in your browser (Firefox or Chrome). Then extract your authentication cookies:
# For Firefox (default)
kobo-md auth login
# For Chrome
kobo-md auth login --browser chrome
# Verify authentication
kobo-md auth statusCookies are securely stored in ~/.config/kobo-md/cookies.json.
View all notebooks in your Kobo library:
kobo-md listThis displays a table with notebook IDs, titles, and preview availability.
Export a specific notebook to Markdown:
# Export by ID (supports prefix matching)
kobo-md fetch abc12345
# Export all notebooks
kobo-md fetch --all
# Export to custom location
kobo-md fetch abc12345 --output ~/Documents/NotesFor improved text quality and automatic wikilinks, enable AI processing:
# Set your API key
export ANTHROPIC_API_KEY=sk-ant-...
# or
export OPENAI_API_KEY=sk-...
# Export with AI processing
kobo-md fetch abc12345 --ai
# Configure AI settings
export KOBO_MD_AI_ENABLED=true
export KOBO_MD_AI_PROVIDER=anthropic
export KOBO_MD_AI_MODEL=claude-sonnet-4-20250514kobo-md --version # Show version
kobo-md --help # Show help messageExtract and save authentication cookies from your browser.
Options:
--browser, -b TEXT: Browser to extract from (firefox,chrome,chromium) [default: firefox]
Example:
kobo-md auth login --browser chromeYou must be logged in to kobo.com in your browser before running this command.
Check current authentication status and validate session.
Example:
kobo-md auth status
# Output: Session valid! Found 5 notebooks.Clear saved authentication cookies.
Example:
kobo-md auth clearList all notebooks in your Kobo library.
Example:
kobo-md listOutput:
┏━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓
┃ ID ┃ Title ┃ Preview ┃
┡━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩
│ abc12345... │ My Reading Notes │ Yes │
│ def67890... │ Project Ideas │ Yes │
└─────────────┴──────────────────┴─────────┘
Fetch and export notebooks to Markdown.
Usage:
kobo-md fetch [NOTEBOOK_ID] [OPTIONS]Arguments:
NOTEBOOK_ID: Notebook ID to fetch (supports prefix matching). Omit when using--all.
Options:
--all, -a: Fetch all notebooks--output, -o PATH: Output directory (overrides config)--overwrite, -f: Overwrite existing files--ai / --no-ai: Enable/disable AI processing (overrides config)--provider, -p TEXT: AI provider:anthropic,openai--dry-run, -n: Preview without writing to vault--confirm, -c: Ask for confirmation before each write--daily-notes, -d: Append to today's daily note instead of creating files--daily-pattern TEXT: Daily notes path pattern (e.g.,Daily/{year}/{month_name}/{day}.md)
Examples:
# Export single notebook
kobo-md fetch abc12345
# Export all notebooks
kobo-md fetch --all
# Export with AI enhancement
kobo-md fetch abc12345 --ai --provider anthropic
# Dry run to preview output
kobo-md fetch abc12345 --dry-run
# Interactive confirmation
kobo-md fetch --all --confirm
# Append to today's daily note
kobo-md fetch abc12345 --daily-notes
# Custom daily note pattern
kobo-md fetch abc12345 --daily-notes --daily-pattern "Journal/{year}-{month:02d}-{day:02d}.md"
# Overwrite existing files
kobo-md fetch abc12345 --overwriteProcess an existing Markdown file with AI to add wikilinks and improve formatting.
Usage:
kobo-md process FILE [OPTIONS]Arguments:
FILE: Path to Markdown file to process
Options:
--output, -o PATH: Output file (default: overwrite input)--provider, -p TEXT: AI provider:anthropic,openai--dry-run, -n: Show changes without writing
Examples:
# Process a file with AI
kobo-md process notes.md
# Preview changes without modifying
kobo-md process notes.md --dry-run
# Save to new file
kobo-md process notes.md --output notes-enhanced.md
# Use specific provider
kobo-md process notes.md --provider openaiScan Obsidian vault and show statistics about notes, tags, and aliases.
Options:
--vault, -v PATH: Vault path (uses config default if not specified)
Example:
kobo-md scan-vault
kobo-md scan-vault --vault ~/Documents/MyVaultShow current configuration settings.
Options:
--show, -s: Show current configuration (default behavior)
Example:
kobo-md config --showOutput:
┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Setting ┃ Value ┃
┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ Browser │ firefox │
│ Kobo Region │ us │
│ Vault Path │ /home/user/Documents/Vault │
│ Output Dir │ kobo │
│ AI Enabled │ False │
│ AI Provider │ anthropic │
└───────────────────┴──────────────────────────────┘
kobo-md can be configured via environment variables or a .env file in your project directory.
All settings use the KOBO_MD_ prefix:
export KOBO_MD_BROWSER=firefox # Browser: firefox, chrome, chromium
export KOBO_MD_KOBO_REGION=us # Kobo region codeexport KOBO_MD_VAULT_PATH=~/Documents/Vault # Path to Obsidian vault
export KOBO_MD_OUTPUT_DIR=kobo # Output directory within vaultexport KOBO_MD_DAILY_NOTES_PATTERN="Daily/{year}/{month:02d}-{month_name}/{year}-{month:02d}-{day:02d}.md"The daily notes pattern supports the following variables:
{year}- Four-digit year (e.g., 2024){month}- Month number without padding (1-12){month:02d}- Month number with zero padding (01-12){month_name}- Full month name (e.g., January){day}- Day without padding (1-31){day:02d}- Day with zero padding (01-31)
Examples:
Daily/{year}-{month:02d}-{day:02d}.md→Daily/2024-01-15.mdJournal/{year}/{month_name}/{day}.md→Journal/2024/January/15.mdNotes/{year}/Week {week}/{year}-{month:02d}-{day:02d}.md
export KOBO_MD_AI_ENABLED=true # Enable AI processing by default
export KOBO_MD_AI_PROVIDER=anthropic # Provider: anthropic, openai
export KOBO_MD_AI_MODEL=claude-sonnet-4-20250514 # Model name
# API Keys (required for AI features)
export ANTHROPIC_API_KEY=sk-ant-... # Anthropic API key
export OPENAI_API_KEY=sk-... # OpenAI API keyexport KOBO_MD_SUGGEST_WIKILINKS=true # Suggest wikilinks to existing notes
export KOBO_MD_CLEANUP_TEXT=true # Clean up text with AI
export KOBO_MD_SKIP_EXISTING=true # Skip existing filesCreate a .env file in your project directory or home directory:
# .env
KOBO_MD_VAULT_PATH=/home/user/Documents/Vault
KOBO_MD_OUTPUT_DIR=kobo
KOBO_MD_AI_ENABLED=true
KOBO_MD_AI_PROVIDER=anthropic
KOBO_MD_AI_MODEL=claude-sonnet-4-20250514
ANTHROPIC_API_KEY=sk-ant-...Settings are loaded in this order (later overrides earlier):
- Default values
.envfile- Environment variables
- Command-line flags
The AI enhancement features are entirely optional but can significantly improve the quality of exported notes.
When enabled with --ai, the tool will:
-
Text Cleanup:
- Fix OCR errors from handwriting recognition
- Improve sentence structure and formatting
- Correct spelling and grammar issues
- Preserve original meaning and content
-
Wikilink Suggestions:
- Scan your vault to understand existing notes
- Identify proper nouns, book titles, concepts
- Suggest
[[wikilinks]]to existing notes - Mark potential new note topics
-
Tag Recommendations:
- Analyze note content and context
- Suggest relevant tags from your existing taxonomy
- Recommend new tags for uncategorized content
# Install AI dependencies
pip install -e ".[ai]"
# Set API key
export ANTHROPIC_API_KEY=sk-ant-...
# Use with fetch command
kobo-md fetch abc12345 --ai --provider anthropicGet an API key from Anthropic Console.
# Install AI dependencies
pip install -e ".[ai]"
# Set API key
export OPENAI_API_KEY=sk-...
# Use with fetch command
kobo-md fetch abc12345 --ai --provider openaiGet an API key from OpenAI Platform.
The AI processor scans your Obsidian vault before processing to provide contextual suggestions:
# The tool will automatically:
# 1. Scan your vault for existing notes
# 2. Build an index of note titles and tags
# 3. Use this context when suggesting wikilinksThis means suggestions are tailored to YOUR vault structure and existing notes.
You can also process existing Markdown files:
# Add wikilinks and cleanup to any Markdown file
kobo-md process my-notes.md --ai
# Preview changes first
kobo-md process my-notes.md --ai --dry-runAI processing uses API calls which have associated costs:
- Anthropic Claude: ~$0.003 per notebook (varies by size)
- OpenAI GPT-4: ~$0.01 per notebook (varies by size)
Use --dry-run to preview before committing to API usage.
Exported notebooks are saved as Markdown files with YAML frontmatter:
---
title: My Reading Notes
notebook_id: abc12345-67890
created: 2024-01-15T10:30:00Z
modified: 2024-01-15T14:22:00Z
tags:
- kobo
- reading
- notes
---
# My Reading Notes
Content of the notebook with [[wikilinks]] to other notes...
Tags: #reading #book-notesBy default, notebooks are saved to {vault}/kobo/:
Vault/
├── kobo/
│ ├── My Reading Notes.md
│ ├── Project Ideas.md
│ └── Book Highlights.md
├── Daily/
│ └── 2024/
│ └── January/
│ └── 2024-01-15.md
└── ...
Customize the output directory with:
export KOBO_MD_OUTPUT_DIR=Imported/KoboAppend notebook content to your daily note instead of creating separate files:
# Append to today's daily note
kobo-md fetch abc12345 --daily-notes
# Result: Adds to Daily/2024/January/2024-01-15.mdThe daily note path pattern is configurable via KOBO_MD_DAILY_NOTES_PATTERN.
When AI processing is enabled, the tool suggests wikilinks to existing notes:
# Before
I read about quantum mechanics and Einstein's theories today.
# After (with --ai)
I read about [[Quantum Mechanics]] and [[Albert Einstein]]'s theories today.The tool distinguishes between:
- Existing wikilinks (green): Links to notes already in your vault
- New wikilinks (yellow): Suggested topics for new notes
kobo-md/
├── src/
│ └── kobo_md/
│ ├── __init__.py # Package version and metadata
│ ├── __main__.py # Module entry point
│ ├── cli.py # Typer CLI commands and interface
│ ├── auth/ # Browser cookie extraction
│ │ ├── __init__.py
│ │ └── cookies.py # Firefox/Chrome cookie handling
│ ├── kobo/ # Kobo API client
│ │ ├── __init__.py
│ │ ├── client.py # HTTP client for Kobo API
│ │ ├── models.py # Pydantic models for API responses
│ │ └── parser.py # HTML/text parsing
│ ├── processor/ # Text processing and AI
│ │ ├── __init__.py
│ │ ├── processor.py # Main processing pipeline
│ │ └── llm.py # LLM integration (Anthropic/OpenAI)
│ ├── obsidian/ # Obsidian integration
│ │ ├── __init__.py
│ │ ├── writer.py # Markdown file output
│ │ ├── vault.py # Vault scanning and indexing
│ │ └── linker.py # Wikilink generation
│ └── config/ # Configuration management
│ ├── __init__.py
│ └── settings.py # Pydantic settings
├── tests/ # Test suite
│ ├── conftest.py # Pytest fixtures
│ └── ...
├── pyproject.toml # Project metadata and dependencies
├── LICENSE # MIT License
└── README.md # This file
# Clone repository
git clone https://github.com/lukepatrick/kobo-notebooks-markdown.git
cd kobo-notebooks-markdown
# Install development dependencies
pip install -e ".[dev,ai]"
# Run tests
pytest
# Run linter
ruff check src tests
# Run type checker
mypy src
# Auto-fix linting issues
ruff check --fix src tests# Run all tests
pytest
# Run with coverage
pytest --cov=kobo_md
# Run specific test file
pytest tests/test_auth.py
# Run with verbose output
pytest -vThe project uses:
- ruff: Fast Python linter (replaces flake8, isort, etc.)
- mypy: Static type checking with strict mode
- pytest: Testing framework with async support
All code must pass type checking and linting before submission.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests and linting (
pytest && ruff check src tests && mypy src) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow PEP 8 (enforced by ruff)
- Use type hints for all functions
- Write docstrings for public APIs
- Keep functions focused and testable
- Prefer explicit over implicit
Problem: "Not authenticated" error
Solution:
- Ensure you're logged in to kobo.com in your browser
- Run
kobo-md auth loginagain - Try a different browser with
--browser chrome - Check that cookies weren't cleared by browser settings
Problem: Notebook shows as empty after export
Solution:
- Ensure the notebook is an "Advanced Notebook" (created on Kobo Sage)
- Regular highlights/annotations are not supported (only Advanced Notebooks)
- Check that
can_be_previewedis "Yes" inkobo-md listoutput
Problem: AI processing fails or produces errors
Solution:
- Verify API key is set correctly:
echo $ANTHROPIC_API_KEY # Should show sk-ant-...
- Check that
[ai]dependencies are installed:pip install -e ".[ai]" - Verify API key has sufficient credits/quota
- Use
--provider openaito try alternative provider
Problem: Cannot write to output directory
Solution:
- Check that vault path exists and is writable
- Verify output directory path is correct
- Use
--outputflag to specify alternative location
- Credentials: Your Kobo cookies are stored locally in
~/.config/kobo-md/cookies.json - API Keys: Never committed to git (add to
.gitignore) - Data: All processing is done locally except optional AI features
- Network: Only connects to:
kobo.com(for notebook data)api.anthropic.comorapi.openai.com(only when using--ai)
MIT License - see LICENSE file for details.
Copyright (c) 2026 Luke
- Built with Typer for the beautiful CLI
- Uses Pydantic for robust data validation
- Powered by httpx for async HTTP
- Text processing by BeautifulSoup
- Cookie extraction via browser-cookie3
- Issues: GitHub Issues
- Discussions: GitHub Discussions