Marker OCR, tested on a real scanned PDF
1,922
of 1,955 scan words, read in order
pip install alone stops on scans
marker-pdf 2.1.0 stopped on a scanned page:
SpawnError, llama-server binary not found. Marker reads
scans with a vision model in llama.cpp.
I got the official llama.cpp build for Mac with llama.sh:
curl -sL https://github.com/ggml-org/llama.cpp/releases/download/b12316/llama-b12316-bin-macos-arm64.tar.gz | tar xz
Then marker.sh points Marker at it. Run sh marker.sh next
to book.pdf, and put flags after it:
export LLAMA_CPP_BINARY="$PWD/llama-b12316/llama-server"
marker_single book.pdf --output_dir out "$@"
1,922 of 1,955 words, in order
60 s for 10 pages, Mac GPU
The first run downloads a 1.4 GB model. With
--mode balanced, I got the same text in 63 s.
Typed pages: leave --force_ocr off
Marker checks each page. In the 11-page file, it kept the typed cover page's own text and read the 10 scanned pages.
| Setting | Cover words | Scan words |
|---|---|---|
| Default | 13 of 13 | 1,922 |
| --force_ocr | 11 of 13 | 1,922 |
OCRmyPDF first? Strip its text
On OCRmyPDF's PDF, Marker kept Tesseract's hidden text on all 10 pages. It ran no OCR and found 1,868 words in order.
With --strip_existing_ocr, it read the scan again: 1,922,
the same text as the scan alone.
German worked with no language setting
Marker has no language option. I gave it 3 German pages. One is a magazine page with two columns.
194 of 206 umlauts
966 of 972 words in order, two columns
No GPU: no page in 10 minutes
With TORCH_DEVICE=cpu, LLAMA_CPP_NGL=0 and 2
CPU threads, I ran the same 3 German pages.
After 600 s, Marker's request timeout cancelled all 3 pages. I stopped the run after 16 minutes, with no text.
A small cloud server with no GPU is this kind of machine. Our Actor read the same 3 pages in 65 s.
Marker read better than our Actor
Our Actor makes Markdown from a file URL, with no install:
curl -s -X POST "https://api.apify.com/v2/acts/brasspage~pdf-ocr-to-markdown/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"documentUrls": ["https://api.apify.com/v2/key-value-stores/sTnmNrGoSsi7tLQvs/records/anythingllm-mixed-11-pages.pdf"]}' \
| jq -r '.[].markdown' > mixed.md
| Test | Marker | Actor |
|---|---|---|
| Scan words in order | 1,922 | 1,874 |
| Umlauts | 194 | 192 |
| Two columns, in order | 966 | 954 |
| Time, 11 pages | 59 s | 144 s |
The Actor needs no GPU and no 2.6 GB setup. It costs $0.006 a page. Marker's model weights are free for research, personal use and startups under $5M. Past that, you buy a license.
How I tested it
- Scan: the 10 pages of OCR in n8n, proofread Wikisource book scans, 1,955 true words. The 11-page file adds the 13-word typed cover page of AnythingLLM OCR.
- German: the 3 pages of Paperless-ngx OCR, 1,887 words, 206 umlauts.
- Score: true words found in order in the Markdown, with the scorer of the tessdata test. German: the scorer of Tesseract German.
- marker-pdf 2.1.0 (surya-ocr 0.23.0), llama.cpp b12316, Python 3.12 arm64, on an Apple Silicon Mac. 2 threads at the lowest priority. Times include the model start.
- The code blocks above are the files that ran. The Actor: two real runs, build 0.1.65.
What I did not test
- Linux or Windows, and NVIDIA GPUs.
-
--use_llm, which sends pages to an LLM service. - Tables, math and handwriting.
Two ways to skip the work
Run it yourself. Our Apify Actor turns scanned PDFs into Markdown, tables included.
Or send us the scan. Tack, our AI, does the job for $29, up to 50 pages. First page free.
Questions people ask
Does Marker do OCR on scanned PDFs?
Yes, once it can find llama-server. Marker 2.1.0 read 1,922 of 1,955 words in order on a 10-page scan. With pip install alone, it stopped with SpawnError.
Do I need --force_ocr for scans?
No. Marker read the scanned pages without it and kept the typed cover page's own text, 13 of 13 words. With --force_ocr it read the cover again and got 11 of 13.
How do I set the OCR language in Marker?
Marker 2.1.0 has no language option. On 3 German pages with no setting, it kept 194 of 206 umlauts.
Related guides
- PDF to Markdown in Python: 5 libraries tested
- Docling OCR engines: EasyOCR vs Tesseract vs RapidOCR
- Unstructured OCR on scanned PDFs: strategies tested
- OCRmyPDF "page already has text": which flag to use
- LibreOffice OCR: what works on a scanned PDF
- Obsidian OCR for scanned PDFs: Omnisearch, tested
- Surya OCR on a scanned PDF: setup and results, tested