Image to Text & PDF OCR: Searchable PDF, 100+ Languages
Pricing
from $1.20 / 1,000 ocr pages
Image to Text & PDF OCR: Searchable PDF, 100+ Languages
Image to text and PDF OCR for screenshots (dark mode too), photos, receipts and scanned PDFs. No OCR API key, built-in Tesseract. Bulk OCR from URLs, base64, an Apify dataset, CSV, Excel or Google Sheet. 100+ languages, RAG chunks, searchable PDF. From $1.20/1,000 OCR pages ($2 on the Free plan).
Pricing
from $1.20 / 1,000 ocr pages
Rating
0.0
(0)
Developer
Otama Labs
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
16 days ago
Last modified
Categories
Features
Share
Image to text OCR and bulk OCR for screenshots (dark mode too), photos, receipts and scanned PDFs (PDF OCR with an optional searchable PDF), for developers building RAG and AI-agent pipelines and for anyone who has to dig text out of screens and scans. No OCR API key is needed: Tesseract runs inside the Actor. Send images or scanned PDFs (PNG, JPEG, TIFF incl. multi-page, WebP, GIF, BMP, PDF) one at a time or in bulk, as links, a column of a spreadsheet or Google Sheet, an Apify dataset, or base64, in 100+ languages (Arabic, Hindi, Thai, Cyrillic and more; Japanese, Chinese and Korean also in vertical writing). You get the text of every page, RAG-ready chunks with page numbers, optional word boxes with confidence, and an optional searchable PDF (grayscale page images with an invisible text layer).
From $1.20 per 1,000 OCR pages ($2 on the Free plan, less on paid plans). PDF pages that already have text are read without OCR for $0.30 per 1,000, and blank pages and failures are free. Your files are not sent to a third-party OCR API.
What you can use it for
- Screenshot to text: app screens, chat windows, dashboards and error messages become text. Dark mode works as is: in our tests, a dark web page with white text was read at a mean confidence of about 93 (also at half size, because images under 1,200 px on the long side are enlarged before OCR), and a full-HD dark screen with small letters at about 91.
- RAG and AI agents: scanned contracts, letters, receipts and old reports become text with page numbers, ready for a vector database. Agents can send a file they hold as base64.
- A sheet full of links: a CSV, Excel file or Google Sheet with a column of receipt, invoice or product-label image links is read row by row, and each result keeps the row's own columns (
sourceRow) so you can join it back by ID or SKU. - After another Actor: point the Actor at the dataset of a scraper run that collected image or PDF URLs, or let it start automatically after each successful run through an integration (see the FAQ). When a field holds a list of links, such as all the images of a carousel post or a product gallery, set URL column to that field: every image is read and comes back with its row and its position (
urlIndex). - Searchable archives: turn a folder of scans into searchable PDFs that open in any PDF viewer.
- Mixed PDFs: a PDF where only some pages are scans is handled in one run; text pages are copied exactly and charged at the lower price.
- Data entry: word boxes (position and confidence) let you pick out fields such as totals and dates.
Output example
The prefilled example (phototest.tif from the Tesseract test set), Output format = Text (one row per file):
{"url": "https://raw.githubusercontent.com/tesseract-ocr/test/main/testing/phototest.tif","fileName": "phototest.tif","fileType": "tiff","pageCount": 1,"pagesProcessed": 1,"pagesOcr": 1,"pagesTextLayer": 0,"pagesBlank": [],"pagesFailed": [],"languages": "eng","meanConfidence": 96.4,"text": "This is a lot of 12 point text to test the\nocr code and see if it works on all types\nof file format.\n\nThe quick brown dog jumped over the\nlazy fox. ...","charCount": 285,"pages": [{ "page": 1, "method": "ocr", "confidence": 96.4, "rotation": 0, "text": "This is a lot of 12 point text to test the\n..." }],"searchablePdfUrl": null,"error": null,"processedAt": "2026-10-21T03:49:28.376303+00:00"}
The same page turned sideways (phototest-rotated-L.png) comes back with the same text and "rotation": 90. A two-page PDF that already has text comes back with "method": "text-layer" on both pages and is charged as two text-page events.
With Include word boxes, each page also has words, e.g. { "text": "quick", "confidence": 95.9, "left": 102, "top": 194, "width": 75, "height": 30, "block": 1, "line": 1 }. Positions are in pixels of the original image (for PDF pages: pixels at 300 DPI), measured on the page after its orientation was fixed.
How to use
- Give the Actor the files in one of these ways (you can combine them in one run):
- Image or PDF URLs: public links, pre-signed S3 or Cloud Storage URLs, direct Dropbox links, Apify key-value store records.
- Bulk: an Apify dataset, or the link of a CSV, Excel or JSON file or a Google Sheet shared as "Anyone with the link", with one file link per row (in a dataset or JSON file, also a field with a list of links). The column with the links is found automatically, or name it in URL column.
- Files as base64:
[{"name": "receipt.jpg", "data": "<base64>"}], for agents and scripts that hold the file itself.
- Pick the Languages of the text (English by default, up to 3 at once).
- Optionally turn on Create a searchable PDF or Include word boxes, or choose RAG chunks as the output format.
- Press Start and wait for the run to finish (seconds for a few images). Each file is one row in the run's dataset, which you can export in several formats or fetch over the API.
The prefilled example reads phototest.tif, a test image from the Tesseract project, and finishes in under a minute.
Input
| Field | What it does |
|---|---|
| Image or PDF URLs | Links to images (PNG, JPEG, TIFF, WebP, GIF, BMP) or PDFs. The type is detected from the file content. Up to 50 MB per file. You can also link a text file that lists URLs (up to 1,000). |
| Languages | Up to 3 at once, English by default. 100+ languages (123 language models, counting vertical and historic variants), e.g. eng, deu, fra, spa, por, ita, rus, ukr, ara, heb, hin, ben, tha, vie, ind, jpn, jpn_vert, chi_sim, chi_tra, kor; the full list is in the input form. |
| Output format | text (one row per file with a pages list) or chunks (one row per RAG chunk with pageStart / pageEnd). |
| Create a searchable PDF | Saves one PDF per file in the run's key-value store; the link is in searchablePdfUrl. No extra charge. |
| Include word boxes | Adds every word with its confidence (0-100) and pixel position to each page. |
| Apify dataset with file links | A dataset ID, e.g. from a scraper run. Up to 5,000 items, one file per item or every link of a list field. |
| Spreadsheet URL | A public CSV, Excel (.xlsx, first sheet), JSON or JSON Lines file, or a Google Sheets link. Up to 5,000 rows. |
| URL column | The column or field with the links (dots for nested fields, e.g. receipt.url). A field with a list of links (e.g. images) gives one result per link, up to 50 per row. Empty = the column with the most links. |
| Files as base64 | A list of {"name", "data"} objects; data is base64 or a data: URL. The whole input must stay under about 9 MB. |
| Fix page orientation | On by default: pages that are sideways or upside down are turned before OCR. |
| OCR pages that already have text | Off by default. Turn on if a PDF's existing text layer is wrong. |
| Max pages per file | 0 = all, up to 500. |
| Chunk size / Chunk overlap | For RAG chunks: maximum characters per chunk (default 2,000) and characters repeated from the previous chunk (default 200). |
With bulk input, every result row also has rowNumber (1 = first data row; empty rows are not counted) and sourceRow (the row's own columns, up to 30, values cut at 500 characters). A CSV that is just a list of links, without a header row, works too. Rows without a link are skipped; a cell that is not exactly one http(s) link gives a free error row. In a dataset or JSON file, a field that holds a list (of links, or of objects with a link) gives one result per link with urlIndex, its position in the list (1 = first). In a list of objects, the link that looks like an image or PDF is taken, or name the field inside, e.g. childPosts.displayUrl. Single-link columns are preferred when the column is guessed, so name a list field in URL column. Up to 50 links per row and 5,000 files per dataset or sheet are read (the rest is reported in the run log and SUMMARY). The same link in several rows is read and charged once. Rows after the first 5,000 are not read (the run log and SUMMARY say so). If you leave the form's example link in Image or PDF URLs next to bulk or base64 input, it is skipped.
How it works
- Each file is downloaded (at most one request per second to the same host; busy servers are retried twice).
- PDF pages with a usable text layer are read directly. Other pages are rendered at 300 DPI; images are converted to grayscale, and images under 1,200 px on the long side (small screenshots, thumbnails) are enlarged so small letters can be read.
- Tesseract detects the orientation, then recognizes the text (LSTM engine). A page that comes back empty is read once more as sparse text (not with the vertical variants), which catches a label, a screenshot or a single word that the page layout analysis misses; that second reading counts only when Tesseract is fairly sure (mean confidence 60 or more, with at least one sure word of 3 or more letters), so specks and noise stay blank. Each page gets a
method:ocr,text-layer,blankorfailed. - With a searchable PDF, OCR'd pages are rebuilt as image + invisible text and other pages are copied as they are, in the original order.
Pricing
Pay per event:
| Event | Free plan | Bronze | Silver | Gold and above |
|---|---|---|---|---|
ocr-page: a page read by OCR that has text | $0.002 | $0.0018 | $0.0015 | $0.0012 |
text-page: a PDF page read from its own text layer | $0.0003 | $0.0003 | $0.0003 | $0.0003 |
Plus Apify's tiny start fee of $0.00005 per GB of memory per run (the default 2 GB counts as two). Platform usage is included.
Examples on the Free plan: a 10-page scanned PDF costs $0.02. A 30-page PDF where 3 pages are scans costs 3 × $0.002 + 27 × $0.0003 = $0.0141. 1,000 receipts photographed with a phone cost $2.
Blank pages, pages OCR cannot process and files that cannot be downloaded are reported and not charged. If you give the run a spending limit, the Actor reads only as many pages as the limit covers and skips the remaining files.
Limits
- OCR quality depends on the scan: clear printed text at 200-300 DPI works best. Handwriting is not supported. Low-contrast photos, decorative fonts and very small text give lower confidence (see
meanConfidenceand the per-word confidence). - Phone photos of receipts: in a test, a receipt photographed with a phone was read at mean confidence 76, with the shop name, address and date right and a few errors in the item lines. Check the items before you rely on them.
- Layout is returned as text lines and paragraphs; tables come out as text lines, not Markdown tables.
- Up to 50 MB per file, 500 pages per file and about 100 megapixels per image (larger images are reported as an error). For very long documents the row keeps the full text and the per-page methods; the per-page texts are dropped (
pageTextsOmitted) so the row fits the 9 MB dataset limit. OCR takes roughly a few seconds per page with the default 2 GB of memory; give the run more memory (4 GB = one full CPU core, 8 GB = two) to process pages in parallel. - Password-protected PDFs are not supported.
- For vertical Japanese, Chinese or Korean text, pick the vertical variant (
jpn_vert,chi_sim_vert,chi_tra_vert,kor_vert): the page is then read as top-to-bottom columns from right to left, and the orientation check is skipped. A page that mixes vertical and horizontal text reads best in two runs, one with each variant. On a clean three-column sample of printed vertical Japanese,jpn_vertread every character (mean confidence 90). For Japanese and Chinese, the spaces Tesseract puts between words are removed from the text (word boxes are unchanged). - Base64 files travel inside the run input, which Apify limits to about 9 MB; send bigger files as links.
- The searchable PDF is built from grayscale page images, so colors are not kept. Keep the original if you need it.
- A PDF page that has only a short text layer (under 200 characters) on top of a picture, such as a scan with a watermark, is OCR'd rather than trusted.
Data and privacy
OCR happens in the Actor's own container with Tesseract; no file, page image or text leaves your run for an outside OCR service. Nothing is fetched besides the links you list, and the text, word boxes and searchable PDFs are written only to your run's storage. Please OCR only files you are allowed to process.
FAQ
Why is a page empty? OCR found no text on it (a blank or photo-only page). It is listed in pagesBlank and not charged.
What does meanConfidence mean? The average confidence Tesseract gives the words on the OCR pages (0-100). Values above 85 usually mean clean text; below 60, check the scan.
How do I OCR a list of image links in a Google Sheet? Share the sheet as "Anyone with the link", paste its link into Spreadsheet URL, and run. Each row's other columns come back in sourceRow; export the dataset as CSV or Excel to get a sheet back.
How do I call it from code, an agent or a no-code tool? Start a run through the Apify API with {"urls": [...], "outputFormat": "chunks"} (or base64Files, datasetId, fileUrl) and read the dataset; run-sync-get-dataset-items does both in one request for small batches. LangChain and LlamaIndex have Apify loaders that take the dataset rows directly. Make, n8n and Zapier have Apify modules, and AI agents can find and run the Actor through the Apify MCP server.
How do I run OCR automatically after another Actor (a scraper, an image downloader)? Open that Actor's Integrations tab, choose Add integration and pick this Actor. In the integration's Actor input, set Apify dataset with file links (datasetId) to {{resource.defaultDatasetId}} and URL column (urlField) to the field with the links. If the form does not let you type the variable into the dataset field, enter the input as JSON: {"datasetId": "{{resource.defaultDatasetId}}", "urlField": "imageUrl"} (with your field name). Each time the other Actor's run succeeds, this Actor reads that run's dataset. We tested this setup as an integration created through the Apify API, after an image downloader Actor ("urlField": "sourceUrl"): the image of the previous run was read. The dataset has to be given in that field: this Actor runs with limited permissions and cannot read the dataset from the integration's payload alone, so without it the run stops with a hint (and does not read the form's example link).
Why did I get a wordsUrl instead of words in the row? A dataset row is limited to about 9 MB. When a long document with word boxes would exceed that, the words are saved as a JSON record in the run's key-value store and the row links to it.
Which OCR engine is used? Tesseract (Apache-2.0) with the language data packaged by Debian (the fast LSTM models).
Can it read handwriting? No. Tesseract is trained on printed text.
Related Actors by the same author
| Actor | What it does |
|---|---|
| PDF to Markdown | Digital documents to Markdown and RAG chunks with headings and tables. Use it for PDFs that have text; use this Actor for scans. |
| Website Change Monitor | See what changed on a list of web pages since the last run. |
| ATS Jobs | Every open job of chosen companies from their Greenhouse, Lever, Ashby and other job boards. |
| RemoteOK & Jobicy Remote Jobs API | Remote jobs from two official APIs, with salary and source credit. |
For AI Agents
An agent that meets a scan, a photo of a receipt or a PDF without a text layer can hand it to this Actor and get text back. There is no login and no API key of a third-party OCR service; the run is paid per page through Apify (pay per event) and uses limited permissions.
Connect through the Apify MCP server and expose just this Actor:
{"mcpServers": {"pdf-ocr": {"url": "https://mcp.apify.com?tools=tamatama/image-pdf-ocr"}}}
Sign in with OAuth when the client asks, or add the header Authorization: Bearer <APIFY_TOKEN>. The Actor then shows up as its own tool, tamatama--image-pdf-ocr, and the server adds get-dataset-items for paging through long results. Input an agent usually sends:
{ "urls": [{ "url": "https://example.com/scan.pdf" }], "languages": ["eng", "deu"], "outputFormat": "text" }
When the agent holds the file itself (an upload, an e-mail attachment), it can send it as base64 instead of a link:
{ "base64Files": [{ "name": "receipt.jpg", "data": "/9j/4AAQSkZJRgABAQ..." }], "languages": ["eng"] }
Tips for agents: in the default text format each file row has meanConfidence (check it before trusting the text) and pagesFailed / error (read them when a row looks empty). Chunk rows (outputFormat: "chunks") carry only the text and page range; page counts and pagesFailed per file are then in the SUMMARY record of the run's key-value store (meanConfidence is only in text rows). Set searchablePdf: true when the user wants a file back; the row then has a searchablePdfUrl.
OpenAPI for tool importers: https://api.apify.com/v2/acts/tamatama~image-pdf-ocr/builds/default/openapi.json. One-request REST call: POST https://api.apify.com/v2/acts/tamatama~image-pdf-ocr/run-sync-get-dataset-items with the JSON input.