# PDF to Markdown & Text with OCR (Tables, Scans) (`tidelane/pdf-to-markdown`) Actor

Convert PDF files to clean Markdown or plain text for AI, RAG and data extraction. Headings, lists and tables are kept, scanned pages are read with OCR in 8 languages (English, German, French, Spanish, Italian, Portuguese, Dutch, Polish). Page ranges, one item per page or per document.

- **URL**: https://apify.com/tidelane/pdf-to-markdown.md
- **Developed by:** [Tidelane Data](https://apify.com/tidelane) (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 $0.70 / 1,000 pdf pages

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

## PDF to Markdown & Text with OCR (Tables, Scans)

Turn PDF files into clean **Markdown** or **plain text**, ready for ChatGPT, RAG pipelines, search indexes or data extraction. Give it links to PDFs and get back one dataset item per document (or per page) with the text, headings, lists and tables. **Scanned pages are read with OCR** automatically.

- ✅ **Headings, lists and tables** become real Markdown (`#`, `-`, `| … |`)
- ✅ **Two-column layouts** (papers, newsletters, forms) are read column by column, not line across line
- ✅ **OCR for scans** in English, German, French, Spanish, Italian, Portuguese, Dutch and Polish; mixed PDFs only OCR the pages that need it
- ✅ **Clean text**: words in tightly set PDFs are split correctly, rotated margin stamps and unmapped glyphs are removed, hyphenated words are rejoined
- ✅ **Page ranges** (`1-3, 7, 10-`) and a page limit per document
- ✅ **Token estimate** per item so you can plan LLM costs
- ✅ **Fair billing**: you pay per converted page; pages that fail, broken links and non-PDF files are free

### Input example

```json
{
    "pdfUrls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf", "https://arxiv.org/pdf/1810.04805"],
    "outputFormat": "markdown",
    "ocr": "auto",
    "ocrLanguages": ["eng"]
}
```

| Field | What it does |
|---|---|
| `pdfUrls` | Direct links to PDF files (up to 100 MB each) |
| `outputFormat` | `markdown` (default) or `text` |
| `ocr` | `auto` (only pages without a text layer), `always` or `never` |
| `ocrLanguages` | Languages for OCR, e.g. `["deu", "eng"]` |
| `pages` | Page range like `1-3, 7, 10-`; empty means all pages |
| `maxPages` | At most this many pages per document (default 500) |
| `outputPerPage` | One dataset item per page instead of per document |

### Output example

Real output from a test run (the `content` field is shortened here):

```json
{
    "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
    "fileName": "fw9.pdf",
    "title": "Form W-9 (Rev. March 2024)",
    "pageCount": 6,
    "pagesProcessed": 6,
    "ocrPages": 0,
    "tables": 1,
    "format": "markdown",
    "content": "# W-9\n\n# Request for Taxpayer\n\n… Check only one box on line 3a.\n\n| IF the entity/individual on line 1 is a(n) . . . | THEN check the box for . . . |\n| --- | --- |\n| • Corporation | Corporation. |\n| • Individual or • Sole proprietorship | Individual/sole proprietor. |\n| • Partnership | Partnership. | …",
    "charCount": 37933,
    "wordCount": 6338,
    "approxTokens": 9484
}
```

A scanned page from the same run (OCR, English):

```
The LinnSequencer is a state-of-the-art composition and performance tool for the professional musician. It is extremely powerful, yet amazingly simple to learn and use.
```

A summary per PDF (`ok` or `error` with the reason) is saved as `OUTPUT` in the key-value store.

### Pricing

You pay per converted page, plus a small extra per page that needed OCR. In our tests 23 pages from 3 PDFs (one of them scanned) took 12 seconds with the default 1 GB memory.

Paid Apify plans pay less: 10% off on Starter, 20% on Scale and 30% on Business and higher plans.

### FAQ

**Which tables are detected?** Tables with ruling lines (borders or cell lines), like most forms, reports and statistics. Tables drawn without any lines come out as text lines in the right order.

**Can it read password-protected PDFs?** No. Such files are reported as an error and are not charged.

**My scan has a different language.** Pick the languages in `ocrLanguages`; several at once work (`["deu", "eng"]`), but each extra language makes OCR a little slower.

**Can I use files from my computer?** Upload them anywhere that gives a direct link (for example an Apify key-value store or a cloud drive share link that downloads the file) and paste that link.

***

#### Impressum

Levi Griesinger, Tidelane Data\
Besigheimer Straße 18\
74369, Löchgau, Deutschland\
E-Mail: LeviGriesinger@gmail.com

# Actor input Schema

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

Direct links to PDF files, one per line (up to 100 MB each).

## `outputFormat` (type: `string`):

Markdown keeps structure for AI and RAG; plain text is the raw reading order.

## `ocr` (type: `string`):

Scanned pages have no text layer and are read with Tesseract OCR. OCR pages cost a little extra.

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

Languages in the scans. Allowed: eng, deu, fra, spa, ita, por, nld, pol. Fewer languages are faster and more accurate.

## `pages` (type: `string`):

Optional page range, for example "1-3, 7, 10-". Empty means all pages.

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

Stop after this many pages per document.

## `outputPerPage` (type: `boolean`):

Create one dataset item per page (handy for RAG chunks and citations) instead of one per document.

## Actor input object example

```json
{
  "pdfUrls": [
    "https://arxiv.org/pdf/1706.03762"
  ],
  "outputFormat": "markdown",
  "ocr": "auto",
  "ocrLanguages": [
    "eng"
  ],
  "maxPages": 500,
  "outputPerPage": false
}
```

# Actor output Schema

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

All results of the run as dataset items.

## `summary` (type: `string`):

Summary per input with status (ok, error and the reason).

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

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

```

## MCP server setup

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