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

Converts PDF, Word (.docx), Excel (.xlsx, .xls, .csv), PowerPoint (.pptx), EPUB and HTML files from URLs into clean Markdown for LLMs and RAG: headings, paragraphs and tables kept, optional chunks with page numbers. Obeys robots.txt. $0.003 per document.

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

## Pricing

from $2.10 / 1,000 documents

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

## Document to Markdown: PDF, Word, Excel, PowerPoint, EPUB

Give it links to documents and get each one back as clean Markdown, ready for an LLM, a RAG index or an AI agent. It reads PDF, Word (.docx), Excel (.xlsx, .xls, .ods, .csv), PowerPoint (.pptx), EPUB and HTML files, all in one tool. Headings, paragraphs, lists and tables are kept, and spreadsheets and slide tables come out as Markdown tables. Ask for chunks and each document is also split into pieces of the size you choose, each with the pages it covers. Scanned PDFs can be read too, with OCR. $0.003 per document, and files that fail are free. PDF tables can be found too, for $0.002 per page with a table. It works for people and for AI agents through Apify's MCP server.

### What it's for

- Load reports, manuals and contracts into a RAG index or a custom GPT.
- Give an AI agent the text of a PDF, a slide deck or a spreadsheet it found.
- Turn a folder of mixed files (once they're online) into one kind of text.
- Pull the tables out of spreadsheets and slides as Markdown.

### Input

| Field | What to put |
|---|---|
| Document URLs | One per line, like `https://example.com/report.pdf`. Required. The type is read from the file itself, so download links without an extension work too. |
| Chunk size | Optional. For a RAG index: about this many characters per chunk, cut at paragraph ends where it can. 0 (the default) gives the Markdown only. |
| Read scanned pages (OCR) | Off by default. Turn it on to read PDF pages that are pictures of text, like scans. $0.01 per page read this way. |
| Find tables in PDFs | Off by default. Turn it on to get tables in PDFs as Markdown tables, and as rows and CSV. $0.002 per page with a table. |
| OCR languages | The languages of the scanned text. English by default; 28 to pick from. Pick only the ones you need, since each one makes reading slower. |
| Max file size (MB) | Bigger files are skipped for free. Default 50. |
| Proxy | Off by default. Turn on Apify Proxy for a site that blocks cloud servers. |

Example:

```json
{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
    "https://calibre-ebook.com/downloads/demos/demo.docx"
  ],
  "chunkSize": 1500
}
```

### Output

One row per document. This one came from a test run on October 16, 2026 (Markdown and chunks cut short here):

```json
{
  "type": "document",
  "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "fileName": "fw9.pdf",
  "format": "pdf",
  "title": "Form W-9 (Rev. March 2024)",
  "author": "SE:W:CAR:MP",
  "pages": 6,
  "words": 6504,
  "characters": 37850,
  "bytes": 140815,
  "markdown": "## Form W-9\n\n(Rev. March 2024)\n\n## Request for Taxpayer ...\n\n## What Is FATCA Reporting?\n\nThe Foreign Account Tax Compliance Act (FATCA) requires a participating foreign financial institution to report ...",
  "chunks": [
    { "index": 0, "text": "## Form W-9\n\n(Rev. March 2024) ...", "characters": 1328, "pageFrom": 1, "pageTo": 1 }
  ],
  "markdownUrl": null,
  "convertedAt": "2026-10-16T01:11:15.204Z"
}
```

- `format` is `pdf`, `docx`, `xlsx`, `xls`, `ods`, `csv`, `pptx`, `epub`, `html` or `text`.
- `pages` counts PDF pages, slides, spreadsheet sheets or EPUB chapters (1 for Word and HTML). Chunks give `pageFrom` and `pageTo` in the same units.
- `ocrPages` says how many pages were read with OCR.
- With Find tables in PDFs on, `tables` lists each table with its `page`, its `rows` (header first) and `csv`, and `tablePages` counts the pages they're on. The tables are also in the Markdown, where they were on the page. From a test run on an EU report (rows cut short here):

```json
"tablePages": 3,
"tables": [
  { "page": 1, "rows": [["", "to air kg/year", "to water kg/year", "to land kg/year"], ["Carbon dioxide (CO2)", "100 million", "-", "-"]], "csv": ",to air kg/year,to water kg/year,to land kg/year\nCarbon dioxide (CO2),100 million,-,-" }
]
```

- A file that couldn't be converted has an `error` instead, like an old .doc file or a scanned PDF with OCR off, and isn't charged.
- A very large document (over 5 million characters) keeps its first 100,000 characters in the row, with the whole Markdown at `markdownUrl`.

The run's `SUMMARY` record says how many documents were converted and failed, the pages and words in all, documents per format, and why the run stopped.

### How each type comes out

- **PDF:** text in reading order, with lines joined into paragraphs and larger type turned into headings. Two-column pages read column by column. Tables come out as Markdown tables with Find tables in PDFs on, and as plain text without it.
- **Word:** headings, paragraphs, lists, links and tables, from the document's own styles.
- **Excel and CSV:** each sheet as a Markdown table under its name, without empty rows, columns and sheets.
- **PowerPoint:** each slide as `## Slide N: title`, then its text as bullets, its tables, and its speaker notes.
- **EPUB:** the chapters in reading order, with the book's title and author.
- **HTML:** the page's body, without scripts and styles.

### Price

- $0.003 per document converted, so $3 per 1,000. That covers up to 50 pages (or slides, sheets, chapters).
- $0.0001 for each page past a document's 50th, so a 250-page PDF is $0.003 + $0.02.
- $0.01 for each scanned page read with OCR, only when you turn it on. Pages that already have text are never read again, and scanned pages where OCR finds no text aren't charged.
- $0.002 for each PDF page with a table, only when you turn on Find tables in PDFs. Pages without one aren't charged, and Word, Excel and PowerPoint tables are always free.
- That's close to a document's price, so a table-heavy PDF costs more: a 40-page report with tables on 20 pages is $0.003 + $0.04.
- A page where a chart or numbered list is taken for a table is charged too. That's about 1 in 10 charged pages (14 of 133 in our test of 67 government reports).
- Forms with columns of text (like tax forms) get flagged as tables more often than that. Our first live run of the IRS W-9 flagged 4 of its 6 pages.
- Free: files that fail to download, files that aren't documents, scanned PDFs with OCR off, and addresses robots.txt disallows.
- You can set a maximum charge for a run. It stops when it's reached and keeps what it already converted. With tables on, pages past your maximum keep their tables as plain text, then the run stops.

### Good to know

- **Scanned PDFs.** Turn on Read scanned pages (OCR) and pages without text are read with Tesseract, run inside the tool, so your files go to no other service. It takes about 15 seconds a page, so a run with OCR on starts with 4 GB of memory to read faster. A PDF with typed pages and scanned ones gets both. With OCR off, a PDF that is only scans gets a free error row.
- **PDF tables.** Tables are found from how the text lines up, so ones without ruled lines work too. On the 67 PDFs of the ICDAR 2013 table benchmark (EU and US government reports), 119 of the 133 pages it found tables on have a table in the benchmark's answer key, and it missed 16. Charts with numbers on their axes sometimes come out as a table. Scanned pages read with OCR come out as text.
- **Old Office files.** Word and PowerPoint files from Office 97-2003 (.doc, .ppt) aren't supported; save them as .docx or .pptx. Old Excel files (.xls) work.
- **Images.** Pictures inside documents are left out. The Markdown is the text, lists and tables.
- **Polite.** It reads each site's robots.txt and skips what it disallows, and paces requests to the same site.
- **Your files.** Make sure you have the right to use the documents you convert.

### Use it from an AI agent

The Actor works through Apify's MCP server like any Store Actor. An agent can find it, read its input and call it. One or two documents usually finish within the MCP call's wait. The MCP call waits 30 seconds by default (45 at most). A bigger run takes longer, so the call returns the run's status and dataset ID, and the agent reads the rows with `get-dataset-items`.

### Questions or problems

Open an issue on this Actor's Issues tab. I read them.

# Actor input Schema

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

One per line: PDF, Word (.docx), Excel (.xlsx, .xls, .ods, .csv), PowerPoint (.pptx), EPUB or HTML files, like https://example.com/report.pdf. The type is read from the file itself, so addresses without an extension work too.

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

For a RAG index: split each document into chunks of about this many characters, at paragraph ends where possible. Each chunk says which pages (or slides, sheets, chapters) it covers. 0 gives the Markdown only.

## `ocr` (type: `boolean`):

Reads PDF pages that have no text, like scans and photographed pages, with Tesseract OCR. Each page read this way is one ocr-page event on top of the document's price; pages that already have text are never read again. Off: a PDF that is all scanned pages gives a free error row.

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

The languages of the scanned text. Pick only the ones in your documents: each extra language makes reading slower and a little less accurate.

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

Finds tables on PDF pages from how the text lines up, with or without ruled lines, and writes them as Markdown tables in place. Each table also comes as rows and CSV in the tables field. Each page with a table is one table-page event on top of the document's price. Word, Excel and PowerPoint tables are always kept, free.

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

Bigger files are skipped for free, with an error row that says so.

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

Off by default. Turn on Apify Proxy for a site that blocks cloud servers.

## Actor input object example

```json
{
  "urls": [
    "https://calibre-ebook.com/downloads/demos/demo.docx"
  ],
  "chunkSize": 0,
  "ocr": false,
  "ocrLanguages": [
    "eng"
  ],
  "pdfTables": false,
  "maxFileSizeMb": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `documents` (type: `string`):

All document rows as JSON: url, fileName, format, title, author, pages, words, markdown and chunks. A file that failed has an error instead.

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

Documents converted and failed, pages and words in all, documents per format, and why the run stopped.

# 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://calibre-ebook.com/downloads/demos/demo.docx"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nebulacolors/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://calibre-ebook.com/downloads/demos/demo.docx"] }

# Run the Actor and wait for it to finish
run = client.actor("nebulacolors/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://calibre-ebook.com/downloads/demos/demo.docx"
  ]
}' |
apify call nebulacolors/document-to-markdown --silent --output-dataset

```

## MCP server setup

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