OCR: Image and Scanned PDF to Text avatar

OCR: Image and Scanned PDF to Text

Pricing

from $4.00 / 1,000 page recogniseds

Go to Apify Store
OCR: Image and Scanned PDF to Text

OCR: Image and Scanned PDF to Text

OCR for images and scanned PDFs, run inside the Actor with no outside service. PNG, JPEG, WebP, TIFF, BMP, GIF and PDF in; text per file and page out, with confidence and optional word or line boxes. 30 languages, auto-rotation, OCR only on PDF pages that lack a text layer.

Pricing

from $4.00 / 1,000 page recogniseds

Rating

0.0

(0)

Developer

Anton Sajin

Anton Sajin

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Get the text out of images and scanned PDFs. Give the Actor links, uploads or files another Actor saved, and it returns one result per file: the text, the text of every page, and a confidence figure that tells you which pages to trust.

The recognition runs inside the Actor, on its own CPU, with the open-source Tesseract engine. No file leaves the run, there is no API key, and you pay per page.

It reads printed and typewritten text: scanned letters, reports, contracts, book pages, faxes, screenshots, receipts. It does not read handwriting, and it returns tables as rows of text, not as tables. On a clean scan about one word in a hundred comes out wrong; the measurements are below. If your inputs are photos of crumpled paper, handwriting or forms with tiny print, a vision model will do better and you should use one.

What you get for each file

  • Text with paragraphs put back together: wrapped lines are joined, words hyphenated at a line end are restored, and rows of a receipt or a table stay rows.
  • Text per page, with the page's confidence from 0 to 100 and a lowConfidence flag when the text is probably unreliable.
  • What was done to the page: which way up it was, how many degrees of skew were corrected, whether it was enlarged because the letters were very small.
  • Optional line or word boxes in pixels, each with its own confidence, for highlighting, field extraction or review of doubtful words.
  • For PDFs: which pages were recognised and which were read from the file's own text layer.

What it handles

FormatsPNG, JPEG, WebP, BMP, GIF (first frame), TIFF including multi-page, and PDF. The format is read from the file's first bytes, not from its name.
PDFs that are part scanA page with a text layer is read from it, exactly and almost for free; only pages without one go through OCR. You can also force OCR on every page.
Pages on their side or upside downFound and read the right way up; rotation reports 90, 180 or 270. Camera orientation (Exif) is applied first.
Skewed scansStraightened when the skew is one degree or more; skewDegrees reports the angle. Tested up to 10 degrees.
Low resolutionA small image whose text lines are under 17 pixels high is enlarged to twice its size before recognition. When the text is too small even so, the confidence drops and the result says so.
Blank pagesReported as isBlank, quickly, and not charged.
30 languagesSee the list below. Up to 5 in one run.
Bad filesA link to a web page, a damaged file, a PDF that needs a password, an image too large: each gets its own item with an errorCode, the run carries on, and nothing is charged for it.

Input example

{
"fileUrls": [
"https://upload.wikimedia.org/wikipedia/commons/2/26/%28Interim_Review_of_Reports%29-_Neches_River_and_Tributaries_Salt_Water_Barrier_at_Beaumont_Texas_-_DPLA_-_3d1ae87deadc66d36f1932bb17bb728a_%28page_19%29.jpg",
"https://upload.wikimedia.org/wikipedia/commons/e/ea/Aeronautics_and_Space_Report_of_the_President_-_1969_Activities.pdf"
],
"languages": ["eng"],
"pageRange": "1-4",
"boxes": "lines"
}

The first file is a typewritten page scanned by the U.S. National Archives. The second is a scanned government report of 113 pages, most of which carry a text layer from an earlier OCR pass. With this input its pages 1 and 2 are recognised (page 1 has no text layer, page 2 only a few characters of one) and pages 3 and 4 are read from the text layer.

Where the files come from

FieldUse
fileUrlsDirect links to images or PDFs. Redirects are followed. A link to a file's page on GitHub or Wikimedia Commons is pointed at the file itself.
filesFiles uploaded from your computer in the Apify Console.
keyValueStoreIdA key-value store that holds images or PDFs, for example screenshots saved by a crawler. Every record whose key ends in .pdf, .png, .jpg, .jpeg, .webp, .tif, .tiff, .bmp or .gif is read.
recordKeysRead only these records of the store, whatever their keys look like.

Options

FieldDefaultWhat it does
languages["eng"]Languages of the text, main language first. Choose only those that occur.
autoRotatetrueStraighten skewed pages and read pages that lie on their side or upside down.
pdfOcrModeautoauto recognises only PDF pages without a text layer. all recognises every page.
pageRangeall pagesPages to read from PDFs and multi-page TIFFs, for example 1-5, 8, 12-.
maxPagesPerFileno limitStop reading a file after this many pages.
passwordnoneOpens PDFs that need a password. PDFs that only restrict printing or copying open without one.
boxesnonelines or words adds the rectangle and confidence of every line or word.
keepLineBreaksfalseReturn every line as it stands on the page instead of joining the lines of a paragraph.

Languages

English eng, Spanish spa, French fra, German deu, Italian ita, Portuguese por, Dutch nld, Polish pol, Czech ces, Hungarian hun, Romanian ron, Swedish swe, Danish dan, Norwegian nor, Finnish fin, Turkish tur, Greek ell, Russian rus, Ukrainian ukr, Bulgarian bul, Arabic ara, Hebrew heb, Hindi hin, Thai tha, Vietnamese vie, Indonesian ind, Chinese Simplified chi_sim, Chinese Traditional chi_tra, Japanese jpn, Korean kor.

These are the languages people most often ask OCR tools for; Tesseract has models for about a hundred more, which are left out to keep the Actor small. The accuracy figures below were measured on English. For 22 of the other languages we checked that a few lines of clean rendered text come back right or nearly right; that shows the language works, not how well it reads your scans. Text in a language you did not select is still read, as if it were one you did, which usually goes wrong on accented and non-Latin letters.

Output

One item per file. This is from a real run on the first file of the example above, with boxes set to lines. The text is cut short here and only two of the 29 line boxes are shown:

{
"id": "2cdf029be2b25f89",
"url": "https://upload.wikimedia.org/wikipedia/commons/2/26/%28Interim_Review_of_Reports%29-_Neches_River_and_Tributaries_Salt_Water_Barrier_at_Beaumont_Texas_-_DPLA_-_3d1ae87deadc66d36f1932bb17bb728a_%28page_19%29.jpg",
"loadedUrl": "https://upload.wikimedia.org/wikipedia/commons/2/26/%28Interim_Review_of_Reports%29-_Neches_River_and_Tributaries_Salt_Water_Barrier_at_Beaumont_Texas_-_DPLA_-_3d1ae87deadc66d36f1932bb17bb728a_%28page_19%29.jpg",
"status": "ok",
"errorCode": null,
"errorMessage": null,
"fileName": "(Interim_Review_of_Reports)-_Neches_River_and_Tributaries_Salt_Water_Barrier_at_Beaumont_Texas_-_DPLA_-_3d1ae87deadc66d36f1932bb17bb728a_(page_19).jpg",
"fileType": "jpeg",
"fileSizeBytes": 332303,
"fileSha256": "53275d9ecd0653eb627e6a84a3ff077237c3d9ce5da98221a1a39f1eb4741be0",
"pageCount": 1,
"pagesProcessed": 1,
"pagesRecognised": 1,
"pagesFromTextLayer": 0,
"pagesBlank": 0,
"pagesFailed": 0,
"isEncrypted": false,
"languages": ["eng"],
"text": "\"In view of the statement of the rice growers along Taylors Bayou, that prior to the year 1901 oiey had ae had sufficient water for the irrigation of their crops, it is quite natural to conclude that the Port Arthur Canal is wholly responsible for the condition which prevailed this year.\"\n\nHouse Document No. 836, 61st Congress, 2nd Session, in 1910, discussed the proposed dredging of a navigation channel from the mouth of the Neches River to Beaumont. It stated (on Page 13) that:\n\n…",
"charCount": 1815,
"wordCount": 317,
"confidence": 94.7,
"lowConfidencePages": [],
"pages": [
{
"page": 1,
"source": "ocr",
"text": "\"In view of the statement of the rice growers along Taylors Bayou, that prior to the year 1901 oiey had ae had sufficient water for …",
"charCount": 1815,
"wordCount": 317,
"confidence": 94.7,
"lowConfidence": false,
"isBlank": false,
"width": 1500,
"height": 1922,
"dpi": null,
"rotation": 0,
"skewDegrees": 0,
"textHeightPx": 31,
"enlarged": false,
"lines": [
{ "text": "\"In view of the statement of the rice growers along Taylors", "confidence": 96.2, "x0": 303, "y0": 76, "x1": 1227, "y1": 107, "paragraph": 0 },
{ "text": "Bayou, that prior to the year 1901 oiey had ae had sufficient", "confidence": 85.8, "x0": 221, "y0": 109, "x1": 1245, "y1": 164, "paragraph": 0 }
],
"words": null,
"error": null,
"errorCode": null
}
],
"warnings": [],
"fullResultKey": null,
"chargedEvents": 1,
"scrapedAt": "2026-10-11T07:23:57.357Z"
}

The sample is left as it came out, mistakes included. The page says "they had always had sufficient"; the engine read "oiey had ae had sufficient". That line's confidence is 85.8 where its neighbours score 96, which is how the line and word boxes let you find the doubtful spots without reading everything.

id is derived from the URL or record you gave, so it is the same on every run. fileSha256 changes when the file does. Fields that do not apply are null.

Confidence

confidence is the engine's own certainty about the words it returned, averaged over the page with long words counting for more. It is a good warning light and a poor measurement:

  • 93 to 96 is what clean scans scored in our tests, at 0.2 to 1 % of characters wrong.
  • 80 to 92: readable with some wrong words. A 72 dpi page read without enlarging it scored 82, with one word in ten wrong.
  • Below 70 sets lowConfidence, lists the page in lowConfidencePages and adds a warning to the item. Check that text before you use it.

A high figure does not prove the text complete. It says nothing about words the engine never saw: a line it took for a picture, a margin note it skipped.

Positions

With boxes set to lines or words, every line or word has x0, y0, x1, y1 in pixels of the page image, measured from the top left corner, with x1 and y1 just outside the box. The page's width and height give the size of that image: for an image file its own pixels (after the camera orientation is applied), for a PDF page the page rendered at dpi, so a position in PDF points is the pixel value times 72 divided by dpi. The boxes describe the image as you supplied it: on a page that was straightened or turned for reading they are mapped back, and rotation tells you which way the text runs. Pages read from a PDF's text layer have no boxes; set pdfOcrMode to all if you need boxes for every page. In Chinese, Japanese, Korean and Thai, what the engine calls a word is mostly a single character.

PDFs

Each selected page is looked at before anything is recognised:

  • A page with a text layer is read from it. That includes scans that already went through OCR somewhere else and carry a hidden text layer: you get that text, as good or as bad as the OCR that made it. Use pdfOcrMode: "all" to recognise such pages afresh.
  • A page without text is rendered and recognised. A scanned page is rendered at the resolution of the scan, between 150 and 300 dpi, other pages at 300 dpi.
  • A page that is one large picture with fewer than 200 characters of text on it is treated as a scan with a stamp (a document number, a "downloaded from" line) and recognised, stamp included.
  • An empty page is reported as blank.

Text-layer pages come back as the lines of the PDF with wrapped lines joined, without paragraph breaks, since a text layer has none. For PDFs that are text throughout and where you want headings, columns in reading order and chunks for a RAG index, our PDF to Markdown and Text Extractor is the better fit; this Actor is for the pages it cannot read.

Files and pages that cannot be read

A bad file never stops the run. It gets an item with status: "failed" and an errorCode, is listed in the SUMMARY record of the run's key-value store, and is not charged. When only some pages of a file fail, the file is ok, the failed pages carry error and errorCode, and a warning names them.

errorCodeMeaning
unsupported_fileThe link returns a web page, or a kind of file that is not supported. HEIC, AVIF, JPEG 2000, SVG and DjVu are recognised and named in the message.
download_failedThe server refused, timed out, or kept failing after four attempts.
too_largeThe file is over 100 MB.
too_many_pixelsThe image has more than 64 megapixels.
password_required, wrong_passwordThe PDF needs a password to open.
corrupt_fileThe file is damaged, or a page of it is.
no_pages_selectedThe page range lies outside the file.
timeout, out_of_memoryThe page was given up on. Running with more memory helps with the second.
invalid_url, empty_file, record_not_found, internal_errorAs named.

A file in which no text was found (a photograph, a blank scan) has status: "no_text". That is a result, not a failure, and it is free.

How accurate it is

Measured with this Actor on a desktop machine, against text we know to be right. Character error rate (CER) is the share of characters wrong, word error rate (WER) the share of words; typographic quotes and dashes were counted as equal to plain ones.

InputPagesCERWER
Book page, real library scan, colour JPEG at about 250 dpi10.35 %1.15 %
The same page skewed by 3 and by 7 degrees20.28 %, 0.21 %1.15 %, 0.77 %
The same page turned 90, 180 and 270 degrees30.28 to 0.35 %0.77 to 1.15 %
The same page reduced to 150, 100 and 75 dpi30.21 %, 0.49 %, 0.28 %0.77 %, 1.92 %, 1.15 %
Four such pages as a scanned PDF without text layer40.51 %2.36 %
Typewritten government report page, real archive scan10.88 %1.57 %
Dense report page printed and scanned at 300, 200 and 150 dpi (simulated scan)60.03 to 0.37 %0.21 to 1.15 %
Screenshot of that page at 96 and 72 dpi20.03 %, 0.14 %0.23 %, 0.92 %
Noisy greyscale scan of that page at 72 dpi10.37 %2.53 %

The book pages are from a 1925 novel whose scan has a proofread transcription on Wikisource; the typewritten page is the one in the example, transcribed by hand. The dense pages are a modern report rendered to pixels, then blurred, tilted, given noise and saved as JPEG, which is kinder than a real scanner. Every one of these is clean, printed, single-column English. Expect worse on anything else.

What it does not do well

  • Handwriting is not read. Handwritten notes on a typed page come out as stray characters.
  • Tables are returned as lines of text, one per row, cells separated by a space. Column structure is not kept.
  • Forms and receipts are read line by line. Small print, dotted lines and boxes around letters cause mistakes.
  • Columns are usually read in order, column by column. Sidebars, captions between columns and text wrapped around pictures can come out in the wrong place.
  • Text inside pictures: large words on a poster are read, with low confidence; text over a busy photograph mostly is not.
  • Light text on dark is read when the whole image is like that. Light-on-dark bars and buttons inside an otherwise ordinary screenshot are often missed.
  • Heavy skew and perspective: a 10 degree skew was corrected on a 300 dpi page and lost the page at 150 dpi. A photo taken at an angle, a curled page or a book gutter is not flattened.
  • Very small text: under about 8 pixels of line height even the enlarged image reads badly.
  • Formulas, music, barcodes are not read. Stamps, seals and signatures produce stray characters.
  • Orientation of sparse pages: a page with only a few words that also lies on its side may be read as it lies.
  • Old typefaces: the long s of 18th-century print and blackletter need models that are not bundled.
  • PDF form fields are not rendered, so text typed into a fillable form is not seen when the page is recognised.

Limits

  • File size: 100 MB per file.
  • Image size: 64 megapixels, which covers an A4 sheet scanned at 600 dpi and 50-megapixel phone cameras. A PDF page is rendered at up to 25 megapixels; a larger page is rendered at a lower resolution.
  • Languages: up to 5 per run.
  • Result size: Apify dataset items hold about 9 MB. A larger result (a long file with word boxes) is saved whole as a JSON record in the run's key-value store; the item then leaves out the page array and names the record in fullResultKey.
  • Stalls: a page that makes no progress for three minutes is given up on and reported, and the run moves on.

Speed and memory

On one CPU core of our test machine: a typed letter takes about a second, an ordinary page of 3,000 characters 1.3 to 1.7 seconds depending on resolution, a dense two-column page of 5,000 characters about 3 seconds. A skewed page takes a quarter to a half longer, a page that lies upside down or on its left side two to three times as long, a 38-megapixel image 7 seconds. A blank white page takes a tenth of a second and a page read from a text layer a few milliseconds.

Apify gives a run one CPU core per 4 GB of memory, and the Actor works on as many pages at once as it has cores: one at the default 4 GB, four at 16 GB. So more memory means a faster run at the same price per page. The memory itself is not the constraint: the whole process peaked at 0.4 GB on ordinary pages and 0.55 GB on the 38-megapixel image with one worker, and at 1.6 GB with eight.

Pricing

Pay per page: $4 per 1,000 pages recognised ($0.004 a page). There is no monthly fee; starting a run costs $0.00005.

  • A recognised page is an image, or a page of a PDF or TIFF, that went through OCR and returned text.
  • PDF pages read from the text layer cost one page per started 100 of them: a 30-page PDF with 4 scanned pages costs 5 pages.
  • Free: blank pages, pages and files that could not be read, links that are not an image or a PDF.

chargedEvents in every item shows what the file cost. maxPagesPerFile caps one file, and the Maximum cost per run setting caps the run: the Actor stops cleanly at the limit, a long file is read as far as the budget pays for and says so in warnings, and files it did not reach are counted in the SUMMARY record.

FAQ

Which engine does the reading? Tesseract 5 with its "fast" neural models, compiled to WebAssembly, and PDFium to render PDF pages. Both run inside the Actor. Nothing is sent to an outside service, and the files are not kept after the run.

Why is my text full of mistakes? Look at confidence and at textHeightPx. If the lines are under 20 pixels high the image is too small: scan at 300 dpi, or send the original instead of a thumbnail. If the page is a photo, crop it to the paper and take it square on. If the language is not English, set languages.

Can I get the text of a searchable PDF re-recognised? Yes, with pdfOcrMode: "all". Every page is then charged as a recognised page.

Does it keep the layout? Paragraphs and line order, yes. Fonts, sizes, bold, columns as columns and tables as tables, no. Use the line or word boxes if you need to rebuild a layout yourself.

How do I process files I already have on Apify? Put them in a key-value store and give its ID in keyValueStoreId.

The orientation is wrong. A page is first read as it lies; only when that reads badly is it tried another way up. Turn autoRotate off if your pages are always upright and you want no second guessing.

Something looks wrong. Open an issue on the Issues tab with the run ID and, if you can, the file's URL.

Credits

Text recognition by Tesseract and Leptonica through tesseract.js-core, with language models from tessdata_fast; PDF rendering by PDFium through @hyzyla/pdfium; TIFF decoding by UTIF.js. The example files are public-domain scans from the U.S. National Archives, hosted by Wikimedia Commons.