# PDF, Word, Excel & PowerPoint to Markdown for RAG (`propped_cloak/document-to-markdown`) Actor

Convert PDF, DOCX, XLSX, PPTX, HTML and EPUB files to clean Markdown with tables, page markers and optional RAG chunks. Built for LLM pipelines. Pay per page, no text no charge.

- **URL**: https://apify.com/propped_cloak/document-to-markdown.md
- **Developed by:** [Emmanuel Vingadassalon](https://apify.com/propped_cloak) (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.80 / 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.
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, Word, Excel & PowerPoint to Markdown for RAG

Turn documents into **clean Markdown that LLMs read well**: PDF, DOCX, XLSX, XLS, PPTX, HTML, EPUB, CSV and TXT. You get tables as Markdown tables, page markers for citations, and optional chunks ready for embeddings.

- **Correct word spacing on PDFs.** Many extractors glue words together on LaTeX and InDesign PDFs ("Providedproperattributionisprovided"). This Actor reads characters with tuned tolerances, so the text stays readable for search and embeddings.
- **Tables kept as tables.** PDF tables are detected and rendered as Markdown tables, separately from the body text. Excel sheets, Word tables and PowerPoint tables become Markdown tables too.
- **Page markers for RAG citations.** Every PDF page starts with `<!-- page N -->`, so your pipeline can cite the page an answer came from.
- **Chunks included.** Set `chunkSize` and each document is also split on paragraph boundaries into chunks of at most N characters.
- **Pay per page, no text no charge.** Scanned PDFs without a text layer, empty files and failed downloads are returned with an error message and are not billed.

### What can I use it for?

- **RAG and chatbots:** feed contracts, manuals, reports or slide decks into a vector database.
- **Data extraction with LLMs:** give a model clean Markdown instead of messy PDF text, and get better answers and fewer tokens.
- **Search indexing and archiving:** turn a folder of Office files into plain text you can search.
- **AI agents:** an agent can call this Actor as a `read_document` tool through the Apify MCP server (see below).

### Input

```json
{
    "urls": [
        "https://example.com/annual-report.pdf",
        "https://example.com/price-list.xlsx"
    ],
    "chunkSize": 2000,
    "pdfTables": true
}
```

**Speed.** With table detection (`pdfTables: true`, the default), a long PDF takes about 2 seconds per page at the default 1 GB of memory. Set `pdfTables: false` for text only, which is about 30x faster. You can also give the run 4 GB of memory to get 4x more CPU. The price per page is the same either way.

Each URL must point directly to a file of up to 50 MB. The format is detected from the file extension or the `Content-Type` header.

### Output

One row per document. The example is shortened; real values come from a test run on the 142-page IRS Publication 17.

```json
{
    "url": "https://www.irs.gov/pub/irs-pdf/p17.pdf",
    "fileName": "p17.pdf",
    "format": "pdf",
    "title": null,
    "pages": 142,
    "tables": 69,
    "characters": 1039871,
    "markdown": "<!-- page 1 -->\n\n... Publication 17\nYour Federal\nFor use in preparing\nIncome Tax\n2025\nReturns\nFor Individuals\n...\n\n| TAX GUIDE 2025 |\n| --- |\n| Get forms and other information faster and easier at: ... |\n\n<!-- page 2 -->\n...",
    "chunks": [{ "index": 0, "text": "..." }],
    "convertedAt": "2026-10-10T12:40:58.784016+00:00"
}
```

| Field | Meaning |
|---|---|
| `pages` | Number of pages (PDF only). |
| `tables` | Number of tables rendered as Markdown tables. |
| `markdown` | The full document. For documents over 4 million characters, it is stored in the run's key-value store and `markdownUrl` links to it. |
| `chunks` | Only when `chunkSize` > 0. |
| `error` | Why a document was skipped, e.g. a scanned PDF or a failed download. Rows with an error are not charged. |

### Pricing

**$0.001 per page converted ($1 per 1,000 PDF pages)**, plus Apify's small run-start fee. Each non-PDF file (DOCX, XLSX, PPTX, HTML...) counts as one page, whatever its length. There is no subscription. Scanned PDFs without text, empty files and failed downloads are free.

### Limits

- **No OCR yet.** Scanned PDFs (images of text) return an error and are not charged.
- PDF table detection works best on tables with ruled lines. On borderless tables, the cells may stay in the body text.
- Tables are detected on the first 300 pages of a PDF. Pages after that are converted as text only.
- The files must be reachable by URL. Files behind a login are not supported.

### Use it from AI agents (MCP)

Add `document-to-markdown` to the [Apify MCP server](https://mcp.apify.com) in your MCP client (Claude, Cursor, VS Code...), and your agent can read any document by URL.

### Integrations

Apify API, schedules, webhooks, Make, Zapier, n8n. Export as JSON, CSV, Excel or HTML.

### FAQ

**Which engine is used?** For PDFs, pdfplumber handles text and tables, and pdfium handles very long documents. Other formats go through Microsoft's open-source MarkItDown. Everything runs inside the Actor: your files are not sent to any third-party API.

**Is my data kept?** Files are downloaded to the run's temporary disk, converted, then deleted. The Markdown is stored only in your own Apify dataset.

**Something wrong or missing?** Open an issue on the Issues tab.

# Actor input Schema

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

Direct links to the files (PDF, DOCX, XLSX, XLS, PPTX, HTML, EPUB, CSV, TXT), one per line. Up to 50 MB per file. The file type is detected from the extension or the Content-Type header.

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

Also split the Markdown into chunks of at most this many characters, cut on paragraph boundaries. Ready for embeddings. 0 = no chunks.

## `pdfTables` (type: `boolean`):

Render PDF tables as Markdown tables (first 300 pages). Turn off for text-only conversion, about 100x faster on long PDFs. Same price.

## Actor input object example

```json
{
  "urls": [
    "https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.pdf"
  ],
  "chunkSize": 0,
  "pdfTables": true
}
```

# Actor output Schema

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

One row per document: Markdown, page count, tables and optional RAG chunks.

# 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://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.pdf"
    ]
};

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

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

```

## MCP server setup

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