๐Ÿฆ– TinyRex Data Toolspractical guides for web data APIs

How to extract text from hundreds of PDFs, including scanned ones, in one run

Updated 2026-10-28 ยท Tool used: PDF Text Extractor & OCR - Bulk PDF and Image to Text on Apify ยท Price: $1.50 per 1,000 PDFs + $0.002 per OCR page; failed downloads, non-PDF links, password-protected files and blank pages are free

Reports, invoices, tender documents and archives usually arrive as a pile of PDFs, and some of them are scans with no selectable text at all. Online converters handle one file at a time, and sending every page through a paid OCR API is wasteful when most PDFs already contain exact text.

This guide shows how to pass a list of PDF or image links and get one row per file with the full text, page count and the method used. Digital PDFs are read from their text layer (fast, no OCR errors) and only pages without text go through Tesseract OCR, so you only pay the OCR price where it was needed. Everything runs inside the actor, with no outside OCR service.

Open PDF Text Extractor & OCR on Apify โ†’

Steps

  1. Add direct links to fileUrls, or set datasetId to collect PDF and image links from another run (for example a sitemap export filtered to .pdf).
  2. Leave ocrMode on auto and choose languages for non-English scans. Turn on includePages if you want per-page text for RAG chunking and page citations.
  3. Run it and export the dataset as JSON or CSV, or load it straight into LangChain, LlamaIndex or a vector database.

Example input

{
  "url": "https://raw.githubusercontent.com/ocrmypdf/OCRmyPDF/v16.0.0/tests/resources/ccitt.pdf",
  "status": "ok",
  "fileType": "pdf",
  "method": "ocr",
  "pageCount": 1,
  "pagesProcessed": 1,
  "textLayerPages": 0,
  "ocrPages": 1,
  "blankPages": 0,
  "ocrLanguages": [
    "eng"
  ],
  "producer": "libtiff / tiff2pdf - 20150912",
  "createdAt": "2016-01-19T12:38:47.000Z",
  "chars": 4508,
  "words": 728,
  "text": "The LinnSequencer\n32 Track MIDI Sequence Recorder\n\nThe LinnSequencer is a state-of-the-art composition and performance tool for the professional music ...",
  "textTruncated": false,
  "charged": {
    "documents": 0,
    "ocrPages": 1
  },
  "httpStatus": 200,
  "fileSizeBytes": 103856,
  "extractedAt": "2026-10-19T07:28:29.277Z"
}

Example output (one item, shortened)

{
  "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "status": "ok",
  "fileType": "pdf",
  "httpStatus": 200,
  "error": null,
  "downloadMs": 107,
  "fileSizeBytes": 140815,
  "extractMs": 49,
  "method": "text-layer",
  "pageCount": 6,
  "pagesProcessed": 6,
  "pagesNotProcessed": 0,
  "textLayerPages": 6,
  "ocrPages": 0,
  "blankPages": 0,
  "ocrLanguages": [],
  "title": "Form W-9 (Rev. March 2024)",
  "author": "SE:W:CAR:MP",
  "subject": "Request for Taxpayer Identification Number and Certification",
  "keywords": "Fillable",
  "creator": "Designer 6.5",
  "producer": "Designer 6.5",
  "createdAt": "2024-03-06T13:18:13.000Z",
  "modifiedAt": "2024-03-06T13:18:13.000Z",
  "encrypted": false,
  "pdfVersion": "1.7",
  "chars": 37820,
  "words": 6279,
  "text": "W-9\n\nForm\n(Rev. March 2024)\nDepartment of the Treasury\nInternal Revenue Service\n\nRequest for Taxpayer\nIdentification Number and Certification\n\nGive fo ...",
  "textTruncated": false,
  "charged": {
    "documents": 1,
    "ocrPages": 0
  },
  "extractedAt": "2026-10-19T07:28:25.882Z"
}

Every run's dataset can be downloaded as JSON, CSV, Excel, XML or HTML from the Apify Console, or via https://api.apify.com/v2/datasets/<datasetId>/items?format=csv.

Run it from code

Python

from apify_client import ApifyClient  # pip install apify-client

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run_input = {
  "url": "https://raw.githubusercontent.com/ocrmypdf/OCRmyPDF/v16.0.0/tests/resources/ccitt.pdf",
  "status": "ok",
  "fileType": "pdf",
  "method": "ocr",
  "pageCount": 1,
  "pagesProcessed": 1,
  "textLayerPages": 0,
  "ocrPages": 1,
  "blankPages": 0,
  "ocrLanguages": [
    "eng"
  ],
  "producer": "libtiff / tiff2pdf - 20150912",
  "createdAt": "2016-01-19T12:38:47.000Z",
  "chars": 4508,
  "words": 728,
  "text": "The LinnSequencer\n32 Track MIDI Sequence Recorder\n\nThe LinnSequencer is a state-of-the-art composition and performance tool for the professional music ...",
  "textTruncated": false,
  "charged": {
    "documents": 0,
    "ocrPages": 1
  },
  "httpStatus": 200,
  "fileSizeBytes": 103856,
  "extractedAt": "2026-10-19T07:28:29.277Z"
}
run = client.actor("tinyrex/pdf-ocr-text-extractor").call(run_input=run_input)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

JavaScript / Node.js

import { ApifyClient } from 'apify-client'; // npm i apify-client

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('tinyrex/pdf-ocr-text-extractor').call({
  "url": "https://raw.githubusercontent.com/ocrmypdf/OCRmyPDF/v16.0.0/tests/resources/ccitt.pdf",
  "status": "ok",
  "fileType": "pdf",
  "method": "ocr",
  "pageCount": 1,
  "pagesProcessed": 1,
  "textLayerPages": 0,
  "ocrPages": 1,
  "blankPages": 0,
  "ocrLanguages": [
    "eng"
  ],
  "producer": "libtiff / tiff2pdf - 20150912",
  "createdAt": "2016-01-19T12:38:47.000Z",
  "chars": 4508,
  "words": 728,
  "text": "The LinnSequencer\n32 Track MIDI Sequence Recorder\n\nThe LinnSequencer is a state-of-the-art composition and performance tool for the professional music ...",
  "textTruncated": false,
  "charged": {
    "documents": 0,
    "ocrPages": 1
  },
  "httpStatus": 200,
  "fileSizeBytes": 103856,
  "extractedAt": "2026-10-19T07:28:29.277Z"
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);

curl (no SDK)

# Runs the actor and returns the results directly (CSV here; use format=json for JSON)
curl -X POST "https://api.apify.com/v2/acts/tinyrex~pdf-ocr-text-extractor/run-sync-get-dataset-items?format=csv" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://raw.githubusercontent.com/ocrmypdf/OCRmyPDF/v16.0.0/tests/resources/ccitt.pdf", "status": "ok", "fileType": "pdf", "method": "ocr", "pageCount": 1, "pagesProcessed": 1, "textLayerPages": 0, "ocrPages": 1, "blankPages": 0, "ocrLanguages": ["eng"], "producer": "libtiff / tiff2pdf - 20150912", "createdAt": "2016-01-19T12:38:47.000Z", "chars": 4508, "words": 728, "text": "The LinnSequencer\n32 Track MIDI Sequence Recorder\n\nThe LinnSequencer is a state-of-the-art composition and performance tool for the professional music ...", "textTruncated": false, "charged": {"documents": 0, "ocrPages": 1}, "httpStatus": 200, "fileSizeBytes": 103856, "extractedAt": "2026-10-19T07:28:29.277Z"}' > results.csv

Get your API token in Apify Console โ†’ Settings โ†’ API & Integrations. The free Apify plan includes monthly credit that is enough to try this.

Use it from an AI agent (MCP)

The Apify MCP server exposes this actor as a tool for Claude, Cursor, VS Code, n8n and other MCP clients. Add this to your client's MCP config (or use the OAuth flow at mcp.apify.com instead of a token):

{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=tinyrex/pdf-ocr-text-extractor",
      "headers": {
        "Authorization": "Bearer <YOUR_APIFY_TOKEN>"
      }
    }
  }
}

Then ask in plain language, e.g. "Bulk pdf to text for this list and give me a table".

Pricing

Pay per result: $1.50 per 1,000 PDFs + $0.002 per OCR page; failed downloads, non-PDF links, password-protected files and blank pages are free. You can cap the cost of every run in the run options.

FAQ

How do I extract text from a scanned PDF?

Add its link and keep OCR mode on auto. Pages without a text layer are rendered and read by Tesseract; the row shows method: ocr and the number of ocrPages.

How can I tell if a PDF is scanned or has real text?

Each row reports textLayerPages, ocrPages and a method of text-layer, ocr or mixed.

Does it work on images like PNG or JPG?

Yes. PNG, JPEG, TIFF, WebP, GIF and BMP links are OCR'd as one page each.

Are my documents sent to a third-party API?

No. Download, text extraction (poppler) and OCR (Tesseract) all run inside the actor's container, and results stay in your own Apify storage.

Try it on Apify โ†’

More guides