# PDF Text Extractor — Markdown, Tables, OCR & RAG Chunks (`nanare-sudo/pdf-text-extractor-rag`) Actor

Extract text, GFM Markdown, tables (2D arrays + CSV) and heading-aware RAG chunks with page anchors from PDFs. URLs, base64 upload, key-value store, password-protected PDFs, page ranges, OCR fallback (eng/deu/fra/spa) only for pages without a text layer. Failed PDFs are never charged.

- **URL**: https://apify.com/nanare-sudo/pdf-text-extractor-rag.md
- **Developed by:** [Leonardo Berisha](https://apify.com/nanare-sudo) (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 $4.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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## PDF Text Extractor — Markdown, Tables, OCR & RAG Chunks

**PDF text extractor** that turns any PDF into clean plain text, GitHub-flavored **Markdown**, structured **tables** (2D arrays + CSV) and — unique on Apify — **heading-aware RAG chunks** with page anchors, ready for your vector database. Feed it URLs, base64 uploads or key-value store files; encrypted PDFs and page ranges are supported, and scanned pages are recovered via **OCR fallback** (English, German, French, Spanish) — you only pay OCR for pages that actually have no text layer. Failed PDFs are never charged.

### What is PDF Text Extractor — Markdown, Tables, OCR & RAG Chunks?

It is one actor that replaces four: a PDF-to-text converter, a PDF-to-Markdown API, a PDF table extractor and a RAG chunking pipeline.

- **Plain text** — word-spacing-safe extraction (handles tight LaTeX/arXiv kerning that glues words together in naive extractors)
- **Markdown (GFM)** — headings inferred from font sizes, paragraphs merged across line breaks, tables embedded as pipe tables
- **Tables** — detected per page, returned as 2D arrays **and** ready-made CSV strings with page numbers
- **RAG chunks** — the differentiator: ~`chunkTokens`-sized chunks that break at headings, each carrying `headingPath` (e.g. `["Attention Is All You Need", "3 Model Architecture"]`), `pageStart`/`pageEnd` for citations, and a token estimate. No other PDF actor ships this
- **OCR fallback** — Tesseract runs **only** on pages without a text layer (eng, deu, fra, spa), capped by `maxOcrPages`
- **Every input channel** — public URLs, inline base64 (also `data:` URIs), Apify key-value store keys; password-protected PDFs; page ranges like `1-5,8,12-`
- **`info` mode (free)** — page count, metadata, encryption status, how many pages would need OCR and the exact cost estimate **before** you spend anything
- **`canary` mode (free)** — six-point self-test (parser, metadata, tables, download, OCR engine, chunker) so you can verify health before relying on it

### Input

```json
{
    "mode": "extract",
    "urls": ["https://arxiv.org/pdf/1706.03762"],
    "pageRange": "1-5,8,12-",
    "ocrLanguages": ["eng", "deu"],
    "outputFormats": ["text", "markdown", "tables", "chunks"],
    "chunkTokens": 800
}
```

Or upload inline: `"base64Files": [{"name": "report.pdf", "data": "<base64>"}]`, or reference a key-value store record: `"keyValueStoreKeys": ["my-upload"]`. Encrypted files: add `"password": "..."`.

### Output

One dataset item per PDF:

```json
{
    "source": "url",
    "url": "https://arxiv.org/pdf/1706.03762",
    "fileName": "1706.03762",
    "status": "success",
    "pageCount": 15,
    "metadata": { "title": "...", "producer": "pdfTeX", "encrypted": false },
    "text": "Attention Is All You Need\n\nAshish Vaswani ...",
    "markdown": "# Attention Is All You Need\n\n### 1 Introduction\n\nRecurrent neural networks ...",
    "tables": [{ "page": 9, "rows": [["Model", "BLEU"], ["Transformer (big)", "28.4"]], "csv": "Model,BLEU\n..." }],
    "chunks": [{
        "index": 3,
        "text": "### 3 Model Architecture\n\nMost competitive neural sequence transduction models ...",
        "tokenEstimate": 720,
        "headingPath": ["Attention Is All You Need", "3 Model Architecture"],
        "pageStart": 2,
        "pageEnd": 4
    }],
    "ocrPagesUsed": [],
    "charCount": 35539,
    "wordCount": 6204,
    "processingMs": 9932
}
```

`status` is `success`, `partial` (some pages failed or OCR budget ran out — details in `notes`), `failed` (never charged) or `skipped` (your cost limit was reached).

### Pricing

Pay per event — you only pay for delivered results:

| Event | Price |
|---|---|
| Actor start | $0.002 |
| PDF processed (text + markdown + tables + chunks) | $0.004 |
| OCR page (only pages without a text layer) | $0.008 |

10 regular PDFs cost **$0.042**. A fully scanned 20-page document costs **$0.166**. Compare: Unstructured.io charges $0.03 **per page**; other Apify PDF actors charge up to $0.02 per PDF and $0.015 per OCR page — and none of them outputs RAG chunks. **Failed PDFs are free**, `info` and `canary` runs are free (start fee only).

### Use with AI agents (MCP), n8n, Make & Zapier

Every mode works through the [Apify MCP server](https://mcp.apify.com) — point Claude, ChatGPT, LangGraph or CrewAI at Apify and the agent can call this actor directly, e.g. *"extract the tables from this PDF and give me heading-aware chunks for retrieval"*. The input schema is agent-friendly by design: one `mode` switch, no required fields beyond the file itself, sensible defaults, and the free `info` mode lets an agent estimate cost before committing. The same applies to the Apify integrations for n8n, Make and Zapier — batch-convert PDFs to Markdown in a no-code pipeline without hosting your own parser.

### FAQ

**Is this legal?** Yes. The actor only processes PDFs you explicitly provide (your URLs, your uploads, your storage). It uses open-source extraction libraries (pdfplumber, pypdf, Tesseract), collects no personal data and scrapes nothing on its own.

**How is this different from other PDF text extractors?** Three things in one actor that competitors split or skip: (1) tables as structured data *plus* Markdown, (2) every input channel incl. base64 upload and password PDFs, (3) heading-aware RAG chunks with page anchors — the format vector databases and citation-grounded RAG actually need.

**Does OCR run on every page?** No. OCR runs only on pages whose text layer is empty (true scans). Pages with digital text are extracted natively and never billed as OCR. Set `maxOcrPages: 0` to disable OCR entirely.

**What about scanned PDFs in German, French or Spanish?** Pass `"ocrLanguages": ["deu"]` (or `fra`/`spa`, combinable with `eng`). The language packs ship inside the actor image.

**Can I extract only specific pages?** Yes — `"pageRange": "1-5,8,12-"` (1-based, open ranges allowed).

**How do I estimate cost before running a big batch?** Run `mode: "info"` first (free): it reports per document how many pages lack a text layer and the exact extraction price.

**What happens when my cost limit is reached mid-run?** The actor stops charging, marks remaining PDFs as `skipped` and finishes gracefully — you never pay above your `maxTotalChargeUsd`.

**Is my data safe?** Files are processed in an isolated container with limited permissions and are not persisted anywhere except your own dataset output.

### Keywords

pdf text extractor, pdf to markdown api, extract tables from pdf, pdf parser for RAG, rag chunking pdf, pdf ocr api, scanned pdf to text, batch pdf processing, pdf to json, LLM document extraction, pdf chunking for vector database, langchain pdf loader alternative, password protected pdf extraction, pdf table to csv, heading aware chunking, MCP pdf tool, n8n pdf extraction, make.com pdf parser

# Actor input Schema

## `mode` (type: `string`):

`extract` = full extraction (charged per processed PDF + per OCR page). `info` = free probe: page count, metadata, encryption status, which pages have a text layer and an OCR-cost estimate — run this first on unknown PDFs. `canary` = free health self-test of the whole pipeline (download, parser, tables, OCR engine).

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

Direct links to PDF files, e.g. `https://arxiv.org/pdf/1706.03762`. Redirects are followed. Combine freely with `base64Files` and `keyValueStoreKeys`.

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

Inline PDF uploads as objects `{"name": "report.pdf", "data": "<base64>"}`. `data` may be a raw base64 string or a `data:application/pdf;base64,...` URI. Ideal for API and AI-agent callers that hold the file in memory.

## `keyValueStoreKeys` (type: `array`):

Keys of PDF records in this run's default key-value store (upload the file first via the Apify API or console, then pass its key here).

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

Password for encrypted PDFs. Applied to every input file that is encrypted; files that fail to decrypt are reported as `failed` and are not charged.

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

Pages to extract, 1-based, e.g. `1-5,8,12-` (12- means page 12 to the end). Empty = all pages.

## `maxPdfMb` (type: `integer`):

PDFs larger than this are rejected (status `failed`, not charged).

## `maxOcrPages` (type: `integer`):

Hard cap on OCR-processed pages across the whole run (OCR is the only per-page charge). Set 0 to disable OCR entirely.

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

Tesseract language packs to use for scanned pages. Available in this actor: `eng`, `deu`, `fra`, `spa`. Multiple languages are combined (e.g. eng+deu).

## `outputFormats` (type: `array`):

Which representations to include in each result item: `text` (plain text), `markdown` (GitHub-flavored, incl. pipe tables), `tables` (2D arrays + CSV strings + page numbers), `chunks` (heading-aware RAG chunks with headingPath and page anchors). Fewer formats = smaller items, same price.

## `chunkTokens` (type: `integer`):

Approximate target size of each RAG chunk in tokens (estimated at ~4 characters per token). Chunks break at headings when possible and always carry `headingPath`, `pageStart`, `pageEnd`.

## Actor input object example

```json
{
  "mode": "extract",
  "urls": [
    "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
  ],
  "base64Files": [],
  "keyValueStoreKeys": [],
  "maxPdfMb": 50,
  "maxOcrPages": 100,
  "ocrLanguages": [
    "eng"
  ],
  "outputFormats": [
    "text",
    "markdown",
    "tables",
    "chunks"
  ],
  "chunkTokens": 800
}
```

# Actor output Schema

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

One record per processed PDF (status success/partial/failed) with text, markdown, tables\[], chunks\[], metadata.

# 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"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nanare-sudo/pdf-text-extractor-rag").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"] }

# Run the Actor and wait for it to finish
run = client.actor("nanare-sudo/pdf-text-extractor-rag").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"
  ]
}' |
apify call nanare-sudo/pdf-text-extractor-rag --silent --output-dataset

```

## MCP server setup

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

```

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/eYl4IMzlLIon7XRQl/builds/R3mFokhcW3rc8zaCm/openapi.json
