# Document to Markdown – PDF, Word, PowerPoint, Excel + OCR (`kris_fili/document-to-markdown`) Actor

Convert PDF, DOCX, PPTX, XLSX, HTML, EPUB and images to clean Markdown or plain text for LLMs and RAG. Tables kept, scanned pages read with OCR in 100+ languages, optional RAG chunks with page numbers.

- **URL**: https://apify.com/kris_fili/document-to-markdown.md
- **Developed by:** [Krzysztof Filipiak](https://apify.com/kris_fili) (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 $1.00 / 1,000 page converteds

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

## Document to Markdown – PDF, Word, PowerPoint, Excel + OCR

Turn **PDF, Word (DOCX), PowerPoint (PPTX), Excel (XLSX/XLS), HTML, EPUB, CSV, Outlook (MSG) and images** into clean **Markdown or plain text** – ready for ChatGPT, Claude, LangChain, LlamaIndex and any RAG pipeline.

- 📄 **Headings, lists and tables preserved** – PDF tables become Markdown tables, not a jumble of words.
- 🔍 **Scanned pages are read with OCR** automatically (100+ languages), mixed PDFs included.
- ✂️ **RAG-ready chunks** with page numbers and section path – one dataset item per chunk, straight into a vector database.
- 🧹 **Clean text** – running headers, footers and page numbers removed; hyphenated words re-joined; paragraphs split across pages merged.
- 🔗 Paste **Google Drive, Google Docs, Dropbox or GitHub** share links – they are turned into downloads for you.
- 💸 **From $0.001 per page**, no subscription. Failed files are free.

### Who uses it

| You want to… | How |
|---|---|
| Feed documents to an LLM or an AI agent | `outputFormat: markdown` – one item per document |
| Build a RAG knowledge base | `chunkSize: 1500` – chunks with `pageStart`, `pageEnd`, `headingPath` |
| Extract tables from PDF reports | Markdown tables in the output, or `.md` files to download |
| Digitise scanned contracts, invoices, letters | `ocr: auto` + `ocrLanguages: eng+deu` |
| Convert slide decks and spreadsheets to text | PPTX slides and XLSX sheets become sections |
| Batch-convert thousands of files | Give a list of URLs, raise `maxConcurrency` and memory |

### How to use

1. Paste document URLs into **Document URLs** (or upload files).
2. Pick **Markdown** or **Plain text**. For RAG set **RAG chunk size** (e.g. 1500).
3. Click **Start**. Each document (or chunk) appears in the dataset; every document is also saved as a `.md` file.

#### Input example

```json
{
  "urls": [
    "https://arxiv.org/pdf/1706.03762",
    "https://example.com/annual-report.docx"
  ],
  "outputFormat": "markdown",
  "ocr": "auto",
  "ocrLanguages": "eng",
  "chunkSize": 0
}
```

#### Output example (one document)

```json
{
  "url": "https://arxiv.org/pdf/1706.03762",
  "fileName": "1706.03762v7.pdf",
  "fileType": "pdf",
  "status": "ok",
  "title": "Attention Is All You Need",
  "pages": 15,
  "pagesConverted": 15,
  "ocrPages": 0,
  "characters": 38781,
  "words": 6239,
  "markdown": "# Attention Is All You Need\n\nAshish Vaswani ∗ Google Brain avaswani@google.com\n\n...\n\n## Abstract\n\nThe dominant sequence transduction models are based on complex recurrent or convolutional neural networks ...\n\n## 1 Introduction\n\n...",
  "markdownUrl": "https://api.apify.com/v2/key-value-stores/…/records/0001-1706.03762v7.md"
}
```

#### Output example (RAG chunk, `chunkSize: 1500`)

```json
{
  "fileName": "1706.03762v7.pdf",
  "chunkIndex": 7,
  "chunkCount": 36,
  "pageStart": 3,
  "pageEnd": 3,
  "headingPath": "Attention Is All You Need > 3 Model Architecture > 3.1 Encoder and Decoder Stacks",
  "text": "### 3.1 Encoder and Decoder Stacks\n\nEncoder: The encoder is composed of a stack of N = 6 identical layers. Each layer has two sub-layers. ...",
  "characters": 1360,
  "approxTokens": 340
}
```

Failed files appear with `status: "error"` and a plain-English `error` (e.g. *access denied – the file must be publicly downloadable*). They are not charged.

### Supported formats

| Format | Notes |
|---|---|
| PDF | Text layer + automatic OCR of scanned pages; tables; page ranges; password-protected PDFs (`pdfPassword`) |
| Word `.docx` | Headings, lists, tables, bold/italic, links |
| PowerPoint `.pptx` | Slide by slide, including tables and speaker notes |
| Excel `.xlsx`, `.xls`, CSV | Every sheet as a Markdown table |
| HTML, EPUB, TXT, MD, JSON, XML, Jupyter `.ipynb`, Outlook `.msg` | Converted to Markdown |
| Images: PNG, JPEG, TIFF (multi-page), WebP, GIF, BMP | OCR |

Older binary formats (`.doc`, `.ppt`, `.rtf`, OpenDocument) – save them as `.docx`/`.pptx`/PDF first.

### Pricing

Pay per event – you only pay for pages actually converted:

| Event | Price |
|---|---|
| Page converted (PDF page, PowerPoint slide; other formats: every started 3,000 characters of output) | **$0.001** |
| Page read with OCR (scanned page or image) – instead of the above | **$0.003** |

Examples: a 20-page text PDF costs **$0.02**; a 10-page scanned contract **$0.03**; 1,000 one-page invoices (text PDFs) **$1**. Apify platform usage is included in the price. You can set a maximum cost per run in the run options.

### Use it from code, AI agents and automation tools

**API (any language):**

```bash
curl -X POST "https://api.apify.com/v2/acts/kris_fili~document-to-markdown/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://arxiv.org/pdf/1706.03762"], "chunkSize": 1500}'
```

**Python:**

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_TOKEN")
run = client.actor("kris_fili/document-to-markdown").call(run_input={"urls": ["https://arxiv.org/pdf/1706.03762"]})
for doc in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(doc["title"], len(doc["markdown"]))
```

**AI agents (MCP):** connect Claude, Cursor or any MCP client to `https://mcp.apify.com?tools=kris_fili/document-to-markdown` and ask *"convert this PDF to Markdown: …"*.

**No-code:** use the Apify integration in Make, Zapier or n8n, or the **Integrations** tab to send results to Google Drive, Slack or a webhook.

Need only text from photos and scans (with word positions)? Use **[Image to Text OCR](https://apify.com/kris_fili/image-to-text-ocr)**. Need screenshots or PDFs of web pages? Use **[Website Screenshot & PDF](https://apify.com/kris_fili/website-screenshot-pdf)**.

### FAQ

**How accurate is it?** Text PDFs are converted exactly from their text layer. Tables with ruling lines are detected reliably; borderless tables may come out as text. OCR accuracy is typically 95%+ on clean scans at 300 DPI and lower on photos and handwriting (handwriting is not supported). Each OCR result has an `ocrConfidence` score.

**Which OCR languages?** English by default; set `ocrLanguages` to any of 100+ languages (e.g. `deu`, `fra`, `spa`, `pol`, `chi_sim`, `jpn`, `ara`) or combine them (`eng+deu`).

**How big can files be?** Up to 500 MB per file (`maxFileSizeMb`) and 5,000 pages per document (`maxPages`). For big batches give the run more memory – OCR uses all CPU cores of the run.

**Are my documents private?** Files are downloaded and processed only inside your own run; results are stored in your Apify account's storage. Nothing is shared or kept by the developer.

**Can it read files behind a login?** No – files must be downloadable from their URL. For Google Drive and Dropbox share them as *anyone with the link*, or upload them in the input.

Questions or a format you need? Open an issue on the **Issues** tab – we answer quickly.

# Actor input Schema

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

Direct links to files: PDF, DOCX, PPTX, XLSX, XLS, CSV, HTML, EPUB, TXT, MD, JSON, XML, MSG, IPYNB or images (PNG, JPEG, TIFF, WebP). Google Drive, Google Docs, Dropbox and GitHub share links are converted to downloads automatically (files must be shared as 'anyone with the link').

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

Upload documents from your computer (stored in your Apify key-value store).

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

Markdown keeps headings, lists and tables (best for LLMs). Text removes the Markdown syntax.

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

0 = one result per document. Above 0 = split each document into chunks of at most this many characters (about 4 characters per token) – one dataset item per chunk with page numbers and heading path, ready for a vector database. Paragraphs and tables are kept together when possible.

## `chunkOverlap` (type: `integer`):

How many characters from the end of a chunk are repeated at the start of the next one (only with chunking).

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

Auto: OCR only pages without a text layer (scans, photos). Always: OCR every PDF page (for PDFs with a broken text layer). Off: never OCR.

## `ocrLanguages` (type: `string`):

Language(s) of scanned text, joined with +, e.g. eng, deu, fra, spa, pol, eng+deu, chi_sim, jpn. Two-letter codes work too (en, de, pl). 100+ languages supported.

## `ocrPageSegmentation` (type: `string`):

Auto works for most documents. Use single column for receipts and narrow text, sparse for forms, signs and screenshots with scattered text.

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

Convert only these PDF pages, e.g. 1-5,8,10- (empty = all pages).

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

Safety limit per document (PDF pages, image frames).

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

Insert  (or ) comments between pages in the Markdown.

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

Lines repeated at the top or bottom of most pages (running titles, page numbers) are removed – cleaner text for LLMs.

## `saveMarkdownFiles` (type: `boolean`):

Also save every document as a .md file in the run's key-value store (link in 'markdownUrl').

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

Password for protected PDFs (used for all PDFs in this run).

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

Bigger files are skipped with an error (no charge).

## `maxFiles` (type: `integer`):

Stop after this many files.

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

For many small files raise it (and the run memory). OCR uses all CPU cores of the run anyway.

## Actor input object example

```json
{
  "urls": [
    "https://arxiv.org/pdf/1706.03762"
  ],
  "outputFormat": "markdown",
  "chunkSize": 0,
  "chunkOverlap": 150,
  "ocr": "auto",
  "ocrLanguages": "eng",
  "ocrPageSegmentation": "auto",
  "maxPages": 500,
  "pageMarkers": false,
  "removeHeadersFooters": true,
  "saveMarkdownFiles": true,
  "maxFileSizeMb": 100,
  "maxFiles": 1000,
  "maxConcurrency": 2
}
```

# Actor output Schema

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

No description

## `files` (type: `string`):

No description

## `summary` (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://arxiv.org/pdf/1706.03762",
        "https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.pptx"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("kris_fili/document-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://arxiv.org/pdf/1706.03762",
        "https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.pptx",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("kris_fili/document-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://arxiv.org/pdf/1706.03762",
    "https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.pptx"
  ]
}' |
apify call kris_fili/document-to-markdown --silent --output-dataset

```

## MCP server setup

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