Apache Tika OCR, tested on a scanned PDF
0 of 1,955
words the default Tika image found
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:
X-Tika-PDFOcrStrategy: no_ocrX-Tika-PDFOcrStrategy: ocr_onlyX-Tika-OCRskipOcr: trueX-Tika-OCRLanguage: porX-Tika-PDFOcrDPI: 72
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
| 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,
/tikareturns Markdown. It found 1,870 words here, and/tika/textfound 1,881. A plain requests PUT to/tika/textalso 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.
Or send us the scan. Tack, our AI, does the job for $29, up to 50 pages. First 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.