# PDF Text Extractor: PDF to Text, Digital or Scanned (OCR) (`brasspage/pdf-text-extractor`) Actor

PDF text extractor and PDF to text API for digital and scanned PDFs. Get plain text, one item per PDF, with a per-page array and a .txt file. Digital pages are read from the text layer; scans and images get Tesseract 5 OCR inside the Actor, no outside AI. $0.002 per PDF plus $0.005 per OCR page.

- **URL**: https://apify.com/brasspage/pdf-text-extractor.md
- **Developed by:** [Brasspage](https://apify.com/brasspage) (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 $2.00 / 1,000 pdf processeds

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

### PDF Text Extractor: PDF to Text, Digital or Scanned (OCR)

This PDF text extractor pulls the words out of any PDF, digital or scanned, and gives them back as plain text: one dataset item per PDF, with a per-page array and a `.txt` file. Digital pages are read straight from the PDF's text layer. Scanned pages go through OCR. Run it in the Apify Console or call it as an API from your code or an AI agent. **$0.002 per PDF, plus $0.005 for each scanned page.**

- **Digital PDFs cost one flat fee.** Pages read from the text layer have no page fee, so a 300-page report is $0.002.
- **Scans work too.** Pages with no text layer (scans, faxes, photos of paper) are read by Tesseract 5, the open source OCR engine, running inside the Actor.
- **Images count.** PNG, JPEG, WEBP and multi-page TIFF files are read the same way as scanned pages.
- **Nothing leaves the run.** No page is sent to an outside AI service or any other API.
- **You pay only for text.** Blank pages, failed pages and files that fail are free.
- **The free plan works.** There is no trial cap: free-plan users run it like anyone else, paid from their Apify credit.

**Try it now.** The input comes prefilled with the 6-page IRS Form W-9 PDF. Click **Start**. All 6 pages carry a text layer, so the run costs $0.00205: $0.002 for the PDF and $0.00005 for the start.

Need Markdown with headings and tables for an LLM or RAG pipeline? [PDF to Markdown OCR](https://apify.com/brasspage/pdf-ocr-to-markdown) runs the same engine and is the better fit. This Actor is for plain text.

### Pricing

Pay per event, no monthly fee.

| Event                             | Price                                             |
| --------------------------------- | ------------------------------------------------- |
| Actor start                       | $0.00005 per run, per GB of memory (default 1 GB) |
| `pdf-processed`                   | $0.002 per PDF or image that gives you text       |
| `ocr-page`                        | $0.005 per OCR page that gives you text           |
| Text layer page                   | free                                              |
| Blank or failed page, failed file | free                                              |

Worked examples, start fee included:

| File                               | Text layer pages | OCR pages | Total    |
| ---------------------------------- | ---------------- | --------- | -------- |
| IRS Form W-9 (the prefilled input) | 6                | 0         | $0.00205 |
| A 300-page digital report          | 300              | 0         | $0.00205 |
| A scanned 10-page PDF              | 0                | 10        | $0.05205 |
| A 50-page PDF with 4 scanned pages | 46               | 4         | $0.02205 |

One PNG screenshot is $0.007 plus the start. 1,000 digital PDFs in one run are $2.00005 (raise `maxPagesPerRun` if they hold more than 1,000 pages in all).

**Your maximum cost is a hard stop.** Set **Maximum cost per run** in the run options. A page starts only if its worst case still fits: the PDF fee, plus $0.005 for this page, for each page in progress and for each OCR page already read. A page that does not fit is skipped with a note, and the run ends there, under your limit.

### How each page is read

1. The Actor downloads each file from public hosts only, checks every redirect, and finds the real file type from its first bytes.
2. A PDF page that carries real text is read from that text layer. The characters are exact, no OCR runs, and there is no page fee.
3. Every other page (a scan, a photo, an image, or a PDF page with broken font codes) is rendered at 300 dpi in grayscale, straightened, cleaned and read by Tesseract 5. These are OCR pages.
4. You get one dataset item per file and a `document-NNNN.txt` file in the run's key-value store.

Wrapped lines are joined and end-of-line hyphens removed. Tables with ruling lines come out as Markdown pipe tables, even in plain text. Set `markdownTables` to `false` to get one row per line with cells split by tabs, ready to paste into a spreadsheet. In scanned tables, a row where quantity times price does not match the amount gets a warning.

### How accurate is it?

Our own test files, run on Oct 29 and 30, 2026:

| Test file                                                                      | Result                                                             |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------ |
| Digital invoice PDF with a 13-row table                                        | 36 of 36 numbers and 65 of 65 cells exact (text layer)             |
| Typed 1942 government page, scanned at 300 dpi                                 | 98.5% of words match a reference transcript                        |
| The same invoice made to look scanned, as a PDF, on Apify                      | 36 of 36 numbers exact, 64 of 65 cells                             |
| 24 more made-up scanned invoices, 150 to 300 dpi, kept out of building the fix | 810 of 810 numbers exact (a lost comma was found here, then fixed) |
| 12 made-up scanned tables with no math between their columns                   | 391 of 393 numbers exact, and both misses carried warnings         |
| Table screenshot (PNG, 44 cells), on Apify                                     | 44 of 44 cells in 6.6 seconds                                      |
| Scanned 4-column table with a line under the header                            | 44 of 44 cells                                                     |
| Scanned 4-column table with no lines at all                                    | 44 of 44 cells, with 1 warning                                     |

In short: digital PDFs give exact text. Clean typed scans are very good, with a few wrong letters where ink is faint. Scanned numbers are good, but check any page that has `warnings`. Handwriting, tilted phone photos and tiny print give many errors. No OCR is perfect, so check numbers that matter against the original.

### Input

| Field            | What it does                                                                                                                               |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `documentUrls`   | Direct, public links to PDF, PNG, JPEG, WEBP or TIFF files, up to 100 MB each. Signed Apify record URLs work too.                          |
| `documentFile`   | One PDF or image uploaded from your computer in the Console. Works together with `documentUrls`.                                           |
| `pages`          | Pages to read in each file, for example `1-3, 5, 10-` (`10-` means page 10 to the end). Empty means every page.                            |
| `outputFormat`   | `text` (default) or `markdown`.                                                                                                            |
| `markdownTables` | `true` (default): tables as Markdown pipe tables. `false`: one row per line, cells split by tabs.                                          |
| `language`       | Language of scanned pages: English (default), Spanish, French, German, Portuguese, Russian, Arabic, Hindi, Chinese (simplified), Japanese. |
| `maxPagesPerRun` | Safety limit on total pages per run (default 1000).                                                                                        |

Example: the first 3 pages of the W-9 (digital) plus a scanned TIFF test page from the Tesseract project. Cost: $0.002 for the W-9, $0.007 for the scan, $0.00005 for the start, so $0.00905 in all.

```json
{
  "documentUrls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
    "https://raw.githubusercontent.com/tesseract-ocr/test/main/testing/phototest.tif"
  ],
  "pages": "1-3",
  "outputFormat": "text",
  "language": "eng"
}
```

### Output

One dataset item per file. This is the item for the scanned TIFF page above (the text is real output of this engine, Oct 29, 2026, shortened here):

```json
{
  "url": "https://raw.githubusercontent.com/tesseract-ocr/test/main/testing/phototest.tif",
  "fileType": "tiff",
  "pageCount": 1,
  "pagesConverted": 1,
  "ocrPages": 1,
  "text": "This is a lot of 12 point text to test the ocr code and see if it works on all types of file format.\n\nThe quick brown dog jumped over the lazy fox. ...",
  "pages": [
    {
      "page": 1,
      "text": "This is a lot of 12 point text to test the ocr code and see if it works on all types of file format.\n\n...",
      "source": "ocr",
      "meanConfidence": 95.6
    }
  ],
  "outputFormat": "text",
  "model": "Tesseract 5 OCR, run locally (pages with a text layer are read directly)",
  "fileUrl": "https://api.apify.com/v2/key-value-stores/.../records/document-0002.txt",
  "convertedAt": "2026-10-29T15:52:10+00:00"
}
```

- `text` holds all pages with text, joined by a blank line. With `outputFormat: "markdown"` the field is `markdown`, in the item and in `pages[]`.
- `pageCount` is the pages in the file. `pagesConverted` is the pages that gave text. `ocrPages` is the OCR pages that gave text: the pages charged $0.005.
- Each `pages[]` entry has `source`: `"text layer"` or `"ocr"`. OCR pages also have `meanConfidence` (Tesseract's average word confidence, 0 to 100) and, when a table cell or row looks wrong, `warnings`.
- A page that fails looks like `{ "page": 4, "error": "..." }`. A file that fails (a 404, a web page instead of a file, a password-protected PDF) gives an item with `error`. Neither is charged.
- `note` explains a stop: the page limit, the maximum cost, or text too large for one item (then download it from `fileUrl`).

### Use it from the API

One call starts the run, waits for it, and returns the dataset items as JSON:

```bash
curl -X POST "https://api.apify.com/v2/acts/brasspage~pdf-text-extractor/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"documentUrls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf"]}'
```

The sync call has a time limit, so for long batches start a normal run and read its dataset when it finishes. To give an AI agent this one tool, connect it to the Apify MCP server at `https://mcp.apify.com?tools=brasspage/pdf-text-extractor`.

### Limits

- **Pages per run:** `maxPagesPerRun`, default 1000. The run stops there, and the last dataset row has a `note`. Raise it or start another run.
- **File size:** up to 100 MB per file, 14 MB per page, and 5,000 pages per document.
- **Formats:** PDF, PNG, JPEG, WEBP and TIFF. Export Word, Excel and PowerPoint files to PDF first. Password-protected PDFs fail (free).
- **Handwriting** is not read well. Math stays as plain text.
- **Pictures and charts** give no text. On a scanned page, short labels inside a chart can be lost.
- **Dense forms** with many small boxes can come out in a different reading order than you would read them.
- **Scanned tables with no ruling lines** are rebuilt from word positions, and the math checks run only on tables with lines. A number that Tesseract is unsure of still goes to a second OCR model (English only). A misread digit that it is sure of gets no warning.
- **Text only:** no searchable PDF and no word positions. For each word with its box and confidence from an image, use [Image to Text OCR](https://apify.com/brasspage/image-to-text-ocr).

### Tested on Apify

Our own runs on Apify, Oct 30, 2026, at the default 1 GB of memory:

| Run                                                    | Pages                                 | Time   | Charged, start included        |
| ------------------------------------------------------ | ------------------------------------- | ------ | ------------------------------ |
| IRS W-9, a 3-page digital invoice and a 2-page scan    | 9 text layer, 2 OCR                   | 99 s   | $0.01605 (3 PDFs, 2 OCR pages) |
| A 48-page digital PDF (the W-9 eight times)            | 48 of 48 from the text layer          | 18.7 s | $0.00205                       |
| A 2-page scan of the invoice at 300 dpi, made into PDF | 2 OCR, 36 of 36 numbers exact on each | 47.1 s | $0.01205 (1 PDF, 2 OCR pages)  |

The same 2-page scan took 32.3 s and read 32 of 36 numbers on the build before the table math and second model (one run each). Build 0.1.8 (Oct 31, 2026) prepares scanned pages faster, with the same output: 3 made-up scans (2 invoices and a form) took 19.3 s, against 62.0 s on build 0.1.7 (one run each). The digital invoice table came out exact. The first run took 99 s because its scan was enlarged 1.5 times by our mistake, so OCR had far more pixels to read.

### FAQ

**Why not just use pdftotext?**
On a digital PDF on your own computer, it works well: in our test it read all 36 numbers of the digital invoice. On a scan it returns nothing (0 words in our test), because a scan has no text to pull. This Actor handles both kinds in one call, from a link, with nothing to install.

**Do I pay the OCR fee on digital pages?**
No. Only pages that go through OCR and give text cost $0.005. A page read from the text layer is covered by the $0.002 PDF fee.

**My scan was already made searchable by another tool. What happens?**
If its hidden text layer is usable, the Actor reads that layer: no OCR fee, and the text is only as good as that earlier OCR. A page with broken or near-empty text is OCRed.

**Where do my documents go?**
Nowhere outside the run. The Actor downloads your file, reads it inside the container and writes the results to your own Apify storage. No page goes to an AI model or any other outside service.

**Can text inside a PDF trick it?**
No. There is no AI model to give instructions to. Text in your file is only ever output as text.

**Can I use it on the Apify free plan?**
Yes, with no trial cap. Runs are paid from the credit in your Apify account, at the same prices.

**Who is responsible for the rights to the documents?**
You are. Only process files that you own, that are public, or that you have permission to use. The Actor does not get around logins, paywalls or PDF passwords.

### Issues and support

Something does not work, or you need a feature? Open an issue in the Actor's **Issues** tab, with the run URL if you have one. We reply there.

### Related Actors

- [PDF to Markdown OCR](https://apify.com/brasspage/pdf-ocr-to-markdown): the same engine, with Markdown headings, lists and tables for LLMs and RAG.
- [Image to Text OCR](https://apify.com/brasspage/image-to-text-ocr): text from images, with optional word boxes and confidence.
- [PDF to Image & Image to PDF](https://apify.com/brasspage/pdf-to-image): PDF pages to PNG, JPG or WebP.

Built and maintained by Tack, an AI agent at Brasspage.

# Changelog

This Actor's version history is a separate document: https://apify.com/brasspage/pdf-text-extractor/changelog.md

# Actor input Schema

## `documentUrls` (type: `array`):

Direct links to PDF, PNG, JPEG, WEBP or TIFF files, up to 100 MB each. Scanned and digital PDFs both work. Signed Apify key-value store record URLs also work.

## `documentFile` (type: `string`):

Upload one PDF or image (PNG, JPEG, WEBP or TIFF) from your computer. You can use it together with Document URLs.

## `pages` (type: `string`):

Optional. Pages to read in each document, for example 1-3, 5, 10- (10- means page 10 to the end). Leave empty for all pages. An image is page 1.

## `outputFormat` (type: `string`):

Plain text has no formatting. Markdown keeps headings, lists and tables.

## `markdownTables` (type: `boolean`):

On: tables become Markdown pipe tables (also in plain text output). Off: one table row per line, cells separated by tabs.

## `language` (type: `string`):

The language of scanned pages. English also reads most Latin-alphabet text, but accents come out better with the right language. Pages with a text layer are read as they are, in any language.

## `maxPagesPerRun` (type: `integer`):

Safety limit. The run stops after this many pages in total, and the last dataset row says so.

## Actor input object example

```json
{
  "documentUrls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ],
  "pages": "1-3",
  "outputFormat": "text",
  "markdownTables": true,
  "language": "eng",
  "maxPagesPerRun": 1000
}
```

# Actor output Schema

## `documents` (type: `string`):

One item per document: URL, file type, page count, pages with text, OCR pages, the full plain text (or Markdown), and a pages array with the text, source or error of each page. A last row with a note appears when the run stops at its page limit.

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

Key-value store with one document-NNNN.txt (or .md) file per document with text.

# 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 = {
    "documentUrls": [
        "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("brasspage/pdf-text-extractor").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 = { "documentUrls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf"] }

# Run the Actor and wait for it to finish
run = client.actor("brasspage/pdf-text-extractor").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 '{
  "documentUrls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ]
}' |
apify call brasspage/pdf-text-extractor --silent --output-dataset

```

## MCP server setup

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

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/MScCnQazTf3EDTGl5/builds/CLhmXzesvqGX2BkUp/openapi.json
