# PDF to Markdown & Text Converter: Word, Excel, PPT, HTML, RAG (`gmyri/doc-to-markdown`) Actor

Convert PDF, Word (DOCX), PowerPoint (PPTX), Excel (XLSX), CSV and HTML to clean Markdown and text, plus RAG-ready chunks for LLMs and AI agents. $3 per 1,000 documents.

- **URL**: https://apify.com/gmyri/doc-to-markdown.md
- **Developed by:** [Giorgos Myrianthous](https://apify.com/gmyri) (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 $3.00 / 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.

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 Converter: Word, Excel, PPT, HTML, RAG

Convert **PDF to Markdown** (and Word, PowerPoint, Excel, CSV, HTML to Markdown/text) in bulk, with heading-aware **RAG chunks** for LLMs, vector databases and AI agents.

💲 **$3 per 1,000 documents** (10 pages included per document, then $0.30 per 1,000 extra pages). Failed files are free. Pay only for results. No subscription, and the Apify free plan's monthly credit covers small jobs.

**Also known as:** PDF to Markdown, PDF to text, PDF parser, Word to Markdown, DOCX to Markdown, PPTX to Markdown, Excel to Markdown, HTML to Markdown, document parser for RAG, MarkItDown alternative.

### Quick start

1. Click **Start**. The prefilled input works as is.
2. Or paste this input (JSON tab):

```json
{
  "documentUrls": [
    {
      "url": "https://arxiv.org/pdf/1706.03762"
    }
  ],
  "chunksAsSeparateItems": true
}
```

**Output** (one dataset item):

```json
{
  "url": "https://arxiv.org/pdf/1706.03762",
  "status": "ok",
  "format": "pdf",
  "title": "Attention Is All You Need",
  "pages": 15,
  "wordCount": 6012,
  "markdownUrl": "https://api.apify.com/v2/key-value-stores/…/records/1706.03762v7.md"
}
```

### Use it from the API, AI agents (MCP), n8n, Make or Zapier

```bash
curl -X POST "https://api.apify.com/v2/acts/gmyri~doc-to-markdown/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"documentUrls":[{"url":"https://arxiv.org/pdf/1706.03762"}]}'
```

- **AI agents / MCP:** connect `https://mcp.apify.com/?actors=gmyri/doc-to-markdown` to Claude, Cursor, ChatGPT or any MCP client. The input schema is fully described for tool calling.
- **Python:** `ApifyClient(token).actor("gmyri/doc-to-markdown").call(run_input={...})`
- **n8n / Make / Zapier:** use the Apify "Run Actor" action with `gmyri/doc-to-markdown`, then read the dataset items.

### Quick FAQ

- **Does it OCR scanned PDFs?** No. This Actor reads the text layer. For scanned PDFs and images use [OCR Image to Text & Scanned PDF](https://apify.com/gmyri/image-pdf-ocr).
- **Can I convert private files?** Yes, if you pass a signed or temporary URL (S3, GCS, Dropbox, Google Drive direct download, Apify key-value store).
- **What if I set a spending limit?** The Actor stops cleanly at your *Max cost per run* and you're never charged above it.

### Details

Turn **PDF, DOCX, PPTX, XLSX, CSV and HTML** files into **clean Markdown** and **heading-aware chunks** ready for embeddings, vector databases and LLM prompts. One Actor for every office format, priced per document, with no subscription. It works well as a tool for AI agents through the Apify MCP server.

### What it does

- **One input, many formats.** Paste file URLs. The Actor detects the format from the file's magic bytes, then falls back to Content-Type and file extension.
- **Structure-preserving Markdown:**
  - Word headings become `#`/`##`. Bold and italic, bullet lists and tables are kept.
  - PowerPoint becomes one `## Slide N: Title` section per slide, with bullet levels, tables and speaker notes.
  - Excel and CSV become one Markdown table per sheet.
  - HTML is stripped of navigation, scripts and footers.
  - PDF text is re-flowed into paragraphs. Hyphenation and bullets are fixed, and page markers are kept (`<!-- page 3 -->`).
- **RAG chunks out of the box.** Sections are split on headings, then on paragraphs and sentences, with configurable size and overlap. Every chunk carries its `headingPath` (for example `Methods > Training Data`) for better retrieval.
- **Outputs:** a dataset row per document (metadata, Markdown, chunks), an optional dataset row per chunk, and a `.md` file per document in the key-value store.
- **Fair billing.** Failed downloads and corrupt files are **never charged**. A per-page cap (`maxPagesPerDocument`) keeps the cost of huge PDFs predictable.

### Supported formats

| Format | Extensions | Notes |
|---|---|---|
| PDF | `.pdf` | Text-layer PDFs (poppler). Scanned PDFs return a warning; OCR is not included. |
| Word | `.docx` | Headings, lists, bold/italic, tables, core properties |
| PowerPoint | `.pptx` | Slides, bullet levels, tables, speaker notes |
| Excel | `.xlsx`, `.xlsm` | Every sheet becomes a table (formulas are read as their cached values) |
| CSV / TSV | `.csv`, `.tsv` | Delimiter auto-detected (`,` `;` tab `|`) |
| HTML | `.html`, `.htm` | Main/article content only |
| Text | `.md`, `.txt`, `.json` | Passed through (JSON is pretty-printed in a code block) |

Legacy binary formats (`.doc`, `.ppt`, `.xls`) are not supported. Save them as the modern format first.

### Input

```json
{
  "documentUrls": [
    { "url": "https://arxiv.org/pdf/1706.03762" },
    { "url": "https://example.com/files/q3-report.docx" }
  ],
  "includeChunks": true,
  "chunkSize": 2000,
  "chunkOverlap": 200,
  "chunksAsSeparateItems": false,
  "maxPagesPerDocument": 0
}
```

| Field | Default | Description |
|---|---|---|
| `documentUrls` | – | Public file URLs, or Apify key-value-store record URLs from your account |
| `includeChunks` | `true` | Produce RAG chunks |
| `chunkSize` / `chunkOverlap` | 2000 / 200 | Target characters per chunk and overlap between chunks |
| `chunksAsSeparateItems` | `false` | One dataset row per chunk (best for vector-DB integrations) |
| `includeMarkdownInDataset` | `true` | Include the full Markdown in the dataset row |
| `saveMarkdownFiles` | `true` | Save `<name>-<hash>.md` to the key-value store |
| `maxPagesPerDocument` | `0` (all) | Convert only the first N PDF pages |
| `maxRowsPerTable` | 500 | Truncate large sheets and tables |
| `includeSpeakerNotes` | `true` | Include PowerPoint notes |
| `maxFileSizeMb` | 50 | Skip larger files (not charged) |

### Output

One dataset item per document:

```json
{
  "url": "https://arxiv.org/pdf/1706.03762",
  "status": "ok",
  "fileName": "1706.03762v7.pdf",
  "format": "pdf",
  "title": "Attention Is All You Need",
  "pages": 15,
  "wordCount": 6482,
  "charCount": 40306,
  "metadata": { "Creator": "LaTeX with hyperref", "Pages": "15" },
  "warnings": [],
  "markdownKey": "1706.03762v7-dd93e904.md",
  "markdown": "## Attention Is All You Need\n\n...",
  "chunkCount": 47,
  "chunks": [{ "index": 2, "headingPath": "Abstract", "text": "## Abstract\n\nThe dominant ...", "charCount": 1987 }]
}
```

Failed files get `"status": "error"` and an `error` message, and are free.

### Pricing (pay per event)

| Event | Price | When |
|---|---|---|
| `document-converted` | **$0.003** | Each successfully converted file. Includes up to 10 pages, slides or sheets, plus chunking. |
| `extra-page` | $0.0003 | Each PDF page, slide or sheet beyond the first 10 |
| `apify-actor-start` | $0.00005 | Platform default, once per run |

Examples: 1,000 Word documents cost **$3.00**. A 15-page PDF costs $0.003 + 5 × $0.0003 = **$0.0045**. You can set a maximum cost per run, and the Actor stops cleanly when the limit is reached.

### Use with AI agents (MCP)

Add the Actor to the Apify MCP server, and your agent (Claude, Cursor, ChatGPT and others) can call it as a tool:

```
https://mcp.apify.com/?tools=<your-username>/doc-to-markdown
```

The Actor uses pay-per-event pricing only, runs with limited permissions and does not use Standby mode, so it is eligible for Apify agentic payments (an agent can pay per run without an Apify account).

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/<your-username>~doc-to-markdown/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"documentUrls":[{"url":"https://arxiv.org/pdf/1706.03762"}],"chunksAsSeparateItems":true}'
```

### Limitations

- OCR is not included. Scanned or image-only PDFs produce a warning and little text.
- PDF headings are detected heuristically (pdftotext gives no font sizes). Tables inside PDFs come out as text, not Markdown tables.
- Only process documents you have the right to use. The Actor downloads only the URLs you give it and does not crawl.

### Changelog

- 0.1: First release. PDF, DOCX, PPTX, XLSX, CSV/TSV, HTML, TXT/MD/JSON. RAG chunking. Pay-per-event pricing.

### Related tools

- [OCR Image to Text & Scanned PDF to Text (Tesseract, 100+ langs)](https://apify.com/gmyri/image-pdf-ocr)
- [Website Screenshot API: Full Page, Mobile & URL to PDF](https://apify.com/gmyri/website-screenshot)
- [Merge & Deduplicate Datasets: Remove Duplicates, Only New Items](https://apify.com/gmyri/dataset-merge-dedup)

# Actor input Schema

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

Direct links to the files you want converted: PDF, DOCX, PPTX, XLSX/XLSM, CSV/TSV, HTML, Markdown, TXT or JSON. Apify key-value store record URLs from your own account work too.

## `includeChunks` (type: `boolean`):

Split the Markdown into heading-aware chunks ready for embedding.

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

Target maximum characters per chunk.

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

Characters repeated between consecutive chunks of the same section.

## `chunksAsSeparateItems` (type: `boolean`):

If enabled, every chunk becomes its own dataset row (handy for vector-DB integrations). Otherwise chunks are nested in the document row.

## `includeMarkdownInDataset` (type: `boolean`):

Put the full Markdown into the dataset item (up to 2M characters).

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

Also store each document as a .md file in the run's key-value store.

## `maxPagesPerDocument` (type: `integer`):

Convert only the first N pages of each PDF (0 = all pages). Caps cost on very long PDFs.

## `maxRowsPerTable` (type: `integer`):

Spreadsheet and table rows beyond this are truncated.

## `includeSpeakerNotes` (type: `boolean`):

Append slide notes under each slide.

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

Files larger than this are skipped (and not charged).

## `requestTimeoutSecs` (type: `integer`):

Timeout for downloading each file.

## Actor input object example

```json
{
  "documentUrls": [
    {
      "url": "https://arxiv.org/pdf/1706.03762"
    }
  ],
  "includeChunks": true,
  "chunkSize": 2000,
  "chunkOverlap": 200,
  "chunksAsSeparateItems": false,
  "includeMarkdownInDataset": true,
  "saveMarkdownFiles": true,
  "maxPagesPerDocument": 0,
  "maxRowsPerTable": 500,
  "includeSpeakerNotes": true,
  "maxFileSizeMb": 50,
  "requestTimeoutSecs": 60
}
```

# Actor output Schema

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

One item per document (status, title, pages, link to the full Markdown) plus one item per RAG chunk.

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

Full Markdown files (.md) in the run's key-value store, plus the OUTPUT run summary.

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

OUTPUT record: converted/failed/requested counts.

# 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://arxiv.org/pdf/1706.03762"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gmyri/doc-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://arxiv.org/pdf/1706.03762" }] }

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

```

## MCP server setup

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