# OCR: Image and Scanned PDF to Text (`antsonahill/ocr-image-pdf-to-text`) Actor

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.

- **URL**: https://apify.com/antsonahill/ocr-image-pdf-to-text.md
- **Developed by:** [Anton Sajin](https://apify.com/antsonahill) (community)
- **Categories:** AI, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 page recogniseds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## OCR: Image and Scanned PDF to Text

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

| | |
| --- | --- |
| **Formats** | PNG, 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 scan** | A 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 down** | Found and read the right way up; `rotation` reports 90, 180 or 270. Camera orientation (Exif) is applied first. |
| **Skewed scans** | Straightened when the skew is one degree or more; `skewDegrees` reports the angle. Tested up to 10 degrees. |
| **Low resolution** | A 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 pages** | Reported as `isBlank`, quickly, and not charged. |
| **30 languages** | See the list below. Up to 5 in one run. |
| **Bad files** | A 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

```json
{
    "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

| Field | Use |
| --- | --- |
| `fileUrls` | Direct 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. |
| `files` | Files uploaded from your computer in the Apify Console. |
| `keyValueStoreId` | A 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. |
| `recordKeys` | Read only these records of the store, whatever their keys look like. |

#### Options

| Field | Default | What it does |
| --- | --- | --- |
| `languages` | `["eng"]` | Languages of the text, main language first. Choose only those that occur. |
| `autoRotate` | `true` | Straighten skewed pages and read pages that lie on their side or upside down. |
| `pdfOcrMode` | `auto` | `auto` recognises only PDF pages without a text layer. `all` recognises every page. |
| `pageRange` | all pages | Pages to read from PDFs and multi-page TIFFs, for example `1-5, 8, 12-`. |
| `maxPagesPerFile` | no limit | Stop reading a file after this many pages. |
| `password` | none | Opens PDFs that need a password. PDFs that only restrict printing or copying open without one. |
| `boxes` | `none` | `lines` or `words` adds the rectangle and confidence of every line or word. |
| `keepLineBreaks` | `false` | Return 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:

```json
{
    "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](https://apify.com/antsonahill/pdf-to-markdown-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.

| `errorCode` | Meaning |
| --- | --- |
| `unsupported_file` | The 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_failed` | The server refused, timed out, or kept failing after four attempts. |
| `too_large` | The file is over 100 MB. |
| `too_many_pixels` | The image has more than 64 megapixels. |
| `password_required`, `wrong_password` | The PDF needs a password to open. |
| `corrupt_file` | The file is damaged, or a page of it is. |
| `no_pages_selected` | The page range lies outside the file. |
| `timeout`, `out_of_memory` | The page was given up on. Running with more memory helps with the second. |
| `invalid_url`, `empty_file`, `record_not_found`, `internal_error` | As 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.

| Input | Pages | CER | WER |
| --- | --- | --- | --- |
| Book page, real library scan, colour JPEG at about 250 dpi | 1 | 0.35 % | 1.15 % |
| The same page skewed by 3 and by 7 degrees | 2 | 0.28 %, 0.21 % | 1.15 %, 0.77 % |
| The same page turned 90, 180 and 270 degrees | 3 | 0.28 to 0.35 % | 0.77 to 1.15 % |
| The same page reduced to 150, 100 and 75 dpi | 3 | 0.21 %, 0.49 %, 0.28 % | 0.77 %, 1.92 %, 1.15 % |
| Four such pages as a scanned PDF without text layer | 4 | 0.51 % | 2.36 % |
| Typewritten government report page, real archive scan | 1 | 0.88 % | 1.57 % |
| Dense report page printed and scanned at 300, 200 and 150 dpi (simulated scan) | 6 | 0.03 to 0.37 % | 0.21 to 1.15 % |
| Screenshot of that page at 96 and 72 dpi | 2 | 0.03 %, 0.14 % | 0.23 %, 0.92 % |
| Noisy greyscale scan of that page at 72 dpi | 1 | 0.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](https://github.com/tesseract-ocr/tesseract) and [Leptonica](http://www.leptonica.org/) through [tesseract.js-core](https://github.com/naptha/tesseract.js-core), with language models from [tessdata_fast](https://github.com/tesseract-ocr/tessdata_fast); PDF rendering by [PDFium](https://pdfium.googlesource.com/pdfium/) through [@hyzyla/pdfium](https://github.com/hyzyla/pdfium); TIFF decoding by [UTIF.js](https://github.com/photopea/UTIF.js). The example files are public-domain scans from the U.S. National Archives, hosted by Wikimedia Commons.

# Actor input Schema

## `fileUrls` (type: `array`):

Direct links to images (PNG, JPEG, WebP, TIFF, BMP, GIF) or PDFs, one per line. Redirects are followed. The kind of file is read from its first bytes, so a link without a file extension works. A link that returns a web page is reported and not charged.

## `files` (type: `array`):

Upload files from your computer instead of, or in addition to, giving URLs. Each file can be up to 100 MB.

## `languages` (type: `array`):

The languages the text is written in, main language first. Choose only the ones that occur: every extra language slows recognition down and can make it worse. Up to 5.

## `autoRotate` (type: `boolean`):

Straighten pages scanned at an angle, and read pages that lie on their side or upside down. A page that reads well as it is costs nothing extra; only a page that reads badly is tried another way up. Turn this off if your images are always upright and you want the page taken exactly as it is.

## `pdfOcrMode` (type: `string`):

Many PDFs are part text, part scan. By default a page that has a text layer is read from it, which is exact and nearly free, and only pages without one are recognised. Choose every page to ignore text layers, for instance when a PDF carries a poor text layer from an earlier OCR pass.

## `pageRange` (type: `string`):

Pages to read from every PDF and multi-page TIFF, for example 1-5, 8, 12- (12 to the end). Pages are numbered from 1. Leave empty for all pages.

## `maxPagesPerFile` (type: `integer`):

Stop reading a file after this many pages. Every recognised page is charged, so this caps what one long scan can cost. Leave empty for no limit.

## `password` (type: `string`):

Password for PDFs that need one to open. It is tried on every protected PDF in the run. PDFs that only restrict printing or copying open without it.

## `boxes` (type: `string`):

Add the rectangle of every line or every word, in pixels of the page image, with its confidence. Use this to highlight text on the image, to read fields by position or to check doubtful words. Word boxes make the result several times larger.

## `keepLineBreaks` (type: `boolean`):

By default the lines of a paragraph are joined and words hyphenated at a line end are put back together, which is what you want for search, for an LLM or for reading. Turn this on to get every line as it stands on the page, which suits receipts, forms and anything laid out in rows.

## `keyValueStoreId` (type: `string`):

Read images and PDFs that another Actor or your own code saved as records in a key-value store, for example screenshots or files downloaded by a crawler. By default every record whose key ends in .pdf, .png, .jpg, .jpeg, .webp, .tif, .tiff, .bmp or .gif is read.

## `recordKeys` (type: `array`):

Keys of the records to read from the key-value store, whatever their names look like. Leave empty to read every record whose key ends like an image or a PDF.

## Actor input object example

```json
{
  "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/ee/Typed_Copy_of_Letter_from_Chief_Clerk_of_the_Department_of_State_R._S._Chew_to_Chief_Clerk_of_the_U._S._Senate_William_J._McDonald_-_DPLA_-_0539575785f626c2d8e02ed29fac0191.pdf",
    "https://upload.wikimedia.org/wikipedia/commons/3/3a/Memorandum_of_Notes_on_the_National_Aeronautics_and_Space_Administration_for_Dr._James_Killian_-_DPLA_-_3e097b200e80f1cb8dd057a660399442.pdf"
  ],
  "languages": [
    "eng"
  ],
  "autoRotate": true,
  "pdfOcrMode": "auto",
  "boxes": "none",
  "keepLineBreaks": false
}
```

# Actor output Schema

## `files` (type: `string`):

One item per image or PDF with its text, the text and confidence of every page and, if requested, word or line boxes.

## `summary` (type: `string`):

Per-file status: pages recognised, pages read from a text layer, events charged, and why any file could not be read.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "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/ee/Typed_Copy_of_Letter_from_Chief_Clerk_of_the_Department_of_State_R._S._Chew_to_Chief_Clerk_of_the_U._S._Senate_William_J._McDonald_-_DPLA_-_0539575785f626c2d8e02ed29fac0191.pdf",
        "https://upload.wikimedia.org/wikipedia/commons/3/3a/Memorandum_of_Notes_on_the_National_Aeronautics_and_Space_Administration_for_Dr._James_Killian_-_DPLA_-_3e097b200e80f1cb8dd057a660399442.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("antsonahill/ocr-image-pdf-to-text").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "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/ee/Typed_Copy_of_Letter_from_Chief_Clerk_of_the_Department_of_State_R._S._Chew_to_Chief_Clerk_of_the_U._S._Senate_William_J._McDonald_-_DPLA_-_0539575785f626c2d8e02ed29fac0191.pdf",
        "https://upload.wikimedia.org/wikipedia/commons/3/3a/Memorandum_of_Notes_on_the_National_Aeronautics_and_Space_Administration_for_Dr._James_Killian_-_DPLA_-_3e097b200e80f1cb8dd057a660399442.pdf",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("antsonahill/ocr-image-pdf-to-text").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "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/ee/Typed_Copy_of_Letter_from_Chief_Clerk_of_the_Department_of_State_R._S._Chew_to_Chief_Clerk_of_the_U._S._Senate_William_J._McDonald_-_DPLA_-_0539575785f626c2d8e02ed29fac0191.pdf",
    "https://upload.wikimedia.org/wikipedia/commons/3/3a/Memorandum_of_Notes_on_the_National_Aeronautics_and_Space_Administration_for_Dr._James_Killian_-_DPLA_-_3e097b200e80f1cb8dd057a660399442.pdf"
  ]
}' |
apify call antsonahill/ocr-image-pdf-to-text --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,antsonahill/ocr-image-pdf-to-text"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Pggveu1tby6fji1Qh/builds/jideqwhaZWICr6RQp/openapi.json
