# Document Text Extractor: PDF, DOCX, Image OCR (`frostpine456/pdf-extractor`) Actor

Turn PDF, DOCX and image files into clean text or markdown, per document or per page. English OCR for scans. $0.006 per document, $0.010 per OCR page, no start fee.

- **URL**: https://apify.com/frostpine456/pdf-extractor.md
- **Developed by:** [M J](https://apify.com/frostpine456) (community)
- **Categories:** AI, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## Document Text Extractor: PDF to Text, Markdown and OCR

**Turn PDFs, Word documents and scanned images into clean text and markdown.** Give the Actor a list of document URLs or uploaded files and it returns the text of each one as plain text and as markdown, with per-page detail and metadata. Pages with no text layer (scans) are read with OCR. It is built for RAG and LLM pipelines, document automation and search indexing. Pricing is pay per result: **$0.006 per document and $0.010 per OCR'd page**. No start fee, and failed documents are free.

Run it from the Apify Console or call it through the Apify API.

### Use cases

- **RAG and LLM ingestion.** Convert PDFs to markdown, one record per page, ready to chunk and embed.
- **Document automation.** Pull the text, title, author and page count out of incoming PDFs and Word files.
- **Search indexing.** Bulk-extract text from up to 100 documents per run to feed a search index.
- **Digitising scans.** Read scanned PDFs and photographed pages with English OCR.
- **Research papers.** Extract text from arXiv-style PDF links in bulk.
- **Metadata audits.** Collect title, author, producer, created date and page count across a set of PDFs.

### Supported formats

- **PDF**: text-layer PDFs, scanned PDFs (OCR), and mixed ones. PDFs with only permission restrictions ("no printing") are extracted normally.
- **DOCX** (Word): text and markdown, using the document's own heading styles.
- **Images**: PNG, JPEG and TIFF, read with OCR as a single page.

The format is detected from the file's contents, not its name or `Content-Type`. Anything else returns an `unsupported format` error, free of charge.

### PDF to text

Pass PDF URLs (or uploaded files) and get the plain text of each document, along with the page count, title, author and other metadata. This works for any link that serves the file directly, including arXiv PDF links. Set `format` to `text` if you only want plain text.

### PDF to markdown for RAG and LLMs

Set `format` to `markdown` (or leave it on `both`) to get markdown with headings and paragraphs. For RAG, set `outputMode` to `page`: you get one dataset item per page, which is a natural unit for chunking and embedding. For a quick prompt, `document` mode returns the whole document in one item.

Markdown heading detection is best effort. In PDFs, headings come from font size relative to the body text: the largest size becomes `#` and smaller heading sizes become `##`. Paragraphs come from line gaps. DOCX files use their own heading styles. Markdown problems never cause an error. Tables and layout are not reconstructed.

### OCR for scanned PDFs and images

Pages with no text layer are read with OCR, and image files (PNG, JPEG, TIFF) are OCR'd as a single page.

- **English only.** Other languages may come out garbled.
- `auto` (default) OCRs only pages whose text layer has fewer than 50 characters, which is usually scanned pages.
- `force` OCRs every page, up to `maxOcrPagesPerDocument`. Use it when a PDF's text layer is broken or garbled.
- `off` never OCRs. Images then fail with `image requires OCR`, free of charge.
- Once a document reaches `maxOcrPagesPerDocument` (default 20, up to 50), OCR stops. The remaining pages keep their text layer, which may be empty.
- Each OCR page has a 20-second limit. A page that times out keeps its text layer and is not billed.

Each page in the output carries an `ocr` flag, so you can see which pages were OCR'd.

### DOCX to text and markdown

Word documents are converted using the document's own heading styles, so headings in Word become headings in the markdown. Use `format` to choose text, markdown or both. Word documents return no title or author metadata, and they count as one page.

### Per-page output and metadata

- **Per page.** `outputMode: "page"` gives one item per page. In `document` mode, every page is inside the item's `pages` array.
- **Metadata.** Each item includes `title`, `author`, `producer`, `created`, `pageCount`, `fileSizeBytes`, `charCount`, `ocrPages` and `truncated`. Values the Actor cannot determine are `null`.

### Pricing

Pay per result, with **no start fee**:

- **$0.006 per document** successfully extracted (`document` event).
- **$0.010 per OCR'd page** (`ocr-page` event), on top of the document charge.

Worked examples:

| Job | Cost |
|---|---|
| 100 text-layer PDFs, no OCR | 100 x $0.006 = **$0.60** |
| One 10-page scanned PDF, all pages OCR'd | $0.006 + 10 x $0.010 = **$0.106** |
| 100 DOCX files | 100 x $0.006 = **$0.60** |
| One PNG image (OCR'd as one page) | $0.006 + $0.010 = **$0.016** |

Failed documents are free: a download error, an unsupported or password-protected file, a file over the size cap, or a document that times out produces an error record and costs nothing. OCR pages that fail or time out are not charged. In page mode you still pay per document, not per page item. The `SUMMARY` record is not billed.

**Charge limit.** If your run hits its charge limit, the Actor stops before downloading the next document and marks the rest `skipped` in `SUMMARY`.

### Real-world benchmark

From a real run on 2026-10-21, OCR off (see `docs/samples/federal-register-run-2026-10-21.json`):

- 60 Federal Register PDFs (public-domain US government documents).
- 60 of 60 extracted: 151 pages in 10 seconds.
- Text per document ranged from about 5,000 to 116,000 characters (median about 13,700).
- User cost: **$0.36** at $0.006 per document.

Excerpt from the first document (`https://www.govinfo.gov/content/pkg/FR-2026-10-21/pdf/C1-2026-20311.pdf`):

```
66591 Federal Register / Vol. 91, No. 202 / Wednesday, October 21, 2026 / Notices
1 15 U.S.C. 78s(b)(1).
2 15 U.S.C. 78a.
3 17 CFR 240.19b–4.
4 See Securities Exchange Act Release No. 106204
(Aug. 27, 2026), 91 FR 56247 (Sept. 1, 2026)
```

### Input

```json
{
  "documents": [
    "https://arxiv.org/pdf/1706.03762"
  ],
  "outputMode": "document",
  "format": "both",
  "ocr": "auto",
  "maxOcrPagesPerDocument": 20,
  "maxFileSizeMb": 25
}
```

- `documents` (required): 1 to 100 items. Each is an `http(s)` URL or a file you upload in the input form.
- `outputMode`: `document` (default) gives one item per document with every page inside it; `page` gives one item per page, which suits RAG pipelines.
- `format`: `both` (default), `text` or `markdown`.
- `ocr` (advanced): `auto` (default), `force` or `off`. See OCR above.
- `maxOcrPagesPerDocument` (advanced): 0 to 50, default 20.
- `maxFileSizeMb` (advanced): 1 to 50, default 25.

### Output

One dataset item per document (document mode):

```json
{
  "sourceUrl": "https://example.com/quarterly-report.pdf",
  "fileName": "quarterly-report.pdf",
  "mimeType": "application/pdf",
  "status": "ok",
  "error": null,
  "text": "Quarterly Report\nRevenue grew in every region.\n\nPage two has more body text.",
  "markdown": "# Quarterly Report\n\nRevenue grew in every region.\n\nPage two has more body text.",
  "pages": [
    { "page": 1, "text": "Quarterly Report\nRevenue grew in every region.", "ocr": false },
    { "page": 2, "text": "Page two has more body text.", "ocr": false }
  ],
  "metadata": {
    "title": "Quarterly Report",
    "author": null,
    "producer": "Example Writer",
    "created": "2026-09-30T12:00:00.000Z",
    "pageCount": 2,
    "fileSizeBytes": 18234,
    "charCount": 75,
    "ocrPages": 0,
    "truncated": false
  }
}
```

In page mode each item holds one page: `pages` is replaced by top-level `page` and `ocr`, and the document fields repeat. `text` is left out when `format` is `markdown`, and `markdown` when it is `text`. Values the Actor cannot determine are `null`.

A failed document gives exactly one item with `"status": "error"` and the reason in `error`, such as `HTTP 404`, `unsupported format`, `encrypted`, `could not parse` or `file too large (> 25 MB cap)`. The run carries on with the next document.

The Actor also writes a `SUMMARY` record to the run's key-value store, with one entry per input (`sourceUrl`, `status`, `pageCount`, `ocrPages` and any `error`), plus totals and `chargeLimitReached`.

If one item would be larger than about 4 MB, its text is shortened, the full JSON is saved to the run's default key-value store, and `fullOutputUrl` points to it.

### Limits

- Up to 100 documents per run, processed 2 at a time.
- Files up to 25 MB by default, 50 MB at most.
- Text extraction reads the first 500 pages of a document. `metadata.truncated` is `true` when there are more.
- Up to 50 OCR pages per document (default 20).
- Each document has a 10-minute budget. A document that runs over it becomes a free error record.
- Downloads time out after 60 seconds.
- Word documents return no title or author metadata, and they count as one page.

### FAQ

**Which file formats are supported?**
PDF (text-layer, scanned and mixed), DOCX, and PNG, JPEG and TIFF images. The format is detected from the file's contents. HTML, XLSX and PPTX files are not supported and return a free `unsupported format` error.

**What are the limits?**
100 documents per run, 25 MB per file by default (up to 50 MB with `maxFileSizeMb`), 500 pages read per document, and up to 50 OCR pages per document.

**Does OCR work in languages other than English?**
No. OCR is English only, and other languages may come out garbled.

**Can it read password-protected PDFs?**
No. A PDF that needs a password to open returns an `encrypted` error and is not charged. PDFs that open without a password but have permission restrictions, such as "no printing", are extracted normally.

**Are my files stored?**
No. Files are not stored after the run. See Privacy below.

**Should I use URLs or uploads?**
Either. A URL must be an `http(s)` link that serves the file. Otherwise, upload files in the input form's `documents` field and mix them with URLs as you like, up to 100 items in total.

**Does it extract tables or keep the page layout?**
No. Tables and layout are not reconstructed. You get text, plus markdown headings and paragraphs detected on a best-effort basis.

### Privacy

Files are not stored after the run. Documents are held only in memory and in a temporary folder that is deleted as soon as each document is done. Uploaded files are read with your run's own token, which is never sent to any other host. The only things saved are the dataset items, `SUMMARY`, and any oversized record, all in the run's own default storages.

### Out of scope

Tables and layout reconstruction, non-English OCR, password-protected PDFs, HTML, XLSX and PPTX files, AI summaries, and long-term storage.

# Actor input Schema

## `documents` (type: `array`):

1 to 100 documents. Each item is an http(s) URL or an uploaded file. Supported: PDF, DOCX, PNG, JPEG and TIFF.

## `outputMode` (type: `string`):

'document' outputs one item per document with every page inside it; 'page' outputs one item per page. You are billed per document in both modes.

## `format` (type: `string`):

Which text forms to include: plain text, markdown (with best-effort headings and paragraphs), or both.

## `ocr` (type: `string`):

'auto' OCRs only pages with under 50 characters of text layer (scans). 'force' OCRs every page, up to the per-document cap. 'off' never OCRs; images then fail with no charge. OCR is English only.

## `maxOcrPagesPerDocument` (type: `integer`):

OCR stops after this many pages in a document (0 to 50). Later pages keep their text layer, which may be empty.

## `maxFileSizeMb` (type: `integer`):

Larger files are rejected with an error record and are not charged (1 to 50).

## Actor input object example

```json
{
  "documents": [
    "https://arxiv.org/pdf/1706.03762"
  ],
  "outputMode": "document",
  "format": "both",
  "ocr": "auto",
  "maxOcrPagesPerDocument": 20,
  "maxFileSizeMb": 25
}
```

# Actor output Schema

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

Extracted text, markdown and metadata: one item per document, or per page in page mode.

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

Per-document status, page and OCR counts, totals, and whether the charge limit was reached.

# 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 = {
    "documents": [
        "https://arxiv.org/pdf/1706.03762"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("frostpine456/pdf-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 = { "documents": ["https://arxiv.org/pdf/1706.03762"] }

# Run the Actor and wait for it to finish
run = client.actor("frostpine456/pdf-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 '{
  "documents": [
    "https://arxiv.org/pdf/1706.03762"
  ]
}' |
apify call frostpine456/pdf-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,frostpine456/pdf-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/0g3NrD9In1UGR4BCL/builds/y2RCrMQm63C6X3tZP/openapi.json
