Convert PDF documents to Markdown using Claude's native PDF API
Project description
pdf2md-claude
Convert PDF documents to high-quality Markdown using Claude's native PDF API. Handles large documents via chunked conversion with context passing, deterministic merging, and output validation. Preserves tables, formulas, figures, and document structure.
Installation
Requires Python 3.11+.
pip install pdf2md-claude
Or install from source:
pip install git+https://github.com/hacker-cb/pdf2md-claude.git
Quick Start
# Set API key
export ANTHROPIC_API_KEY="your-key-here"
# Convert a single PDF (output: document.md next to the PDF)
pdf2md-claude document.pdf
# Convert multiple PDFs
pdf2md-claude doc1.pdf doc2.pdf
# Convert all PDFs in a directory (shell glob)
pdf2md-claude *.pdf
pdf2md-claude docs/*.pdf
# Custom output directory
pdf2md-claude *.pdf -o output/
# Also works via python -m
python -m pdf2md_claude document.pdf
Output
By default, Markdown files are written next to the source PDF:
| Input | Output |
|---|---|
docs/document.pdf |
docs/document.md |
docs/document.pdf --max-pages 5 |
docs/document_first5.md |
With -o DIR, all output goes to the specified directory.
Conversion Pipeline
Chunked conversion with context passing, deterministic merging, and validation:
- Split PDF into disjoint chunks
- Convert each chunk with context from the previous chunk's tail
- Merge chunks by page markers (deterministic, no LLM)
- Merge continued tables across page boundaries (deterministic, no LLM)
- Extract and inject images from bounding-box markers (deterministic, no LLM)
- Validate output (page markers, tables, heading gaps, binary sequences, fabrication detection)
CLI Options
pdf2md-claude [OPTIONS] PDF [PDF ...]
Positional:
PDF One or more PDF files to convert (supports shell globs)
Options:
-o, --output-dir DIR Output directory (default: same directory as each PDF)
-v, --verbose Enable verbose logging
-f, --force Force reconversion even if output exists
--max-pages N Convert only first N pages (useful for debugging)
--cache Enable prompt caching (1h TTL, reduces re-run cost)
--pages-per-chunk N Pages per conversion chunk (default: 10)
--no-images Skip image extraction from bounding-box markers
--remerge Re-merge from cached chunks (no API calls needed)
Run without arguments to display help.
Environment
| Variable | Description |
|---|---|
ANTHROPIC_API_KEY |
Anthropic API key (required) |
File Structure
pdf2md-claude/
├── pdf2md_claude/ # Main package
│ ├── __init__.py # Package exports (lazy imports)
│ ├── __main__.py # python -m pdf2md_claude entry point
│ ├── cli.py # CLI argument parsing and orchestration
│ ├── client.py # Anthropic API client setup
│ ├── converter.py # Core PDF→Markdown conversion logic
│ ├── images.py # Image extraction, rendering, and injection (pymupdf)
│ ├── markers.py # Centralized marker definitions (MarkerDef)
│ ├── merger.py # Deterministic page-marker merge + table continuation merging
│ ├── models.py # Model configurations, pricing, usage tracking
│ ├── pipeline.py # Single-document orchestration (convert → merge → validate → write)
│ ├── prompt.py # Prompts for Claude PDF conversion
│ ├── validator.py # Content validation (page markers, tables, fabrication, etc.)
│ └── workdir.py # Chunk persistence, resume, and work directory management
├── tests/ # Unit tests
│ ├── __init__.py
│ ├── test_converter.py
│ ├── test_images.py
│ ├── test_markers.py
│ ├── test_table_merger.py
│ ├── test_validator.py
│ └── test_workdir.py
├── scripts/
│ └── setup-dev.sh # Dev environment setup (.venv + hooks)
├── .githooks/
│ └── pre-commit # Runs tests before commit
├── pyproject.toml # Project metadata and dependencies
├── .gitignore
└── README.md
Development
Setup
One command sets up everything (.venv, dependencies, git hooks):
git clone https://github.com/hacker-cb/pdf2md-claude.git
cd pdf2md-claude
bash scripts/setup-dev.sh
To recreate the .venv from scratch:
bash scripts/setup-dev.sh --force
Running Tests
pytest tests/ -v
Debugging
Enable verbose logging:
pdf2md-claude -v document.pdf
Use --cache to avoid re-paying for PDF content on repeated runs during
prompt/pipeline development:
pdf2md-claude --cache document.pdf --max-pages 5
Project details
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 pdf2md_claude-0.1.0.tar.gz.
File metadata
- Download URL: pdf2md_claude-0.1.0.tar.gz
- Upload date:
- Size: 65.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
013f07bf538d76328e48c4374e51aa282805fea2970926515c43e6625814025d
|
|
| MD5 |
96c45d1be1ca89795180ad59b9c172aa
|
|
| BLAKE2b-256 |
f925c1b40bc0df826c54f808a9f0eada5d602e8ae0e734b4709e3b7783f112a3
|
Provenance
The following attestation bundles were made for pdf2md_claude-0.1.0.tar.gz:
Publisher:
publish.yml on hacker-cb/pdf2md-claude
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pdf2md_claude-0.1.0.tar.gz -
Subject digest:
013f07bf538d76328e48c4374e51aa282805fea2970926515c43e6625814025d - Sigstore transparency entry: 928528835
- Sigstore integration time:
-
Permalink:
hacker-cb/pdf2md-claude@5c39a767c02c755d324a28a376bf3eb8f6d5d736 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/hacker-cb
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5c39a767c02c755d324a28a376bf3eb8f6d5d736 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pdf2md_claude-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pdf2md_claude-0.1.0-py3-none-any.whl
- Upload date:
- Size: 50.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe87a436c14f29c2a1b9047009d8443f9c3ff2bc6d19ae560897285ff8ea74fd
|
|
| MD5 |
f8af4097ce7cc4cf7cbdc52dd22e4201
|
|
| BLAKE2b-256 |
ae1abadc6be7688fa5a8f8a26e1173b7c6f9906a26f042634fd5925cecd2c3f7
|
Provenance
The following attestation bundles were made for pdf2md_claude-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on hacker-cb/pdf2md-claude
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pdf2md_claude-0.1.0-py3-none-any.whl -
Subject digest:
fe87a436c14f29c2a1b9047009d8443f9c3ff2bc6d19ae560897285ff8ea74fd - Sigstore transparency entry: 928528852
- Sigstore integration time:
-
Permalink:
hacker-cb/pdf2md-claude@5c39a767c02c755d324a28a376bf3eb8f6d5d736 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/hacker-cb
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5c39a767c02c755d324a28a376bf3eb8f6d5d736 -
Trigger Event:
push
-
Statement type: