# PDF Text Extractor — Text, Markdown, Tables and OCR (`lergassy/pdf-text-extractor`) Actor

\[$0.003/PDF] Extract text from PDF files by URL, upload or base64: full text, per-page text, Markdown, tables and metadata. Scanned pages are read with OCR in 19 languages. Password PDFs, page ranges, RAG chunks. No fee per run.

- **URL**: https://apify.com/lergassy/pdf-text-extractor.md
- **Developed by:** [Matvey](https://apify.com/lergassy) (community)
- **Categories:** AI, Developer tools, Agents
- **Stats:** 4 total users, 3 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

Extract text from **PDF** files: full text, page-by-page text, clean Markdown, tables and document metadata. Scanned pages without a text layer are read with **OCR in 19 languages**. Password-protected PDFs, page ranges and RAG chunks are supported. **$0.003 per PDF + $0.0001 per page**, no fee per run, no login, no API key.

It is the [Document Text Extractor](https://apify.com/lergassy/document-text-extractor) engine with the form set up for PDFs; anything that works there works here, including Word, Excel, PowerPoint, EPUB, HTML, CSV and TXT links.

### What it is for

| What you give it | What you get back |
| --- | --- |
| A PDF link, an uploaded PDF or base64 data | Full text, per-page text, Markdown, metadata |
| A report or paper with tables | Each table as Markdown and as a row array |
| A scanned PDF | OCR text for the pages that have no text layer |
| A password-protected PDF | The same output, with the password you pass |
| Hundreds of PDF links | One row per PDF, same fields for all of them |

An empty call (`{}`) reads a two-page sample PDF, so you can see the output shape before sending your own files.

### What data does it return?

| Field | Type | Example |
| --- | --- | --- |
| `source` / `fileName` | string | `https://arxiv.org/pdf/1706.03762` |
| `pageCount` / `pagesProcessed` | integer | `15` / `15` |
| `title`, `author`, `subject`, `creator`, `producer` | string | `Annual Operations Report 2026` |
| `documentCreatedAt` / `documentModifiedAt` | string | `2026-09-05T00:07:28+08:00` |
| `text` | string | the whole document as plain text |
| `markdown` | string | headings, lists and tables kept |
| `pages` | array | `[{"number": 1, "text": "…", "markdown": "…", "ocr": false}]` |
| `tables` / `tableCount` | array / integer | `[{"page": 3, "markdown": "| … |", "rows": [["…"]]}]` |
| `chunks` / `chunkCount` | array / integer | when **Split into RAG chunks** is on |
| `wordCount`, `charCount`, `ocrPageCount` | integer | `6002`, `39811`, `0` |
| `status` / `errorCode` / `errorMessage` | string | `ok`, or why a file failed |
| `warnings` | array | `["2 scanned page(s) were not processed because the OCR page limit was reached."]` |

### How much does it cost?

| Event | Price | When it is charged |
| --- | --- | --- |
| PDF processed | $0.003 | Once per PDF that returned text |
| Page parsed | $0.0001 | Per page of text |
| Scanned page read with OCR | $0.008 | Only for pages with no text layer, and only when OCR found text |

| Job | Cost |
| --- | --- |
| 1 PDF of 10 pages | $0.004 |
| 1,000 PDFs of 10 pages | $4.00 |
| 50 scanned PDFs of 5 pages, OCR | $2.18 |

There is no fee per run. A dead link, a corrupt file or a PDF with no readable text comes back as an `error` row and is **never charged**. Pages that already contain text are never OCR-ed. Switch **OCR pages without a text layer** off and scanned pages are skipped at no cost.

### How to extract text from a PDF

1. Click **Try for free** and sign in to Apify.
2. Paste PDF links into **PDF URLs**, or use **Upload a file**.
3. Leave **Markdown output** on if the text goes to an LLM; switch on **Split into RAG chunks** for a vector database.
4. For scans in another language, pick the **OCR language**.
5. Press **Start**, then read the **Output** tab or export JSON, CSV or Excel.

### ⬇️ Input

```json
{
  "urls": ["https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/document-text-extractor/sample-report.pdf"],
  "includePages": true,
  "extractTables": true,
  "ocrScannedPages": true,
  "ocrLanguage": "eng"
}
```

| Field | What it does |
| --- | --- |
| `urls` | Direct links to the PDF files (not to a viewer page) |
| `documentFile` | Upload one file from your computer |
| `base64Files` | Base64 strings or `{"fileName", "data"}` objects, for API and agent calls |
| `includeMarkdown` / `includeFullText` / `includePages` | Which text fields to return (all on by default) |
| `extractTables` | Detect tables and return them as Markdown and rows |
| `chunkForRag`, `chunkSize`, `chunkOverlap` | Split into retrieval chunks that keep their heading path |
| `pageRange` | Read only some pages, e.g. `1-5`, `8` or `12-` |
| `password` | Password for a protected PDF |
| `ocrScannedPages`, `ocrLanguage`, `ocrMaxPagesPerDocument` | OCR for pages without a text layer (on by default, capped at 20 pages per PDF) |
| `maxConcurrency`, `maxFileSizeMb`, `timeoutPerDocumentSecs` | Advanced limits |

If a host refuses the download (rate limit, block), the Actor retries with a pause and then through a residential proxy before giving up on the file. You are not charged for that.

### ⬆️ Output

```json
{
  "source": "https://…/sample-report.pdf",
  "format": "pdf",
  "pageCount": 2,
  "title": "Annual Operations Report 2026",
  "author": "Malina Data",
  "wordCount": 198,
  "ocrPageCount": 0,
  "markdown": "# Annual Operations Report 2026\n\n## Executive summary\n…",
  "pages": [{ "number": 1, "text": "Annual Operations Report 2026…", "ocr": false }],
  "tableCount": 1,
  "status": "ok"
}
```

#### Error rows

| `errorCode` | Meaning |
| --- | --- |
| `http-error` / `network-error` / `timeout` | The file could not be downloaded, even after retries |
| `too-large` | Above the size limit (50 MB by default, up to 200 MB) |
| `password-required` | The PDF needs a password |
| `corrupt-file` | The file is damaged or not a PDF |
| `no-text-content` | No text found — a scan with OCR switched off, or an empty document |
| `unsupported-format` | Not a supported file type |

### Limitations, stated plainly

- OCR is built for printed text; handwriting and heavy stamps read poorly.
- Tables are detected from the PDF's layout. Tables drawn without lines, or split across pages, may come back merged or partial.
- At most 20 scanned pages per PDF are OCR-ed by default (adjustable up to 500), so a long scan cannot surprise you.
- Google Docs or Dropbox *viewer* links are web pages, not files — use the direct download link.

### Use cases

- **RAG and knowledge bases** — Markdown and chunks with their heading path go straight into embeddings.
- **Contract, invoice and report review** — full text plus metadata and tables for search or an LLM.
- **Research papers** — per-page text and tables from arXiv and journal PDFs.
- **Scanned archives** — OCR only where needed, billed per scanned page.

### 🤖 For AI agents and LLM apps

```json
{ "urls": ["https://example.com/report.pdf"] }
```

- One row per PDF; `status` is `ok` or `error`.
- `markdown` is the field to give a model; `chunks` is the field to embed.
- Files can be passed as base64; an empty call returns a sample result instead of an error.

### Use via API

```bash
curl -X POST "https://api.apify.com/v2/acts/lergassy~pdf-text-extractor/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://example.com/report.pdf"]}'
```

```python
from apify_client import ApifyClient
items = ApifyClient("YOUR_TOKEN").actor("lergassy/pdf-text-extractor").call(
    run_input={"urls": ["https://example.com/report.pdf"]}
).iterate_items()
```

Works the same from JavaScript, n8n, Make, Zapier and the Apify MCP server.

### ❓ FAQ

#### Does one row mean one PDF or one page?

One PDF. Pages are inside the row in `pages`.

#### Can it read password-protected PDFs?

Yes, pass the password in **PDF password**.

#### What is the difference from Document Text Extractor?

None in the engine. [Document Text Extractor](https://apify.com/lergassy/document-text-extractor) is the main Actor, framed around every document format; this one is set up for PDFs, with per-page output and OCR on by default.

#### I only have images, not PDFs.

Use [Image to Text](https://apify.com/lergassy/image-to-text) — the same OCR engine, with confidence scores and word positions.

### You might also like

| Actor | What it does |
| --- | --- |
| [Document Text Extractor](https://apify.com/lergassy/document-text-extractor) | The main Actor: PDF, Word, Excel, PowerPoint, EPUB to Markdown and RAG chunks |
| [OCR Text Extractor](https://apify.com/lergassy/ocr-text-extractor) | Scanned PDFs and images with confidence scores and word boxes |
| [Image to Text](https://apify.com/lergassy/image-to-text) | Screenshots and photos to text |

### Also known as

PDF to text, PDF text extraction API, PDF parser, PDF to Markdown, extract text from PDF, PDF OCR, PDF scraper.

# Actor input Schema

## `urls` (type: `array`):

Direct links to the PDF files you want to read. The link must point at the file itself, not at a viewer page. Word, Excel, PowerPoint, EPUB, HTML, CSV and TXT links are accepted too.

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

Upload one PDF from your computer instead of giving a URL.

## `base64Files` (type: `array`):

Base64-encoded documents for API and agent calls. Each entry is a plain base64 string (a data-URI prefix is allowed) or an object <code>{"fileName": "report.pdf", "data": "…"}</code>.

## `includeMarkdown` (type: `boolean`):

Return clean Markdown with headings, lists and tables. This is the format to feed an LLM or a vector database.

## `includeFullText` (type: `boolean`):

Return the whole document as plain text.

## `extractTables` (type: `boolean`):

Detect tables and return each one as both a Markdown table and a row array.

## `chunkForRag` (type: `boolean`):

Split the document into retrieval chunks. Each chunk keeps the heading path it belongs to, so a search hit can be traced back to its section.

## `chunkSize` (type: `integer`):

Target size of one chunk. Chunks break on headings and paragraphs, so real sizes vary a little.

## `chunkOverlap` (type: `integer`):

How much text from the previous chunk to repeat at the start of the next one.

## `includePages` (type: `boolean`):

Return every page separately with its own text and Markdown, alongside the full text.

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

Read only these pages of a PDF, for example <code>1-5</code>, <code>8</code> or <code>12-</code> (page 12 to the end). Leave empty to read everything.

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

Password for protected PDF files.

## `ocrScannedPages` (type: `boolean`):

Scanned pages have no text inside, only a picture. With this on they are read with OCR and charged per OCR page; pages that already contain text are never OCR-ed or charged for it. Switch off to skip scans entirely.

## `ocrLanguage` (type: `string`):

Language of the scanned pages, used by the OCR engine.

## `ocrMaxPagesPerDocument` (type: `integer`):

Safety cap on how many scanned pages are read per document, so a long scan cannot run up an unexpected bill.

## `maxConcurrency` (type: `integer`):

How many documents to process at the same time.

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

Files larger than this are skipped with an error row instead of being downloaded.

## `timeoutPerDocumentSecs` (type: `integer`):

How long to wait for one file to download before giving up on it.

## Actor input object example

```json
{
  "urls": [
    "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/document-text-extractor/sample-report.pdf",
    "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/document-text-extractor/sample-scan.pdf"
  ],
  "includeMarkdown": true,
  "includeFullText": true,
  "extractTables": true,
  "chunkForRag": false,
  "chunkSize": 1200,
  "chunkOverlap": 120,
  "includePages": true,
  "ocrScannedPages": true,
  "ocrLanguage": "eng",
  "ocrMaxPagesPerDocument": 20,
  "maxConcurrency": 5,
  "maxFileSizeMb": 50,
  "timeoutPerDocumentSecs": 120
}
```

# Actor output Schema

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

Dataset with one row per document, including Markdown, text, tables, chunks and file metadata.

## `documentsInConsole` (type: `string`):

Browse the extracted documents in the Apify Console.

# 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 = {
    "urls": [
        "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/document-text-extractor/sample-report.pdf",
        "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/document-text-extractor/sample-scan.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lergassy/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 = { "urls": [
        "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/document-text-extractor/sample-report.pdf",
        "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/document-text-extractor/sample-scan.pdf",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("lergassy/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 '{
  "urls": [
    "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/document-text-extractor/sample-report.pdf",
    "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/document-text-extractor/sample-scan.pdf"
  ]
}' |
apify call lergassy/pdf-text-extractor --silent --output-dataset

```

## MCP server setup

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