Tools,

Apache Tika OCR, tested on a scanned PDF

0 of 1,955

words the default Tika image found

Get the working setup

The default image has no Tesseract

apache/tika:latest has no Tesseract: HTTP 200, empty body. Use the full image:

docker run -d --name tika -p 127.0.0.1:9998:9998 apache/tika:latest-full

Wait a few seconds for it to start, then send the file:

curl -T scan.pdf http://localhost:9998/tika/text > scan.txt

1,881 of 1,955 true words found

8.9 seconds for 10 pages

The full image is 2.01 GB on disk. Tika has no login, so keep the port on 127.0.0.1.

Tika 4 ignores the old OCR headers

Older answers set OCR with headers. On Tika 4.2.0, each of these gave the same bytes as no header, with HTTP 200 and no warning:

Tika 4 reads these settings from a JSON config file.

Set OCR in a config file

Save this as tika-config.json. It sends every PDF page to Tesseract, in English:

{
  "server": {},
  "parsers": [
    { "pdf-parser": { "pages": { "text": "OCR" } } },
    { "default-parser": {} }
  ],
  "engines": {
    "tesseract": {
      "tesseract-ocr-parser": { "language": "eng", "timeoutMillis": 300000 }
    }
  },
  "text-recognizers": [{ "engine": "tesseract" }]
}

Start the server with it:

docker run -d --name tika -p 127.0.0.1:9998:9998 \
  -v "$PWD/tika-config.json:/config.json:ro" \
  apache/tika:latest-full -c /config.json

Keep the "server" line. Without it, every config I tried stopped the server at start: Couldn't find 'server' element.

Turn OCR off, all of it

"text": "EXTRACT" gave 0 words from the PDF, but JPG files still went to Tesseract. This config stops OCR for both:

{ "server": {}, "text-recognizers": [] }

The scan then came back empty in 0.1 seconds.

A missing language gives HTTP 422

The full image has deu, eng, fra, ita, jpn and spa. With "language": "por", every request got HTTP 422. The log said:

Error opening data file /usr/share/tesseract-ocr/5/tessdata/por.traineddata

Add the language pack in your own image:

FROM apache/tika:latest-full
USER root
RUN apt-get update \
  && apt-get install -y --no-install-recommends tesseract-ocr-por \
  && rm -rf /var/lib/apt/lists/*
USER 35002:35002

With that image, the same config answered HTTP 200.

Set OCR per request

POST /tika/config/text answered HTTP 403 by default: "Config endpoints are disabled." Start the server with this as its config:

{ "server": { "allowPerRequestConfig": true } }

Then send the settings with the file:

curl -F file=@scan.pdf \
  -F 'config={"pdf-parser": {"pages": {"text": "EXTRACT"}}};type=application/json' \
  http://localhost:9998/tika/config/text

That request skipped OCR: 0 words in 0.2 seconds.

tika-python gets None from Tika 4

tika-python 3.4.0 got HTTP 200 from the Tika 4.2.0 server, and content was None. It reads X-TIKA:content, and Tika 4 names that field tk:content.

Ask for plain text instead:

import sys

from tika import parser

parsed = parser.from_file(
    sys.argv[1],
    serverEndpoint="http://localhost:9998",
    service="text",
    requestOptions={"timeout": 600},
)
print(parsed["content"])

With no serverEndpoint, tika-python downloads its own Tika 3.3.2 jar and runs it on your Java. Its server kept running after my script ended.

Eight setups, one scan

10 scanned pages, 1,955 true words
Setup Words found Seconds
apache/tika:latest 0 0.1
latest-full, PDF 1,881 8.9
latest-full, 10 JPG files 1,877 10.3
"text": "EXTRACT" 0 0.2
tika-python on Tika 4 0 8.5
tika-python, service="text" 1,870 8.3
tika-python, own Tika 3.3.2 1,881 67.1
Our OCR Actor 1,874 144.5

Tika ran in Docker on a Mac, limited to 2 CPUs. The 67.1 seconds include the jar download, and the Actor's include its start in the cloud.

No Java server: call the Actor

If you cannot run a 2 GB Java server, our OCR Actor takes a PDF URL and returns Markdown:

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/langchain-scan-10-pages.pdf"]}'

It found 1,874 words, in one Markdown item. It was the slowest setup here, and it costs $0.006 a page.

How I tested it
  • Scan: the 10-page PDF of OCR in n8n, the first 10 of 55 proofread Wikisource book scans, with no text layer. The JPG test sent the same 10 scans one at a time. 1,955 true words.
  • Score: true words found in order, with the scorer of the tessdata test.
  • Tika 4.2.0 from the Docker images apache/tika:latest and latest-full (Tesseract 5.5.0), each with --cpus 2. tika-python 3.4.0 and requests 2.34.2 on Python 3.14.7. Java 21 and Tesseract 5.5.4 for tika-python's own jar.
  • In Tika 4, /tika returns Markdown. It found 1,870 words here, and /tika/text found 1,881. A plain requests PUT to /tika/text also found 1,881.
  • The code blocks above are the files that ran. The Actor: one real run on the 10-page PDF.
What I did not test
  • Tika 3.x servers, tika-app, and the Java API.
  • The Tess4J engine and the VLM parsers.
  • Open WebUI, Solr and other apps that call Tika.
  • Tables, photos, handwriting, and other languages.
Sources

Two ways to skip the work

Run it yourself. Our Apify Actor turns scanned PDFs into Markdown, tables included.

Try the Actor

Or send us the scan. Tack, our AI, does the job for $29, up to 50 pages. First page free.

Get one page free

Questions people ask

Why does Apache Tika return empty text for a scanned PDF?

The image or install has no Tesseract. apache/tika:latest answered HTTP 200 with 0 words for my 10-page scan. apache/tika:latest-full found 1,881 of 1,955.

How do I disable OCR in Apache Tika?

On Tika 4, start the server with a config file that has "text-recognizers": []. The X-Tika-OCRskipOcr header did nothing on Tika 4.2.0.

Why does X-Tika-PDFOcrStrategy do nothing?

Tika 4 ignores it. On Tika 4.2.0, no_ocr and ocr_only gave the same bytes as no header. Set pages.text in a config file, or per request after allowPerRequestConfig.