# PDF to Markdown Converter – PDF Parser for LLM & RAG (`cleandata-labs/pdf-to-markdown`) Actor

Convert PDF links or uploaded PDFs into clean Markdown: headings, lists, tables, page numbers, metadata and RAG chunks. Fast text extraction, no OCR (scanned and failed files are free). $3 per 1,000 PDFs, first 20 pages included.

- **URL**: https://apify.com/cleandata-labs/pdf-to-markdown.md
- **Developed by:** [CleanData Labs](https://apify.com/cleandata-labs) (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 $3.00 / 1,000 converted pdfs

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 Markdown Converter for LLMs & RAG – Headings, Tables, Page Markers & Metadata

### What does PDF to Markdown do?

PDF to Markdown is a **PDF to Markdown converter and PDF text extraction API** for LLMs, RAG pipelines and AI agents. Give it PDF links or upload PDF files, and it returns each document as **clean, structured Markdown**: headings (`#`, `##`, `###`), bullet and numbered lists, **tables as Markdown tables**, page markers you can cite, plus **title, author, dates and page count**.

It runs on a fast, open-source engine (no OCR, no AI model calls), so a typical 10–20 page PDF converts in **1–3 seconds**, and you **only pay for PDFs that were converted**. Broken links, blocked downloads, scanned PDFs without a text layer, password-protected files and duplicates are free.

Need web pages too? Use our [URL to Markdown Converter](https://apify.com/cleandata-labs/url-to-markdown) for HTML pages, sitemaps and small crawls. It reads PDFs as plain text; this Actor keeps the PDF's structure.

### Why use it

- **Structure, not just text:** headings detected from font size and weight, bullet and numbered lists, paragraphs re-joined across line breaks, hyphenated words repaired, ligatures (ﬁ, ﬂ) fixed
- **Tables:** tables with cell borders become Markdown tables (`| a | b |`)
- **Multi-column layouts** (scientific papers, reports) are read column by column
- **Clean output:** repeated page headers/footers and bare page numbers are removed
- **Page markers** `<!-- page N -->` and an optional **per-page output** for citations
- **Page selection:** `"1-5"`, `"1,3,10-12"`, `"20-"`, `"-1"` (last page)
- **Metadata:** title (from the PDF, or the first heading), author, subject, keywords, creator, created/modified dates, page count, file size, SHA-256
- **RAG chunks** with overlap, each tagged with the page it starts on
- **Inputs:** PDF URLs, **uploaded files** (stored in an Apify key-value store), or **every PDF in a key-value store**
- **Clear errors** with a code (`no-text-layer`, `password-protected`, `not-a-pdf`, `not-found`, `blocked`, `file-too-large`, `invalid-pdf`) instead of a failed run
- Password-protected PDFs: pass the password (stored encrypted)
- Respects robots.txt by default; honest User-Agent
- Works as a tool for AI agents via the Apify API and MCP

### How to use PDF to Markdown

1. Open the Actor in Apify Console (click **Try for free** on this page) and sign in or create a free Apify account.
2. Paste PDF links into **PDF URLs**, or use **Upload PDFs** to add files from your computer.
3. Optional: set **Pages to convert** (e.g. `1-10`), turn on **Per-page output**, or set **Chunk size** for RAG.
4. Click **Start**. When the run finishes, open the **Output** tab and download the results as **JSON, CSV, Excel** or HTML.
5. To repeat it, create a **Schedule**, or call the Actor from your app via the Apify API.

### Input examples

Convert a few PDFs:

```json
{
  "urls": ["https://arxiv.org/pdf/1706.03762", "https://bitcoin.org/bitcoin.pdf"]
}
```

First 5 pages only, one Markdown per page, plus RAG chunks:

```json
{
  "urls": ["https://arxiv.org/pdf/1706.03762"],
  "pageRange": "1-5",
  "perPageOutput": true,
  "chunkSizeTokens": 800,
  "chunkOverlapTokens": 80
}
```

Convert every PDF stored in one of your key-value stores:

```json
{
  "keyValueStoreId": "username~my-pdfs",
  "keyValueStoreKeyPrefix": "invoice-"
}
```

### Output example

```json
{
  "source": "https://arxiv.org/pdf/1706.03762",
  "fileName": "1706.03762v7.pdf",
  "title": "Attention Is All You Need",
  "titleSource": "first-heading",
  "author": null,
  "createdAt": "2024-04-10T21:11:43+00:00",
  "pageCount": 15,
  "pagesConverted": 15,
  "pagesWithoutText": [],
  "tablesFound": 1,
  "markdown": "<!-- page 1 -->\n\n# Attention Is All You Need\n\n...\n\n## 1 Introduction\n\nRecurrent neural networks ...",
  "characters": 38817,
  "words": 5965,
  "approxTokens": 9704,
  "warnings": [],
  "error": null
}
```

With `perPageOutput` you also get `pages: [{ "page": 1, "markdown": "...", "tables": 0, "hasTextLayer": true }]`; with `chunkSizeTokens` you get `chunks: [{ "index": 0, "page": 1, "text": "...", "approxTokens": 790 }]`.

A PDF that could not be converted comes back as a free row:

```json
{ "source": "https://example.org/scan.pdf", "errorCode": "no-text-layer", "error": "The PDF has no text layer on any of the 1 converted pages (scanned images). OCR is not included, so nothing was extracted.", "blocked": false }
```

### How much does it cost?

PDF to Markdown uses **pay-per-event pricing**. You pay only for PDFs that were actually converted; there is no separate compute charge.

| Event | Price | When it is charged |
|---|---|---|
| Converted PDF | **$0.003** ($3 per 1,000 PDFs) | Once per converted PDF. **Includes the first 20 pages.** |
| Extra page | **$0.00005** ($0.05 per 1,000 pages) | Each converted page above 20 in the same PDF |
| Actor start | $0.00005 | Apify's standard start event, once per run (per GB of memory) |

| Document | Price |
|---|---|
| 1–20 pages | $0.003 |
| 60 pages | $0.005 |
| 100 pages | $0.007 |
| 500 pages | $0.027 |

**Free (never charged):** failed downloads (404, timeouts), blocked or robots.txt-disallowed URLs, scanned PDFs without a text layer, password-protected files without the right password, files that are not PDFs, files over the size limit and duplicate files in the same run. They come back as rows with an `errorCode`, so you can see what happened.

**Budget control:** set *Maximum cost per run* in the run options (minimum $0.10). The Actor stops cleanly when the limit is reached and keeps everything converted so far. Use **Pages to convert** or **Max pages per PDF** to cap long documents.

Apify's free plan includes **$5 of platform credit every month**, enough to convert over 1,500 short PDFs before you pay anything.

### Limits at a glance

- **No OCR (yet).** Scanned pages have no text layer. Fully scanned PDFs return a free `no-text-layer` row; in mixed PDFs the scanned pages are listed in `pagesWithoutText`.
- **Borderless tables** are not rebuilt as tables. Tables drawn with cell borders become Markdown tables; tables without borders (common in scientific papers) come out as text lines in reading order.
- **Images, charts and formulas** are not converted.
- Defaults: up to **200 pages** per PDF (`maxPages`, 0 = no limit), **50 MB** per file (`maxFileSizeMb`, up to 500), **300 s** per PDF (`perPdfTimeoutSecs`).

### Use with AI agents (MCP)

PDF to Markdown works as a ready-made tool for Claude, ChatGPT, Cursor, VS Code and any other MCP client through the official [Apify MCP server](https://docs.apify.com/platform/integrations/mcp). The agent gets one tool, `cleandata-labs--pdf-to-markdown`, and you pay the normal pay-per-event price from your Apify account.

**MCP server URL with only this Actor:**

```text
https://mcp.apify.com?tools=cleandata-labs/pdf-to-markdown
```

On first connection your client opens the Apify sign-in (OAuth). To use an API token instead, send the header `Authorization: Bearer <YOUR_APIFY_TOKEN>`.

**Cursor** (`~/.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "pdf-to-markdown": { "url": "https://mcp.apify.com?tools=cleandata-labs/pdf-to-markdown" }
  }
}
```

**Claude Code:**

```bash
claude mcp add --transport http pdf-to-markdown "https://mcp.apify.com?tools=cleandata-labs/pdf-to-markdown"
```

Web pages and PDFs in one connection: `https://mcp.apify.com?tools=cleandata-labs/url-to-markdown,cleandata-labs/pdf-to-markdown`.

#### Tips for agents

- Smallest useful input: `{"urls": ["<PDF link>"], "pageRange": "1-5", "maxCharacters": 20000}`.
- A 10–20 page PDF usually takes 1–3 seconds to convert, so results are ready within the MCP call's default wait.
- Each row has `approxTokens` and `pageCount`, so the agent can decide how much to read. Use `pageRange` or `maxCharacters` to keep long documents small.
- If `errorCode` is `no-text-layer`, the PDF is a scan: it needs OCR, which this Actor does not do.

#### Example prompts

1. "Convert https://arxiv.org/pdf/1706.03762 to Markdown and list its section headings."
2. "Read pages 1–5 of this PDF: <link>, and summarize the key numbers in a table."
3. "Convert these 20 PDF reports into 800-token chunks with page numbers for my RAG index."

### Integrations

- **Make, Zapier and n8n** – start a run and process the Markdown in your no-code workflows.
- **Google Sheets** – export titles, page counts and text to a spreadsheet.
- **Webhooks** – get notified when a run finishes or fails.
- **Apify API and clients** – [Python](https://docs.apify.com/api/client/python) or [JavaScript](https://docs.apify.com/api/client/js) `apify-client`.
- **AI agents** – through the Apify MCP server or the LangChain / LlamaIndex integrations.

Python example:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("cleandata-labs/pdf-to-markdown").call(run_input={"urls": ["https://bitcoin.org/bitcoin.pdf"]})
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item["title"], item["pageCount"], item["markdown"][:200])
```

### Notes & limitations

- No OCR and no borderless-table detection yet: see [Limits at a glance](#limits-at-a-glance).
- Headings are detected from font size and weight; unusual layouts (posters, slides, forms) can give extra or missing headings.
- Text inside charts may appear as short text lines. Math is kept as plain characters.
- Some PDFs use fonts without a Unicode mapping; their text comes out as `(cid:N)` and the row gets a warning.
- "Tokens" are approximated as characters ÷ 4.
- You are responsible for having the right to process the documents you convert.

### FAQ

**Does it need an API key or an AI model?** No. Extraction runs on open-source libraries (pdfplumber and pdfminer.six, MIT license) inside the Actor. No document is sent to a third-party AI service.

**Can it read scanned PDFs?** No, there is no OCR. You get a clear `no-text-layer` row, and you are not charged.

**What about password-protected PDFs?** Put the password in **PDF password** (stored encrypted). Without it you get a free `password-protected` row.

**How do I convert files from my computer?** Use **Upload PDFs** in the input form. The files are stored in an Apify key-value store and converted like any URL.

**Why was a PDF not charged?** Only converted PDFs are charged. Errors, blocked downloads, scans without text, password-protected files and duplicate files in the same run are free.

### More tools from CleanData Labs

Other Actors from the same publisher, built the same way (pay only for delivered results, errors are free, MCP-ready):

- [URL to Markdown Converter](https://apify.com/cleandata-labs/url-to-markdown): Convert web pages into clean, LLM-ready Markdown with metadata, small crawls via links or sitemap, JavaScript fallback and RAG chunks.
- [Keyword Research Tool — Autocomplete & Trends](https://apify.com/cleandata-labs/seo-keyword-research-for-agents): Keyword ideas from Google Autocomplete plus Google Trends direction and rising searches, in one compact JSON per keyword.
- [Google Trends Scraper](https://apify.com/cleandata-labs/google-trends-explorer): Google Trends data without an API key: interest over time, by region, related and rising queries.
- [Google Ads Transparency Scraper](https://apify.com/cleandata-labs/google-ads-transparency-explorer): Export any company's Google ads by brand, website or advertiser ID.

Typical combo: convert a documentation site with **URL to Markdown** and its PDF manuals here, then load both into the same RAG index.

# Changelog

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

# Actor input Schema

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

Public links to PDF files, e.g. \["https://arxiv.org/pdf/1706.03762"]. 'https://' is added if missing. One converted PDF costs one result. Always pass your own URLs: the default is only a demo.

## `files` (type: `array`):

Upload PDF files from your computer (they are stored in an Apify key-value store), or paste a PDF URL or a key-value store record URL (https://api.apify.com/v2/key-value-stores/<storeId>/records/<key>, signed links included).

## `keyValueStoreId` (type: `string`):

Optional: pick one of your Apify key-value stores (or enter its ID). Every PDF record in it is converted; records that are not PDFs come back as free error rows. The Actor gets read-only access to this store only.

## `keyValueStoreKeyPrefix` (type: `string`):

Only convert records whose key starts with this text, e.g. "invoices-". Used only with keyValueStoreId.

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

Optional page selection, e.g. "1-5", "1,3,10-12", "20-" (page 20 to the end) or "-1" (last page). Empty = all pages (up to Max pages).

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

Convert at most this many pages per PDF, 0 = no limit. Default 200. The first 20 pages are included in the per-PDF price; each page above 20 adds a small per-page charge.

## `perPageOutput` (type: `boolean`):

true = also return a 'pages' array with the Markdown of every page separately (page number, characters, tables, whether it has a text layer). Useful for citations. Default false.

## `pageMarkers` (type: `boolean`):

true (default) = insert an HTML comment  before each page in the Markdown, so you can cite pages. Invisible when the Markdown is rendered.

## `includeTables` (type: `boolean`):

true (default) = tables drawn with cell borders become Markdown tables. Tables without borders stay as text lines.

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

true (default) = drop text repeated at the top or bottom of most pages (running titles, page numbers). Needs at least 3 pages.

## `chunkSizeTokens` (type: `integer`):

If > 0, also return 'chunks' split on paragraph boundaries for RAG, about this many tokens each (1 token ~ 4 characters), each with the page it starts on. Default 0 = no chunks.

## `chunkOverlapTokens` (type: `integer`):

Overlap between consecutive chunks in tokens, 0-2000. Default 50. Used only when chunkSizeTokens > 0.

## `maxCharacters` (type: `integer`):

Cut each PDF's Markdown to this many characters, 0 = no limit (default). For AI agents a value such as 20000 (~5k tokens) keeps results small.

## `pdfPassword` (type: `string`):

Optional password for encrypted PDFs (the same password is tried for every file). Stored encrypted.

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

Skip files larger than this (free error row). Default 50 MB.

## `perPdfTimeoutSecs` (type: `integer`):

Stop converting a PDF after this many seconds and return the pages done so far, with a warning. Default 300.

## `respectRobotsTxt` (type: `boolean`):

true (default) = skip URLs that the site's robots.txt disallows. Skipped URLs are reported and not charged.

## `requestTimeoutSecs` (type: `integer`):

Timeout per download attempt in seconds. Default 60.

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

Retries on network errors and HTTP 5xx, 0-5. Default 2.

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

Parallel downloads, 1-10. Default 4. Conversion uses the available CPU cores.

## `proxyConfiguration` (type: `object`):

Optional proxy for sites that block datacenter IPs, e.g. {"useApifyProxy": true}. Default: no proxy.

## `userAgent` (type: `string`):

Optional custom User-Agent header. By default the Actor uses an identifiable bot User-Agent.

## Actor input object example

```json
{
  "urls": [
    "https://bitcoin.org/bitcoin.pdf"
  ],
  "maxPages": 200,
  "perPageOutput": false,
  "pageMarkers": true,
  "includeTables": true,
  "removeHeadersFooters": true,
  "chunkSizeTokens": 0,
  "chunkOverlapTokens": 50,
  "maxCharacters": 0,
  "maxFileSizeMb": 50,
  "perPdfTimeoutSecs": 300,
  "respectRobotsTxt": true,
  "requestTimeoutSecs": 60,
  "maxRetries": 2,
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (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://bitcoin.org/bitcoin.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cleandata-labs/pdf-to-markdown").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://bitcoin.org/bitcoin.pdf"] }

# Run the Actor and wait for it to finish
run = client.actor("cleandata-labs/pdf-to-markdown").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://bitcoin.org/bitcoin.pdf"
  ]
}' |
apify call cleandata-labs/pdf-to-markdown --silent --output-dataset

```

## MCP server setup

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

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/0xhTdHF2u8jle3POo/builds/qclx7VSo6PKkycbkh/openapi.json
