How to Convert PDF to Markdown in Python
Converting PDFs to Markdown has become a standard preprocessing step for RAG pipelines and LLM workflows — Markdown keeps the headings and tables that models use to understand structure, at a fraction of the tokens. Python has several good options, each with real trade-offs. Here are the four that matter, with working code for each.
Why Markdown, and why this is harder than it looks
LLMs handle Markdown exceptionally well: headings mark section boundaries, pipe tables keep tabular data aligned, and lists stay lists. Feeding a model raw PDF-extracted text loses all of that — and feeding it the PDF binary is not an option for most pipelines. That is why nearly every serious RAG stack converts documents to Markdown before chunking and embedding.
The hard part is that PDFs do not contain structure — they contain characters positioned at x/y coordinates. A converter has to reverse-engineer which lines are headings (from font size), which words form a table (from alignment), and what the reading order is (from layout analysis). Libraries differ enormously in how well they do this, which is why the choice matters.
A second complication: scanned PDFs contain no text at all, just images of pages. Only some of the options below handle those, via OCR.
Option 1: PyMuPDF4LLM — fast and light
PyMuPDF4LLM is a thin layer over PyMuPDF (fitz) purpose-built for LLM pipelines: pip install pymupdf4llm, then md = pymupdf4llm.to_markdown("doc.pdf") — one line, and it is very fast, processing most documents in well under a second.
It detects headings from font sizes, renders tables as pipe tables, and can emit page chunks with metadata for direct ingestion into LlamaIndex or LangChain. For digital-born PDFs with straightforward layouts — reports, documentation, papers without exotic formatting — it is an excellent default.
Limits: no OCR, so scanned pages come back empty. Complex multi-column layouts and intricate tables can come out scrambled, because the underlying heuristics favor speed over deep layout analysis. License note: PyMuPDF is AGPL — fine for internal tools, but check with your legal team before shipping it inside a commercial product.
Option 2: Marker — highest quality, needs a GPU
Marker uses a pipeline of deep-learning models for layout detection, reading order, and table structure, and produces the most faithful Markdown of any open-source option — it handles multi-column academic papers, complex tables, and even equations.
The cost is infrastructure: model weights of several gigabytes, a GPU for reasonable speed (CPU works but is slow — often tens of seconds per document), and a heavy dependency tree that takes real effort to deploy in production or serverless environments.
Pick Marker when output quality on difficult documents is the top priority and you have the hardware — bulk-converting an academic corpus on a GPU box is its sweet spot. For a lightweight service or a serverless function, the operational weight usually rules it out.
Option 3: MarkItDown — Microsoft's all-format converter
MarkItDown is Microsoft's open-source everything-to-Markdown library: pip install markitdown, then MarkItDown().convert("doc.pdf").text_content. Its strength is breadth — the same API converts PDF, DOCX, PPTX, XLSX, HTML, and more, which is why it went viral for LLM data preparation.
For PDFs specifically, its extraction is simpler than the dedicated options: it leans on basic text extraction, so heading detection and table fidelity trail PyMuPDF4LLM and Marker, and scanned PDFs need extra OCR setup.
Pick MarkItDown when you need one dependency covering many file types at moderate quality — an internal ingestion script that must accept "whatever people upload" — rather than the best possible PDF output.
Option 4: A hosted API — no dependencies at all
The fourth option is to not run the conversion locally: POST the PDF to a parsing API and get Markdown back. With ParseJet: resp = httpx.post("https://api.parsejet.com/v1/parse/auto/file", headers={"Authorization": "Bearer KEY"}, files={"file": open("doc.pdf", "rb")}) — the "text" field of the JSON response is the Markdown.
This route has OCR for scanned pages built in, no model weights or native libraries to install, and works identically in a serverless function, a container, or a notebook. It also sidesteps the AGPL question entirely. The trade-off is a network round trip and a per-document cost after the free tier (300 credits/month free; a PDF costs 3 credits).
Pick the API when you want the pipeline running today, when your PDFs include scans, or when you are deploying somewhere that heavy dependencies hurt — Lambda, Cloud Run, Vercel.
Which one should you use?
Digital-born PDFs, simple-to-moderate layouts, speed matters: PyMuPDF4LLM. It is the best effort-to-quality ratio in pure Python.
Difficult documents (multi-column papers, complex tables) and you have a GPU: Marker. Nothing open-source beats its output quality.
Many different file types through one library, moderate quality is fine: MarkItDown.
Scanned PDFs in the mix, serverless deployment, or zero appetite for dependency management: the hosted API. One HTTP call, OCR included, same output schema for every document.
Complete example: PDF folder to Markdown files
Whichever route you choose, the pipeline shape is the same — iterate, convert, write. Here it is with the API (swap the inner call for pymupdf4llm.to_markdown() to run it locally):
import httpx; from pathlib import Path — then for each pdf in Path("pdfs/").glob("*.pdf"): resp = httpx.post("https://api.parsejet.com/v1/parse/auto/file", headers={"Authorization": "Bearer KEY"}, files={"file": pdf.read_bytes()}); Path("md/", pdf.stem + ".md").write_text(resp.json()["text"], encoding="utf-8").
For RAG ingestion, chunk the Markdown by headings rather than by fixed character counts — heading-aware chunks keep sections intact and retrieve dramatically better. That structural fidelity is exactly why the conversion step was worth doing carefully.
Try the conversion without writing code
Upload a PDF and see the Markdown output this API produces — headings, tables, and OCR for scanned pages included.
Convert a PDF to Markdown freeFrequently asked questions
What is the best Python library to convert PDF to Markdown?
For digital-born PDFs, PyMuPDF4LLM offers the best speed-to-quality ratio. For difficult layouts with a GPU available, Marker produces the highest quality. For scanned PDFs or dependency-free deployment, a hosted API like ParseJet handles OCR and structure in one HTTP call.
How do I convert a scanned PDF to Markdown in Python?
Scanned pages contain no text, so you need OCR. Locally that means wiring up an OCR engine yourself; the simpler route is an API with OCR built in — ParseJet detects image-based pages and OCRs them automatically in the same request.
Is MarkItDown good for PDF to Markdown?
MarkItDown's strength is converting many formats (PDF, DOCX, PPTX, XLSX) through one library. For PDFs specifically, its structure detection is simpler than PyMuPDF4LLM or Marker — fine for moderate quality needs, not the best choice for faithful heading and table extraction.
Why convert PDFs to Markdown for RAG instead of plain text?
Markdown preserves headings, lists, and tables that plain text flattens. Heading-aware chunking retrieves better, and LLMs use the structure to understand context — at roughly the same token cost as plain text.
Can I convert PDF to Markdown in Python without installing anything?
Yes — call a parsing API over HTTP with httpx or requests. POST the file to ParseJet's /v1/parse/auto/file and the "text" field of the response is the Markdown. Free API keys include 300 credits per month.
Related tools
PDF to Markdown Converter
Convert PDF to Markdown online for free. Preserves headings, lists, tables, and code blocks. No signup required — try it instantly or automate via API.
PDF to JSON Converter
Convert PDF to JSON online for free. Get text, title, and metadata as structured JSON — ready for your app, database, or AI pipeline. API included.
Word to Markdown Converter
Convert Word documents (DOCX) to Markdown online for free. Headings, lists, and tables preserved. No signup — or automate DOCX to MD conversion via API.