PDF to Markdown: free converters tested on the same files

You have a PDF and want Markdown with the headings and tables intact, for Notion, Obsidian, a docs site or a chat model. We ran the free converters we had at hand on the same four public sample PDFs, show what each one returned, and say which tools we could not test and why.

Tools run and documentation checked on 28 October 2026.

Most "PDF to Markdown" results are a converter box with no explanation. This page does the opposite: four small PDFs that each test one thing that tends to go wrong (a table that runs over a page break, two columns of text, a table without ruling lines, and a scan), run through every free tool we could run on one machine without installing anything new, plus our own hosted tool. You can download the same files and check every result yourself.

Disclosure: Quarrywise, the publisher of this site, makes and sells the hosted converter described near the end. The free tools need nothing from us, and for one or a few files they are often all you need.

PDF to Markdown in one minute

  • One or a few born-digital PDFs (exported from Word, Google Docs or LaTeX, so you can select the text): a free tool is enough. MarkItDown gives you Markdown tables in one command; pdftotext gives you clean plain text with the right reading order. Expect to add the # heading marks yourself and to delete page headers and page numbers.
  • You need the Markdown headings, lists and tables right without hand editing: none of the three free tools we ran did that on our samples. The heavier open-source tools (Docling, Marker, pymupdf4llm) aim at exactly this; we did not run them for this page, see what we could not test.
  • A scanned PDF (the page is a picture): it needs OCR first, whichever converter you use. Every tool we ran, ours included, returns no text for a scan. Ours says so; the free ones returned an empty result without a message.
  • Many files, no Python environment (n8n, Make, Zapier), or a per-page quality report: a hosted call starts to pay off. Ours costs USD 0.007 for a 10-page PDF.

What goes wrong: tables, two columns, running headers, scans

A PDF stores characters at positions on a page. It does not store "this is a heading", "this is a table cell" or "this is the page footer". Every converter has to guess those from positions, fonts and lines, and the guesses fail in four familiar ways:

  • Tables come out as one value per line, or column by column, so the row structure is lost. A table that continues on the next page often becomes two tables with the header twice.
  • Two-column pages are read straight across, mixing a line or a paragraph from the left column with one from the right.
  • Running headers, footers and page numbers repeat on every page and land in the middle of your text, sometimes in the middle of a table.
  • Scans have no text layer at all, so a converter without OCR returns nothing.

Our four test files, written by Quarrywise for conversion tests (all names and figures in them are invented), cover exactly these cases:

FilePagesWhat it tests
quarterly-report.pdf3Numbered headings, bullet points, a ruled table that continues on page 2, a running header and "Page N of 3" footers
two-column-article.pdf1Reading order in a two-column page, with a full-width line at the end
price-list.pdf1A ruled table and a table without ruling lines
scanned-notice.pdf1An image-only page with no text layer

What we tested, and what we could not

The rule for this test: only tools already installed on our test machine (a Windows 11 desktop PC with Python 3.14), no new installs and no model downloads. It was a time-boxed test, and every result below comes from a run we did ourselves. That gave us three free tools:

  • MarkItDown 0.1.8 (Microsoft, MIT licence), with its PDF extra.
  • pdftotext 4.00 from Xpdf (the copy that ships with Git for Windows), in its default, -layout and -table modes.
  • PyMuPDF 1.28.0, plain text extraction with page.get_text(). PyMuPDF is the library under pymupdf4llm; plain text is not its Markdown mode (see below).

Plus our own tool, run on the Apify platform with the same four files from their public links. The files on our site and the copies we ran locally are byte for byte the same (same SHA-256).

Tools we did not test, and why

These are described from their own documentation as read on 28 October 2026, not from our runs:

  • pymupdf4llm was not installed. Its PyPI page now says version 2.0 is "an empty package" and that PyMuPDF 2.x contains the code that used to be in it; PyMuPDF's PyPI page shows a doc.to_markdown() method that returns Markdown with tables inline. Our machine has PyMuPDF 1.28.0, which has no to_markdown(), and upgrading was outside this test. Licence: PyPI lists PyMuPDF as dual licensed, GNU AGPL 3.0 or a commercial licence from Artifex. Check that before you use it inside a closed product or a hosted service.
  • Marker was not installed. Its README says it converts PDF, images, PPTX, DOCX, XLSX, HTML and EPUB to Markdown, JSON, chunks or HTML, and runs layout, OCR and table recognition through a vision model served by a local inference server. It works on GPU, CPU or Apple's MPS. The code is under Apache 2.0; the model weights use a modified AI Pubs Open Rail-M licence that the README describes as free for research, personal use and startups under USD 5 million in funding or revenue, and it points to its pricing page for commercial use above that.
  • Docling was not installed. Its README lists page layout, reading order, table structure, formulas and OCR for scanned PDFs, under an MIT licence for the code, and refers to the licences of the individual models it uses. It needs Python 3.10 or newer.
  • Pandoc was not installed, and it would not help: the Pandoc manual lists PDF as an output format only, not as an input format. Pandoc cannot read a PDF.

Marker and Docling run AI models for layout, tables and OCR. Hard pages (scans, complex tables) are what they are built for, and the models are also what makes them heavier to install. We cannot vouch for their output from our own runs: if your PDFs are of that kind, test them on a few of your files.

Free option 1: MarkItDown (tested command and output)

Install and run, per its README:

pip install 'markitdown[pdf]'
markitdown quarterly-report.pdf -o quarterly-report.md

Or from Python, which is how we timed it:

from markitdown import MarkItDown

md = MarkItDown()
result = md.convert("quarterly-report.pdf")
print(result.text_content)

On the quarterly report, MarkItDown found the ruled table and wrote it as a Markdown table, which the plain-text tools did not. But it kept the running header and the "Page 1 of 3" footer, split the table at the page break (with the header row repeated), wrote no # heading marks, and turned the bullet points into (cid:127). An excerpt from the end of page 1:

2 Harvest by variety
The table lists the harvested area and the delivered weight per variety, with the change against the third
quarter of last year.
| Variety         | Hectares | Tonnes | Change |
| --------------- | -------- | ------ | ------ |
| Elstar apple    | 42       | 1,260  | +8%    |
...
| Conference pear | 19       | 494    | +11%   |
Page 1 of 3

Example Orchard Cooperative | Quarterly report Q3 (sample)
| Variety       | Hectares | Tonnes | Change |
| ------------- | -------- | ------ | ------ |
| Comice pear   | 11       | 253    | +5%    |

On the price list it found both tables, including the one without ruling lines, though that one got an extra empty fourth column. On the two-column page it kept every sentence intact but alternated paragraphs from the left and right columns (details in the side-by-side section). Its own README says it is meant for text analysis and "may not be the best option for high-fidelity document conversions for human consumption", which matches what we saw.

Free option 2: pdftotext (plain text, three modes)

pdftotext is a command-line tool, part of Xpdf and of Poppler, that turns a PDF into plain text, not Markdown. On our Windows machine it came with Git for Windows. We ran:

pdftotext -enc UTF-8 two-column-article.pdf two-column-article.txt
pdftotext -layout -enc UTF-8 price-list.pdf price-list.txt
pdftotext -table -enc UTF-8 price-list.pdf price-list.txt
  • Default mode read the two-column page correctly, the whole left column first, and put each paragraph on one long line (in the report it also ran two paragraphs together). It flattened tables column by column (Variety Elstar apple Jonagold apple Braeburn apple Conference pear), so the rows are gone.
  • -layout keeps the page's spacing, which makes tables readable as aligned text. On the ruled price-list table, though, the item names sat two rows lower than their SKU and price, so "Pruning shears" appeared on the row of QW-1003. On the two-column page it printed both columns side by side, which reads fine on screen but not as running text.
  • -table (an Xpdf option, "similar to -layout, but optimized for tables") got both price-list tables aligned correctly, row by row.

In every mode the running header and "Page N of 3" footers stay in the text. You get no Markdown: no heading marks, no table pipes. pdftotext is a good first step if you plan to clean the text yourself or only need it for search.

Free option 3: PyMuPDF (plain text) and pymupdf4llm

We ran PyMuPDF's plain text extraction, which is fast (a few milliseconds per file) and needs only pip install pymupdf:

import pymupdf

with pymupdf.open("two-column-article.pdf") as doc:
    text = "\n".join(page.get_text() for page in doc)
print(text)

It read the two columns in the right order, but put every table cell on its own line and the bullet character on a line of its own, and on each page of the quarterly report it put the footer ("Page 1 of 3") right below the running header, at the top of the page text. As plain text it is like pdftotext's default mode: good reading order, no structure.

The Markdown mode is a different matter, and we did not run it. PyMuPDF's PyPI page (version 2.0, released 19 October 2026) shows this, from its own documentation:

import pymupdf

doc = pymupdf.open("report.pdf")
md = doc.to_markdown()
print(md)

The pymupdf4llm documentation describes Markdown, JSON and text output with layout analysis "without any GPU requirement". Mind the licence: AGPL 3.0 unless you buy a commercial licence from Artifex. For a script on your own machine that rarely matters; for a closed product or a service you offer to others, it does.

Marker and Docling: when the heavier tools pay off

We did not run either (see above), so what follows is from their documentation. Both use AI models for page layout and tables, and both do OCR for scanned pages, which none of the tools we ran can do. They pay off when:

  • your PDFs include scans or photos of pages;
  • tables have merged cells, no ruling lines and uneven columns, or there are formulas;
  • pages have more than two columns or text inside figures.

Their basic commands, from their READMEs: pip install docling, then docling path/to/file.pdf, which writes a .md file; and pip install marker-pdf, then marker_single /path/to/file.pdf. Check Marker's weights licence if you use it commercially above the thresholds it names.

For plain born-digital reports, manuals and papers, a model is often more than you need: the text layer is complete, and the remaining problems are structure and page furniture.

The same files through each tool

All runs on 28 October 2026. Local tools ran on one desktop PC; each took under 0.4 seconds per file (median of five runs). "Ours" is our hosted tool, run on Apify (details in the next section). The free tools built for Markdown structure (PyMuPDF 2.0's Markdown mode or pymupdf4llm, Docling and Marker) are not in this table because we did not run them; see what we tested.

Check MarkItDown 0.1.8 pdftotext 4.00 PyMuPDF 1.28.0 text Ours
OutputMarkdown tables, other text plainPlain textPlain textMarkdown
Headings marked (#, ##)NoNoNoYes, 10 headings in 3 levels in the report
Report: table over the page breakTwo tables, header twice, footer betweenFlattened column by column (default); two aligned tables, header twice, footer between (-layout, -table)One cell per lineOne table, header once
Report: running header and page numbersKeptKeptKeptRemoved, and listed in the quality report
Report: bullet pointsBecame (cid:127)•, joined on one line (default)• on its own lineMarkdown list
Two-column page: reading orderParagraphs alternate between the columnsCorrect (default); side by side (-layout)CorrectCorrect, one paragraph split in two
Price list: ruled tableCorrect Markdown tableCorrect with -table; item names shifted two rows with -layoutOne cell per lineCorrect Markdown table
Price list: table without linesRows correct, extra empty columnAligned text with -layout or -tableOne cell per lineCorrect Markdown table
ScanEmpty output, no messageEmpty output, no messageEmpty output, no messageNo text, status needs_ocr with a message, not charged

Excerpt: the two-column page

The left column starts "Column one starts here", the right column "Column two starts here". MarkItDown put the two column headings next to each other, then alternated paragraphs:

Why columns matter

What a converter should do

Column one starts here. Many reports, papers and
newsletters set their text in two columns, because
shorter lines are easier to read on a wide page.

Column two starts here. A good converter finds the
gap between the columns and reads the whole first
column before it moves to the second.

A converter that reads straight across the page mixes
...

pdftotext (default mode) and PyMuPDF read the left column to the end first, which is right. pdftotext:

Why columns matter
Column one starts here. Many reports, papers and newsletters set their text in two columns, because shorter lines are easier to read on a wide page.
A converter that reads straight across the page mixes the two columns line by line, and the result makes no sense to a person or to a language model.
What goes wrong
...

Ours, also in the right order, with headings marked. It did split one paragraph into two ("The first column continues with this paragraph." became its own paragraph), a small flaw:

## Why columns matter

Column one starts here. Many reports, papers and newsletters set their text in two columns, because shorter lines are easier to read on a wide page.

A converter that reads straight across the page mixes the two columns line by line, and the result makes no sense to a person or to a language model.

## What goes wrong

The first column continues with this paragraph.

When lines from both columns are joined, sentences break in the middle and facts end up next to the wrong heading.
...

Excerpt: the price list's ruled table

pdftotext with -layout (note "Pruning shears" two rows below its SKU QW-1001):

Hand tools                    SKU                              Unit           Price (EUR)
                              QW-1001                          piece          24.50
 Item                         QW-1002                          piece          9.95
 Pruning shears               QW-1003                          piece          31.00
 Hand trowel                  QW-1004                          piece          42.75

PyMuPDF plain text:

Hand tools
Item
SKU
Unit
Price (EUR)
Pruning shears
QW-1001
piece
24.50

MarkItDown and ours both returned a correct Markdown table here. MarkItDown's version of the second table, the one without ruling lines, has an empty fourth column:

| Zone     | Days | Cost (EUR) |     |
| -------- | ---- | ---------- | --- |
| Local    | 1    | 0.00       |     |
| Region   | 2    | 4.95       |     |
| National | 3    | 7.50       |     |

Scanned PDFs: how to tell, and what to do

A quick test: open the PDF and try to select a line of text. If you can, the page has a text layer and any of the tools above can read it. If the whole page selects as one image, or nothing selects, it is a scan and needs OCR.

The trap is silence. On scanned-notice.pdf, MarkItDown and pdftotext both returned an empty result with exit code 0 and no message, and PyMuPDF returned an empty string. (pdftotext actually writes a single form-feed character, so a plain "is the file empty" check misses it.) In a batch, a scan then simply drops out of your notes or your search index, and nobody notices. Check for empty or very short output, ignoring whitespace, after every conversion.

What to do with scans: run them through an OCR tool first, or use a converter that includes OCR. From the tools' own documentation: Docling and Marker include OCR; MarkItDown has a separate markitdown-ocr plugin that uses a vision language model you supply, and can also call Microsoft's Azure Document Intelligence service; PyMuPDF can OCR through Tesseract, which you install separately. Our tool does not do OCR: it detects scanned pages, lists them, and does not charge for them.

Batch or no-code: our PDF to Markdown tool on Apify

Our PDF to Markdown & RAG Chunks: Word, Excel, PowerPoint on the Apify Store converts PDF, Word, PowerPoint, Excel, HTML, Markdown, text and CSV files into Markdown, JSON blocks or RAG chunks. It runs its own layout code on pdfplumber, with no GPU and no language model, so the same file gives the same output every time. It is worth paying for when you have many files, when the conversion has to run inside n8n, Make, Zapier or an AI agent rather than a Python script, or when you want a per-page quality report that tells you which pages to check. For a handful of files on your own computer, the free tools above are the cheaper choice.

A real run on the same four files

We started this run through the Apify API on 28 October 2026 (run oaH7UJ6IKd6mzANyC). The input:

{
    "documentUrls": [
        "https://quarrywise.com/samples/quarterly-report.pdf",
        "https://quarrywise.com/samples/two-column-article.pdf",
        "https://quarrywise.com/samples/price-list.pdf",
        "https://quarrywise.com/samples/scanned-notice.pdf"
    ],
    "outputFormat": "markdown"
}

All four links were fetched and the run took about 6 seconds from start to finish. Results:

  • quarterly-report.pdf: status ok, 3 billable pages, 10 headings, one table. The quality report lists the removed texts: the running header and "Page 1 of 3" to "Page 3 of 3".
  • two-column-article.pdf: status ok, 1 billable page, columns in order.
  • price-list.pdf: status ok, 1 billable page, two tables.
  • scanned-notice.pdf: status needs_ocr, 0 billable pages.

The table from the report, joined across the page break, as it appears in the markdown field:

## 2 Harvest by variety

The table lists the harvested area and the delivered weight per variety, with the change against the third quarter of last year.

| Variety | Hectares | Tonnes | Change |
|---|---|---|---|
| Elstar apple | 42 | 1,260 | +8% |
| Jonagold apple | 35 | 1,015 | +3% |
| Braeburn apple | 28 | 812 | -2% |
| Conference pear | 19 | 494 | +11% |
| Comice pear | 11 | 253 | +5% |
| Kordia cherry | 6 | 54 | -9% |
| Opal plum | 4 | 48 | +1% |
| Total | 145 | 3,936 | +5% |

### 2.1 Notes on the table

Part of the same document's row (other fields left out):

{
    "row_type": "document",
    "file_name": "quarterly-report.pdf",
    "document_id": "9abfce51c8d261d9c32c",
    "status": "ok",
    "title": "Example Orchard Cooperative: Quarterly Report",
    "pages_total": 3,
    "pages_converted": 3,
    "pages_needing_ocr": [],
    "billable_pages": 3,
    "quality": {
        "pages": [{ "page": 1, "status": "ok", "chars": 912, "words": 167, "garble_score": 0.003, "tables": 1, "images": 0, "furniture_removed": 2, "warnings": [] }, ...],
        "stats": { "furniture_lines_removed": 6, "headings": 10, "tables": 1 },
        "furniture_removed_text": ["Example Orchard Cooperative | Quarterly report Q3 (sample)", "Page 1 of 3", "Page 2 of 3", "Page 3 of 3"]
    }
}

And the scan's row, instead of an empty result:

{
    "row_type": "document",
    "file_name": "scanned-notice.pdf",
    "status": "needs_ocr",
    "pages_needing_ocr": [1],
    "billable_pages": 0,
    "error_message": "No text layer found: this looks like a scanned or image-only document. It needs OCR, which this Actor does not do. Nothing was charged."
}

Cost of this run at the listed prices: 3 documents × USD 0.003 + 5 pages × USD 0.0004 = 0.009 + 0.002 = USD 0.011, plus Apify's start event. The scan cost nothing. (This was our own run of our own tool, so the event charges were not billed to us; the figure is what you would pay.)

Price and worked examples

Price when this guide was written: pay per event, USD 0.003 per document converted and USD 0.0004 per page converted, plus Apify's standard Actor start event, a fraction of a cent per run. A page is a PDF page or slide with text; for Word, Excel, HTML, Markdown, text and CSV files, every 3,000 characters of output count as one page. The Store page always shows the current price.

BatchMathCost
One 10-page PDF0.003 + 10 × 0.0004 = 0.003 + 0.004USD 0.007
One 100-page PDF0.003 + 100 × 0.0004 = 0.003 + 0.040USD 0.043
100 PDFs of 10 pages (the default maxDocuments is 100)100 × 0.003 + 1,000 × 0.0004 = 0.30 + 0.40USD 0.70
1,000 PDFs of 10 pages (set maxDocuments to 1000)1,000 × 0.003 + 10,000 × 0.0004 = 3.00 + 4.00USD 7.00
A 30-page scan (needs OCR)nothing convertedUSD 0

The examples leave out the start event. Free: documents that fail, pages that need OCR, empty pages and documents, and notice and error rows. Apify's free plan includes USD 5 of usage a month and needs no credit card; at USD 0.007 per 10-page PDF that covers roughly 700 such PDFs. Set a maximum charge per run in the run options: the tool converts as many pages as that budget allows, says so in the document's warnings, and stops cleanly.

How to run it

  1. Open the tool on the Apify Store and paste links to your PDFs in Document links, or upload files with Upload files.
  2. Choose Output: Markdown (one row per document), JSON blocks, or RAG chunks.
  3. Start the run. Each document's Markdown is in the markdown field of the dataset; switch on saveMarkdownFiles to also get a .md file per document.

From n8n, Make, Zapier, an AI agent through Apify's MCP server, or the Apify API, the call is the same input as above. Our guide on PDF to Markdown for LLMs and RAG shows the API calls in curl and Python, and chunking with page citations.

The limits, honestly

  • No OCR. Scanned and image-only pages are detected, listed and free, not converted.
  • Tables without ruling lines are recognised only when their columns line up cleanly. Merged cells without lines, tables inside text columns and more than two columns of text may come out as paragraphs. The quality report helps you find those pages.
  • Charts and images are not described; only their count is in the quality report.
  • Password-protected PDFs, legacy .doc and .ppt, OpenDocument, EPUB and RTF are not read; each gets a free error row.
  • Files up to 50 MB by default (maxFileSizeMb, at most 100 MB), up to 1,000 documents per run (maxDocuments, default 100), and up to 300 PDF pages or slides per document by default (maxPages, at most 5,000). Each document gets up to 10 minutes.
  • Links behind a login are not supported. Use a public or signed link, or upload the file.

A ready-made input for tables on the Store: extract tables from PDF to JSON and Markdown.

See the PDF to Markdown tool on the Apify Store

FAQ

What is the best free PDF to Markdown converter?

Of the tools we ran, MarkItDown is the only one that returns Markdown at all, and it wrote ruled tables as Markdown tables, but split one at a page break; it missed headings, page furniture and the two-column order. pdftotext got the reading order right but returns plain text. For headings and tables without hand editing, try the heavier tools (Docling, Marker, or PyMuPDF 2.0's Markdown mode) on a few of your own files; we did not test them here.

How do I convert a PDF to Markdown in Python?

The shortest route is MarkItDown: pip install 'markitdown[pdf]', then MarkItDown().convert("file.pdf").text_content (full snippet above). PyMuPDF 2.0 documents pymupdf.open("file.pdf").to_markdown(), under the AGPL licence. Docling has a Python API too.

Can Pandoc convert PDF to Markdown?

No. The Pandoc manual lists PDF as an output format only. Convert the PDF with one of the tools above first; Pandoc can then take the Markdown on to other formats.

Which tool keeps tables?

On our samples: MarkItDown wrote ruled tables as Markdown tables but split one at the page break; pdftotext's -table mode kept rows aligned as text; our tool joined the table across the page break and also read the table without ruling lines. Complex tables (merged cells, no lines, uneven columns) need a layout model.

Is it safe to use an online PDF to Markdown converter?

That depends on the service and the document. Read its privacy terms before you upload anything confidential. The local tools above keep the file on your machine. Our tool processes documents in memory during the run and stores results only in the run's own dataset and key-value store, which you control and can delete; nothing is used for training.

Quarrywise is independent and isn't affiliated with or endorsed by Microsoft, Artifex, Glyph & Cog, the Poppler or Pandoc projects, Datalab, the Docling project or Apify.

Sources