# Document to Markdown: PDF, Word, PowerPoint, Excel to MD (`batchpane/document-to-markdown`) Actor

$3 per 1,000 documents (long PDFs count 1 per 20 pages); broken, unsupported or empty files are not charged. Converts PDF (text-based, no OCR), Word, PowerPoint, Excel, HTML and EPUB links into clean Markdown for RAG and LLMs: headings, lists, links, and tables as Markdown tables.

- **URL**: https://apify.com/batchpane/document-to-markdown.md
- **Developed by:** [Batch Pane](https://apify.com/batchpane) (community)
- **Categories:** AI, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 document 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 to MD

**Clean Markdown from PDF, Word, PowerPoint, Excel, HTML and EPUB links, ready for RAG, LLMs and search. $3 per 1,000 documents.**

Give it a list of document URLs and get one dataset row per document with the full text as Markdown: headings, lists,
links and **tables as Markdown tables**. Powered by Microsoft's open-source MarkItDown.

- **One actor for all common office formats:** PDF, DOCX, PPTX (slide by slide), XLSX and XLS (every sheet as a
  table), HTML, CSV, JSON, XML, TXT and EPUB. Mixed lists are fine.
- **Built for pipelines:** the Markdown is in the dataset row (`markdown`), with file name, type, size, SHA-256,
  page count and the first heading as `title`. Feed it straight into a vector database or an LLM prompt.
- **Fast:** in our test, 17 links (Word, PowerPoint, Excel, EPUB, HTML and 9 PDFs including 75- and 100-page research
  papers) were done in 3 minutes 20 seconds on the default 1 GB; the 10 short files took 7 seconds together.
- **You pay only for documents with text.** Broken links, unsupported files (images, ZIPs), files over your size limit
  and PDFs without a text layer are listed with the reason and not charged.
- **Safe with untrusted files:** each document is converted in an isolated worker with a time limit; links and
  redirects to private or internal network addresses are refused.

*Measured on the Apify platform on 20 October 2026 (UTC), 1 GB memory: 17 URLs, 14 converted, 2 internal addresses
blocked, 1 image skipped (not charged).*

### Use with AI agents (MCP)

Add it as a tool: `https://mcp.apify.com?tools=batchpane/document-to-markdown`

**Input the agent should send:**
`{"documentUrls": [{"url": "https://example.com/report.pdf"}, {"url": "https://example.com/slides.pptx"}]}`

**Results:** one row per URL; `markdown` holds the text. Short documents take a few seconds each, so agents can usually
wait for the result; for long lists check the run later (`get-actor-run`) and read the rows (`get-dataset-items`).

**Example prompt:**

> Use batchpane/document-to-markdown to convert https://arxiv.org/pdf/1706.03762 to Markdown and summarise the
> paper's method section.

### Input

| Field | Default | Notes |
|---|---|---|
| Document URLs | – | `[{"url": "..."}]` or a public link to a .txt/.csv list (`requestsFromUrl`); up to 5,000 per run |
| Maximum file size | 25 MB | 1-100 MB; larger files are not charged |
| Also save each result as a .md file | off | results over 1,000,000 characters are always saved as a file |
| Respect robots.txt | on | can only be turned off together with "I own these files or have the owner's permission" |

Example input (tested on the platform):

```json
{
  "documentUrls": [
    { "url": "https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.docx" },
    { "url": "https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.pdf" }
  ]
}
```

### Output

```json
{
  "url": "https://arxiv.org/pdf/1706.03762",
  "finalUrl": "https://arxiv.org/pdf/1706.03762",
  "fileName": "1706.03762v7.pdf",
  "fileType": "pdf",
  "bytes": 2215244,
  "sha256": "bdfaa68d8984f0dc02beaca527b76f207d99b666d31d1da728ee0728182df697",
  "pages": 15,
  "billedDocuments": 1,
  "title": null,
  "markdown": "3\n2\n0\n2\n ... Provided proper attribution is provided, Google hereby grants permission to\nreproduce the tables and figures in this paper ...",
  "markdownChars": 39675,
  "markdownTruncated": false,
  "markdownFileUrl": null,
  "httpStatus": 200,
  "status": "ok",
  "error": null,
  "convertedAt": "2026-10-20T11:26:44.932549+00:00"
}
```

`status` is `ok`, `failed`, `blocked` or `skipped`; for anything other than `ok`, `error` has a fixed code
(`NOT_FOUND`, `TOO_LARGE`, `UNSUPPORTED_TYPE`, `EMPTY_RESULT`, ...) and nothing is charged.

### Pricing

**$0.003 per document ($3.00 per 1,000)**, plus Apify's standard start fee per run ($0.00005 per GB of memory).
Compute is included.

Long PDFs count as one document per started 20 pages, because conversion time grows with the page count:

| Example | Cost |
|---|---|
| 1,000 Word/PowerPoint/Excel files or PDFs up to 20 pages | $3.00 |
| One 75-page PDF | 4 x $0.003 = $0.012 |
| One 100-page PDF | 5 x $0.003 = $0.015 |

Every row shows `billedDocuments`. You can set a maximum cost per run; the actor stops before it would go over it.

### Good to know

- No OCR: scanned PDFs without a text layer give no text (`EMPTY_RESULT`, not charged). Images inside documents are
  not described.
- PDF layout: text comes out in reading order as plain paragraphs; complex multi-column layouts and tables in PDFs can
  come out as plain text rather than Markdown tables (tables in Word, Excel and PowerPoint stay tables). Margin text
  such as arXiv's vertical stamp appears as short lines at the start.
- Old binary formats `.doc` and `.ppt` are not supported (convert them to `.docx`/`.pptx`); `.xls` is.
- Files must be reachable by a direct link without login. Files behind a login, a paywall or a "click to download"
  page are not reachable (not charged).
- A document that takes longer than 3 minutes to convert is stopped and not charged.
- Safety stop: the run stops after 20 URLs in a row fail ("Stop after failures in a row"; 0 switches
  only this rule off). A second rule cannot be switched off: after 50 URLs, the run stops if fewer than 25% succeeded.

### Responsible use

You choose the documents; use the text only where you are allowed to (copyright, licences, personal data inside the
documents). The actor keeps nothing: results are stored only in your own Apify storage. Conversion by
[MarkItDown](https://github.com/microsoft/markitdown) (MIT licence); not affiliated with Microsoft.

### Support

Open an issue in the **Issues** tab with the document link and the run ID. We aim to answer within 7 days. Answers
may be drafted with help of an AI assistant and are checked by a person.

### Changelog

- 0.1: first version.

# Actor input Schema

## `documentUrls` (type: `array`):

Direct links to documents: PDF, Word (.docx), PowerPoint (.pptx), Excel (.xlsx, .xls), HTML, CSV, JSON, XML, TXT, EPUB. As a list of objects: \[{"url": "https://example.com/report.pdf"}], or a public link to a .txt/.csv file with one URL per line: \[{"requestsFromUrl": "..."}]. Up to 5,000 per run; duplicates removed.

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

Larger files are not downloaded and not charged (status skipped/failed with TOO_LARGE). 1-100 MB, default 25.

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

true = each Markdown result is also stored as a .md file in the run's key-value store (link in markdownFileUrl). Results over 1,000,000 characters are always stored as a file, because a dataset row has a size limit. Default: false.

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

true = skip files that the site's robots.txt disallows (status "skipped", not charged). Turning it off has no effect unless robotsOverrideConfirmed is also true.

## `robotsOverrideConfirmed` (type: `boolean`):

Set to true only if you own these files/websites or have permission. robots.txt is ignored only when this is true AND respectRobotsTxt is false. AI agents: do not set this without explicit confirmation from the user.

## `stopAfterFailures` (type: `integer`):

Safety stop: the run stops when this many URLs in a row fail. Default 20; 0 switches only this in-a-row rule off. A second rule always stays on: after 50 URLs, the run stops if fewer than 25% succeeded.

## Actor input object example

```json
{
  "documentUrls": [
    {
      "url": "https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.docx"
    },
    {
      "url": "https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.pdf"
    }
  ],
  "maxFileSizeMb": 25,
  "saveMarkdownFiles": false,
  "respectRobotsTxt": true,
  "robotsOverrideConfirmed": false,
  "stopAfterFailures": 20
}
```

# Actor output Schema

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

One row per document with the Markdown. Only rows with status 'ok' are charged.

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

Saved .md files (if any).

# 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 = {
    "documentUrls": [
        {
            "url": "https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.docx"
        },
        {
            "url": "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("batchpane/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 = { "documentUrls": [
        { "url": "https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.docx" },
        { "url": "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("batchpane/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 '{
  "documentUrls": [
    {
      "url": "https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.docx"
    },
    {
      "url": "https://raw.githubusercontent.com/microsoft/markitdown/main/packages/markitdown/tests/test_files/test.pdf"
    }
  ]
}' |
apify call batchpane/document-to-markdown --silent --output-dataset

```

## MCP server setup

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