# PDF to Text & Markdown Extractor with OCR (Tables, Bulk) (`latamdatatools/pdf-text-ocr-extractor`) Actor

Extract text, LLM-ready Markdown, tables, metadata and per-page content from PDF URLs in bulk. Scanned pages are OCR'd automatically with Tesseract inside the Actor, no API key and no cloud OCR. Page ranges, password PDFs, dataset and CSV input.

- **URL**: https://apify.com/latamdatatools/pdf-text-ocr-extractor.md
- **Developed by:** [Julian Genuario](https://apify.com/latamdatatools) (community)
- **Categories:** AI, Developer tools, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 pdf extracteds

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 to Text & Markdown Extractor with OCR (Tables, Bulk)

Give it **PDF URLs** (one, thousands, or a dataset/CSV column) and get back **clean text, LLM-ready Markdown, tables, metadata and the text of every page**. **Scanned PDFs are OCR'd automatically** with Tesseract running inside the Actor: no API key, no cloud OCR service, your documents are not sent anywhere else.

### What does PDF to Text Extractor do?

PDF to Text Extractor converts PDF URLs in bulk into plain text, LLM-ready Markdown, tables, metadata and per-page content. Scanned pages are OCR'd automatically with Tesseract inside the Actor (no API key, no cloud OCR), which makes it useful for RAG pipelines and document data extraction.

### How to use PDF to Text Extractor

1. Click **Try for free** on this page (a free Apify account is enough to start).
2. Fill in the input (see the examples below) or keep the prefilled example.
3. Click **Start** and wait for the run to finish.
4. Download the results from the **Output** tab as JSON, CSV, Excel or HTML, or read them with the Apify API.
5. Optional: create a **Schedule** to run it daily or weekly, and connect a webhook or an integration (Make, Zapier, n8n, Slack).

### Who uses it

- **AI / RAG teams**: turn reports, manuals and papers into Markdown chunks with page markers for citations.
- **Legal, procurement and compliance**: pull the text of contracts, tenders and filings to search or classify them.
- **Data teams**: extract tables from statistical reports and price lists.
- **Automation builders**: chain it after a scraper that finds PDF links (dataset input with `urlField`).

### Features

- **Text per page** from the PDF text layer (fast, exact), joined into one `text` field.
- **Markdown for LLMs**: headings detected by font size, paragraphs re-flowed, bullet lists, tables as Markdown tables, `<!-- page N -->` markers.
- **Tables** (ruled tables) as rows of cells.
- **OCR**: `auto` OCRs only pages without a text layer (scans, photos), `always` OCRs every page, `never` turns it off. 12 languages (English, Spanish, Portuguese, French, German, Italian, Dutch, Polish, Russian, Turkish, Chinese simplified, Japanese).
- **Metadata**: title, author, subject, keywords, creator, producer, creation and modification dates.
- Page ranges (`1-5, 8, 10-`), max pages per PDF, password-protected PDFs (your password), duplicates skipped.
- Respects robots.txt on every redirect hop. Retries on timeouts and 429/5xx.
- Very large documents: the full text and Markdown go to downloadable files when they do not fit in one dataset item.

### Input example

```json
{
  "urls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf", "https://example.com/scanned-invoice.pdf"],
  "ocrMode": "auto",
  "ocrLanguages": ["eng", "spa"],
  "markdown": true,
  "extractTables": true,
  "pageRange": ""
}
```

### Output

```json
{
  "status": "ok",
  "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "fileSizeBytes": 140815,
  "pageCount": 6,
  "pagesExtracted": 6,
  "truncated": false,
  "ocrPages": 0,
  "scanned": false,
  "metaTitle": "Form W-9 (Rev. March 2024)",
  "metaAuthor": "SE:W:CAR:MP",
  "metaCreatedAt": "2024-03-04T10:19:41",
  "wordCount": 6210,
  "charCount": 37687,
  "text": "Form W-9 (Rev. March 2024) Request for Taxpayer Identification Number...",
  "markdown": "<!-- page 1 -->\n# W-9 Request for Taxpayer\n...",
  "tables": [{ "page": 1, "rows": [["Social security number", null, "..."]] }],
  "pages": [{ "page": 1, "text": "...", "chars": 6512, "ocr": false, "tables": 2 }],
  "billedPdfEvents": 1
}
```

Failed items (not a PDF, HTTP errors, password needed, blocked by robots.txt, no text at all) come with `status` `failed` or `no_text` and an `error`, and are **not charged**.

### Pricing

Pay per event:

- **USD 0.003 per PDF** (`pdf`). Each started block of 50 pages counts as one PDF (a 120-page report = 3 events = USD 0.009).
- **USD 0.008 per OCR page** (`ocr-page`), only for pages that really were OCR'd.
- Start: USD 0.00005.

A typical 10-page digital PDF costs USD 0.003; a 10-page scan costs USD 0.083. Failed and empty PDFs are free, and the Actor never processes pages your max charge per run cannot pay for.

### Notes and limits

- Only public URLs (or PDFs your password opens). No logins.
- OCR quality depends on the scan; 300 DPI is the default. Handwriting is not supported.
- Table detection works best on tables with ruling lines; tables drawn only with spacing come out as text.
- Headings are guessed from font size; forms and multi-column layouts may need a light clean-up.
- OCR speed is about 2-6 s per page at the default 2 GB memory (more memory = more CPU = faster).

### Why use PDF to Text Extractor

- **OCR included, no extra account:** scanned pages are read with Tesseract inside the Actor. No OpenAI, Google or AWS key, and your documents are not sent to a third-party OCR service.
- **You only pay OCR where it is needed:** in `auto` mode only pages without a text layer are OCR'd; digital pages are read directly and cost nothing extra.
- **LLM-ready Markdown:** headings, lists, tables and `<!-- page N -->` markers, ready for RAG chunking with page citations.
- **Bulk input:** a list of URLs, a dataset from another Actor (for example a scraper that collected PDF links) or a CSV / Google Sheets column.
- **Failed files are free:** non-PDFs, HTTP errors, password-protected files without the password and empty PDFs are listed but never charged.

### Cost examples

Prices at the current pay-per-event rates (see Pricing). You can set a maximum charge per run in the run options.

| Job | Approximate cost |
|---|---|
| 100 digital PDFs of up to 50 pages | about $0.30 |
| 1,000 digital PDFs of up to 50 pages | about $3.00 |
| 100 scanned pages (OCR) | about $0.80 + $0.003 per PDF |

### Use it from your code, an integration or an AI agent

**Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("latamdatatools/pdf-text-ocr-extractor").call(run_input={
  "urls": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ]
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

**JavaScript / Node.js** (`npm install apify-client`):

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('latamdatatools/pdf-text-ocr-extractor').call({
  "urls": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ]
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

**No code:**

- **Schedules:** run it daily or weekly from Apify Console (Schedules), with the same input saved as a Task.
- **Integrations:** send results to Google Sheets, Slack, email, Make, Zapier, n8n or Airbyte from the Integrations tab, or call your own webhook when a run finishes.
- **AI agents:** add it as a tool in Claude, Cursor, VS Code and other MCP clients through the Apify MCP server (`https://mcp.apify.com`), so the agent can run PDF to Text Extractor and read the results.

### FAQ

**Can it read PDFs I have on my computer?** Upload them to any public link (a storage bucket, an Apify key-value store or a file host) and paste the URLs. Private, logged-in links are not supported.

**Which OCR languages can I combine?** Any of the 12 bundled languages, for example `["eng", "spa"]` for bilingual documents.

**Can I call it from my own code or from an AI agent?** Yes. Start runs and fetch results with the Apify API or the JavaScript / Python clients, or use it as a tool from Claude, Cursor and other MCP clients through the Apify MCP server (`mcp.apify.com`).

**Can I run it automatically?** Yes. Create a Schedule in Apify Console (for example every morning) and add a webhook or an integration to get the results where you need them.

**Which export formats are available?** JSON, JSONL, CSV, Excel (XLSX), XML, RSS and HTML table, from the Output tab or the API.

**What does it cost?** See the Pricing section above. You only pay for the events listed there.

### Changelog

- 0.1 (2026-10): first version.

# Actor input Schema

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

One public PDF URL per line (reports, invoices, papers, contracts, scanned documents...).

## `ocrMode` (type: `string`):

Auto = OCR only pages without a text layer (scans, photos). Always = OCR every page (e.g. broken text layers). Never = text layer only. OCR runs with Tesseract inside the Actor; no document is sent to any other service.

## `ocrLanguages` (type: `array`):

Languages of the scanned text. More languages = slower OCR.

## `ocrDpi` (type: `integer`):

300 is best for most scans; 200 is faster for clean documents.

## `markdown` (type: `boolean`):

Markdown with headings (by font size), paragraphs, bullet lists and tables, with page markers. Ideal for RAG and LLM prompts.

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

Detect tables with ruling lines and return them as rows (and as Markdown tables).

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

Add a list with the text of every page (with OCR flag and table count).

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

Optional, e.g. 1-5, 8, 10- (empty = all pages, up to the max below).

## `maxPagesPerPdf` (type: `integer`):

Longer PDFs are cut at this page (marked truncated).

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

For password-protected PDFs you are allowed to open. Used for every PDF of the run.

## `datasetId` (type: `string`):

Optional: extract the PDFs whose URLs are in an Apify dataset. Use "URL field" to say which field holds them.

## `csvUrl` (type: `string`):

Optional: a public CSV with a header row (Google Sheets links work if shared as "Anyone with the link").

## `urlField` (type: `string`):

Field / CSV column with the PDF URL (dot notation for nested fields).

## `keepFields` (type: `array`):

Copy these dataset / CSV fields into each result (under "source").

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

Bigger downloads are stopped (not charged).

## Actor input object example

```json
{
  "urls": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ],
  "ocrMode": "auto",
  "ocrLanguages": [
    "eng"
  ],
  "ocrDpi": 300,
  "markdown": true,
  "extractTables": true,
  "includePages": true,
  "maxPagesPerPdf": 500,
  "urlField": "url",
  "maxFileSizeMb": 100
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# 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://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
        "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("latamdatatools/pdf-text-ocr-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://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
        "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("latamdatatools/pdf-text-ocr-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://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ]
}' |
apify call latamdatatools/pdf-text-ocr-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,latamdatatools/pdf-text-ocr-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/6viE5NEWgwxKvBInG/builds/1CQ9btaTOySjDnphu/openapi.json
