# PDF to Text and Markdown Extractor with Reading Order (`bepolabs/pdf-to-text`) Actor

Get headings, lists and body text from a stack of PDFs. Running headers, footers and numbers are stripped; chunks of a size you set carry their section path. Costs 2.8 USD per 1,000 PDF documents.

- **URL**: https://apify.com/bepolabs/pdf-to-text.md
- **Developed by:** [BEPO Labs](https://apify.com/bepolabs) (community)
- **Categories:** AI, Developer tools, Integrations
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.80 / 1,000 pdf documents

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

### 📄 What is PDF to Text and Markdown?

Bulk-extract text from each PDF document, by URL, and convert it into structured Markdown as JSON, with two- and three-column layouts read in sequence. Scanned image-only pages are flagged with `needsOcr`, so you know which ones need OCR.

- **RAG and AI agents**: feed papers, manuals and reports to your vector store as clean, headed chunks
- **Search**: index the text of every PDF on your site or in your archive
- **Research**: pull the text of academic papers per page for reading, quoting or analysis
- **Data extraction**: get the raw text of invoices, filings or contracts before parsing them

![PDF to Text and Markdown: PDFs to clean text and Markdown, per page, per document or as RAG chunks, with columns in reading order and headers and footers removed.](https://api.apify.com/v2/key-value-stores/wdeAWGooHdIhvFgSY/records/pdf-to-text-hero.png)

#### ✨ Why use this Actor?

- ✅ **New: word count and links**: every row gives its `wordCount` and the web `urls` cited in it, for RAG sizing and link checks
- ✅ **Clean for LLMs**: headers, footers and page numbers removed, so they do not repeat in every chunk
- ✅ **Reading order**: side-by-side text blocks are read in sequence; `columns` reports how many were found
- ✅ **Three output shapes**: `split`: page, document or chunk (with `chunkSize`)
- ✅ **Links or uploads**: public PDF URLs or files in a key-value store
- ✅ **Scans flagged**: `needsOcr` marks pages without a text layer instead of returning empty text silently
- ✅ **Fair price**: 0.0028 USD per PDF with 20 pages included

### 📦 What data can you get with PDF to Text and Markdown?

| | | |
|---|---|---|
| 📝 Plain text | Ⓜ️ Markdown | 🔢 Page number and page count |
| 🔗 Source URL or key | 🏷️ PDF title | 🔠 Characters |
| 🗞️ Columns detected | 🔎 Needs OCR flag |  |

### 🚀 How to use PDF to Text and Markdown

PDF to Text and Markdown works in a few clicks, no coding needed:

1. [Create a free Apify account](https://console.apify.com/sign-up) (the free plan includes 5 USD of monthly credit).
2. Open [PDF to Text and Markdown](https://apify.com/bepolabs/pdf-to-text) and click **Try for free**.
3. Paste one or more PDF links (or keys of uploaded files) and choose page, document or chunk output.
4. Click **Start** and wait for the run to finish (most runs take seconds).
5. Download your data as JSON, CSV, Excel, XML or HTML, or send it straight to Google Sheets, Make, Zapier or your AI agent.

![How PDF to Text and Markdown works](https://api.apify.com/v2/key-value-stores/wdeAWGooHdIhvFgSY/records/pdf-to-text-workflow.png)

### ⬇️ Input

Give `pdfUrls` or `storeKeys`; the defaults return one row per page with headers and footers removed.

##### Split

Choose `page` (default), `document` or `chunk`; chunks hold about `chunkSize` characters with the heading path above.

##### Limits

`maxPages` and `maxSizeMb` default to 300 and 50 and protect you from huge files; a cut PDF is named in the run summary under `truncated`.

| Field | Type | What it does | Default |
|---|---|---|---|
| **PDF URLs** (`pdfUrls`) | array | Direct links to PDF files (http or https). | `["https://arxiv.org/pdf/1706.03762"]` |
| **Uploaded files** (`storeKeys`) | array | Keys of PDF files stored in this run's default key-value store (upload them with the API before the run starts). |  |
| **Rows** (`split`) | string | How the dataset is cut. | `"page"` |
| **Chunk size** (`chunkSize`) | integer | Characters per chunk when Rows is Chunks; chunks end at paragraph or sentence boundaries. | `2000` |
| **Max pages per PDF** (`maxPages`) | integer | Pages read per PDF, from the first; the rest are skipped and the PDF is marked truncated. | `300` |
| **Max file size (MB)** (`maxSizeMb`) | integer | Larger files are refused before they are downloaded. | `50` |
| **Remove running headers and footers** (`removeHeadersFooters`) | boolean | Drop lines repeated at the top or bottom of at least half the pages (page numbers, report titles). | `true` |
| **Include plain text** (`includeText`) | boolean | Off: Markdown only, smaller rows. | `true` |
| **Retries** (`maxRetries`) | integer | Retries per download on network errors, 429 and 5xx, with exponential backoff. | `3` |

![PDF to Text and Markdown input form](https://api.apify.com/v2/key-value-stores/wdeAWGooHdIhvFgSY/records/pdf-to-text-input.png)

#### Input example

```json
{
    "pdfUrls": [
        "https://arxiv.org/pdf/1706.03762"
    ],
    "split": "page",
    "maxPages": 3
}
```

### ⬆️ Output

Each page, PDF or chunk becomes a row with text and Markdown. The run summary (`OUTPUT`) counts PDFs, failures and scans that need OCR.

![PDF to Text and Markdown output table](https://api.apify.com/v2/key-value-stores/wdeAWGooHdIhvFgSY/records/pdf-to-text-output.png)

#### Output example

One row of the dataset (page 2 of arXiv 1706.03762, a real run of 2026-10-16, text shortened):

```json
{
    "source": "https://arxiv.org/pdf/1706.03762",
    "title": null,
    "pageCount": 15,
    "page": 2,
    "text": "1 Introduction\n\nRecurrent neural networks, long short-term memory [13] and gated…",
    "markdown": "1 Introduction\n\nRecurrent neural networks, long short-term memory [13] and gated…",
    "chars": 3631,
    "columns": 1,
    "needsOcr": false
}
```

View the results as a table or JSON in the Output tab and download them as JSON, CSV, Excel, XML, HTML or RSS, all fields or only those you pick.

### 💰 How much does PDF to Text and Markdown cost?

PDF to Text and Markdown is priced **per event**: **2.8 USD per 1,000 PDFs** (0.0028 USD each), with no monthly rental. Each one counts once with 20 pages included, plus 0.0002 USD for every extra sheet; failures cost nothing.

| You get | You pay |
|---|---|
| 100 PDFs | 0.28 USD |
| 1,000 PDFs | 2.8 USD |
| 10,000 PDFs | 28 USD |
| 100,000 PDFs | 280 USD |

💡 Apify's free plan gives you 5 USD of credit every month, enough for about **1,785 PDFs** at no cost. Set a spending limit per run and the Actor stops there.

### ⚖️ PDF to Text and Markdown vs. the alternatives

| | PDF to Text and Markdown | Basic PDF text extractors | Copy and paste |
|---|---|---|---|
| **Headers and footers removed** | ✅ | rarely | ❌ |
| **Columns in reading order** | ✅ | varies | ❌ |
| **Markdown and RAG chunks** | ✅ | rarely | ❌ |
| **Scanned pages flagged** | ✅ | ❌ | – |

### 🔌 Integrations, API and MCP

#### Connect PDF to Text and Markdown to your AI agent (MCP)

Give Claude, ChatGPT, Cursor or any MCP client direct access to PDF to Text and Markdown through the [Apify MCP server](https://mcp.apify.com). Add this URL to its settings:

```text
https://mcp.apify.com/?actors=bepolabs/pdf-to-text
```

For Claude Desktop, add it under `mcpServers` in `claude_desktop_config.json`:

```json
{
    "mcpServers": {
        "pdf-to-text": {
            "command": "npx",
            "args": [
                "-y",
                "@apify/actors-mcp-server",
                "--actors",
                "bepolabs/pdf-to-text"
            ],
            "env": {
                "APIFY_TOKEN": "<your Apify API token>"
            }
        }
    }
}
```

Then ask: *"Read this PDF report and give me its text as Markdown chunks of 2,000 characters for my knowledge base."*

#### Run it from your code

Python (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("bepolabs/pdf-to-text").call(run_input={"pdfUrls":["https://arxiv.org/pdf/1706.03762"],"split":"page","maxPages":3})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

JavaScript (`npm install apify-client`):

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('bepolabs/pdf-to-text').call({"pdfUrls":["https://arxiv.org/pdf/1706.03762"],"split":"page","maxPages":3});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### No-code integrations

Send results to **Google Sheets, Slack, Airtable, HubSpot, Make, Zapier, n8n** and more from the Integrations tab, start runs on a **schedule**, or trigger your own endpoint with a **webhook** when a run finishes. The [Apify API](https://docs.apify.com/api/v2) gives you full control over runs and datasets.

### ❓ FAQ

##### Does it do OCR?

No. It reads the PDF's text layer; pages without one are marked `needsOcr: true` so you can send them to an OCR tool.

##### How large can a PDF be?

Up to `maxSizeMb` (50 MB) and `maxPages` (300) unless you raise them.

##### Can I upload my own files?

Yes: put them in a key-value store and list their keys in `storeKeys`.

##### Is it legal to use PDF to Text and Markdown?

The Actor reads only the PDFs you give it. Make sure you may process them; copyright and confidentiality of the documents stay your responsibility.

##### What if something is missing or does not work?

Open an issue on the **Issues** tab; we read each one and reply. Feature requests are welcome too.

### 💬 Your feedback

Tell us what PDF to Text and Markdown should add on the **Issues** tab or leave a review; your notes decide what we work on next.

### 🔗 Related Actors

| Actor | What it does |
|---|---|
| 🔤 [Image OCR](https://apify.com/bepolabs/image-ocr) | Extract text from images in 29 languages: plain text, clean Markdown and every word with its pixel box and confidence. |
| 🎓 [OpenAlex Research Papers](https://apify.com/bepolabs/openalex-works) | Research papers in bulk from OpenAlex: search, DOI or citing works to clean rows with citations, abstracts and free PDF links. |
| 📝 [Website to Markdown](https://apify.com/bepolabs/web-to-markdown) | Any web page or docs site to clean Markdown for LLMs, RAG and AI agents, with headings, links and ready-made chunks. |
| 🧪 [Clinical Trials Data](https://apify.com/bepolabs/clinical-trials) | ClinicalTrials.gov studies by condition, drug, sponsor or NCT id, with sites, eligibility and posted results. |
| 🎙️ [Audio and Video Transcriber](https://apify.com/bepolabs/media-transcriber) | Whisper transcripts of audio and video files: full text, timed segments, SRT and WebVTT. No API key. |
| 💊 [openFDA Data](https://apify.com/bepolabs/openfda) | FDA adverse events, recalls, drug labels and 510(k) clearances for any drug or device, as clean rows. |
| 📰 [RSS Feed Reader](https://apify.com/bepolabs/rss-reader) | Any RSS, Atom or JSON feed, or just a website URL, to one clean list of articles, with full text on request. |
| 🏛️ [SEC EDGAR Filings](https://apify.com/bepolabs/sec-edgar) | Tickers or CIK numbers to SEC filings: 10-K, 10-Q, 8-K, Form 4 and any other form, with document links. |

# Changelog

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

# Actor input Schema

## `pdfUrls` (type: `array`):

Direct links to PDF files (http or https). A key-value store record URL of an uploaded file works too. Up to 500 per run.

## `storeKeys` (type: `array`):

Keys of PDF files stored in this run's default key-value store (upload them with the API before the run starts).

## `split` (type: `string`):

How the dataset is cut. The price is the same.

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

Characters per chunk when Rows is Chunks; chunks end at paragraph or sentence boundaries.

## `maxPages` (type: `integer`):

Pages read per PDF, from the first; the rest are skipped and the PDF is marked truncated.

## `maxSizeMb` (type: `integer`):

Larger files are refused before they are downloaded.

## `removeHeadersFooters` (type: `boolean`):

Drop lines repeated at the top or bottom of at least half the pages (page numbers, report titles).

## `includeText` (type: `boolean`):

Off: Markdown only, smaller rows.

## `maxRetries` (type: `integer`):

Retries per download on network errors, 429 and 5xx, with exponential backoff.

## Actor input object example

```json
{
  "pdfUrls": [
    "https://arxiv.org/pdf/1706.03762"
  ],
  "split": "page",
  "chunkSize": 2000,
  "maxPages": 300,
  "maxSizeMb": 50,
  "removeHeadersFooters": true,
  "includeText": true,
  "maxRetries": 3
}
```

# Actor output Schema

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

Dataset items, one row per result, in the overview table view.

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

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

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

```

## MCP server setup

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

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/L4j3wPcysjvYGjAxV/builds/r2YZ7DkOdPJJ561iH/openapi.json
