# Document to Markdown - PDF, DOCX, PPTX, XLSX, OCR & RAG Chunks (`fabrikit/document-to-markdown`) Actor

Convert PDF, Word, PowerPoint, Excel/CSV, HTML, EPUB and images to clean Markdown, text and RAG chunks with page numbers and heading paths. Tables kept, OCR for scans (eng+spa default). From US$0.50 per 1,000 pages.

- **URL**: https://apify.com/fabrikit/document-to-markdown.md
- **Developed by:** [Fabrikit](https://apify.com/fabrikit) (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.50 / 1,000 page processeds

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, HTML, EPUB & images for LLMs and RAG

Convert documents into **clean Markdown, plain text and RAG-ready chunks**. Give URLs (or upload files) and get one dataset item per document with the Markdown, headings outline, tables, metadata, page numbers and, if you want, overlapping chunks that remember their page range and heading path.

| Format | What you get |
|---|---|
| **PDF** | Reading order (two-column pages handled), headings from font sizes, lists, tables, running headers/footers removed, hyphenation repaired |
| **Scanned PDF / images** (PNG, JPG, TIFF incl. multi-page, WEBP, BMP, GIF) | OCR with Tesseract (default English + Spanish, 100+ languages selectable) |
| **DOCX** | Headings, lists, tables, bold/italic/links; title and author from properties |
| **PPTX** | One section per slide (title, bullets, tables, speaker notes), slide numbers as pages |
| **XLSX / XLS / ODS / CSV** | Each sheet as a Markdown table, dates formatted |
| **HTML** | Main content only (navigation, cookie banners and ads stripped) |
| **EPUB** | Chapters in reading order with book metadata |
| **TXT / Markdown** | Passed through |

Also supported: public **Google Drive**, **Dropbox** and Google Docs / Sheets / Slides share links.

Everything runs on permissively licensed open-source libraries (pdf.js, mammoth, SheetJS CE, Tesseract.js, turndown); no AGPL code, no external API, no API key.

### Input example

```json
{
  "urls": [
    { "url": "https://example.com/annual-report.pdf" },
    { "url": "https://example.com/handbook.docx" }
  ],
  "enableChunking": true,
  "chunkSize": 1000,
  "chunkOverlap": 100,
  "ocr": "auto",
  "ocrLanguages": "eng+spa"
}
```

Useful options: `maxPagesPerDocument` (cap cost on huge files), `includePages` (Markdown per page / slide / sheet), `ocr` (`auto` = only pages without a text layer, `always`, `never`), `pdfPassword` (secret), `maxFileSizeMb`.

### Output example

```json
{
  "id": "7c1d0f4a9b21",
  "fileName": "annual-report.pdf",
  "fileType": "pdf",
  "status": "ok",
  "title": "Annual Report 2025",
  "author": "ACME Corp",
  "language": "en",
  "unit": "page",
  "pageCount": 48,
  "pagesProcessed": 48,
  "ocrPages": 0,
  "wordCount": 21873,
  "tableCount": 14,
  "markdown": "# Annual Report 2025\n\n## Letter to shareholders\n\n...\n\n| Region | Revenue | Growth |\n| --- | --- | --- |\n| North | 1,200,000 | 8% |",
  "headings": [{ "level": 1, "text": "Annual Report 2025", "page": 1 }],
  "chunks": [{ "index": 3, "text": "...", "charCount": 934, "pageStart": 2, "pageEnd": 3, "headingPath": ["Annual Report 2025", "Letter to shareholders"] }],
  "metadata": { "creator": "InDesign", "createdAt": "2026-01-15T10:00:00Z" }
}
```

Failed documents appear with `status: "failed"` and an `error`, and are **not charged**. The run's `SUMMARY` record lists pages processed, OCR pages and errors.

### Pricing

| Event | Price | When |
|---|---|---|
| `page-processed` | US$0.50 per 1,000 pages | every PDF page, slide, image; about 3,000 characters for flowing formats (DOCX, HTML, EPUB, TXT, spreadsheets) |
| `ocr-page` | US$10 per 1,000 pages | extra, only for pages read with OCR (scans, images) |

A 40-page text PDF costs US$0.02. A 40-page scan costs US$0.42. Use `maxPagesPerDocument` and the run's maximum-cost setting to cap spending; the Actor stops cleanly at the limit.

### FAQ

**How good is table extraction?** Tables in text PDFs are detected from column alignment and converted to Markdown tables; simple grids are reliable, merged-cell or borderless layouts are best effort. DOCX, PPTX, HTML and spreadsheet tables are exact.

**Is my file stored?** No. Downloads are deleted after conversion; only the result is saved in your run.

**Scanned document with columns or stamps?** OCR returns text per paragraph; very noisy scans reduce accuracy. Choose the right `ocrLanguages` for best results.

**Can it read old .doc / .ppt?** Not directly; save them as .docx / .pptx or PDF.

**Why use chunks from here?** Each chunk carries `pageStart`, `pageEnd` and `headingPath`, so retrieval results can cite the page and section.

### Use cases

RAG ingestion, LLM context preparation, document search, contract and report analysis, archive digitisation, data extraction from public filings, knowledge-base migration.

### En español

Convierte PDF, Word, PowerPoint, Excel/CSV, HTML, EPUB e imágenes en **Markdown limpio, texto plano y chunks para RAG** (con número de página y ruta de títulos). Los PDF escaneados y las imágenes se leen con OCR (inglés y español por defecto, más de 100 idiomas). Mantiene tablas, quita encabezados y pies repetidos y soporta enlaces de Google Drive y Dropbox. Precio: US$0,50 por 1.000 páginas, más US$10 por 1.000 páginas con OCR. No necesita API key; los archivos se borran al terminar.

# Actor input Schema

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

Direct links to PDF, DOCX, PPTX, XLSX/XLS/ODS, CSV, HTML, EPUB, TXT/MD or image (PNG, JPG, TIFF, WEBP, BMP, GIF) files. Google Drive and Dropbox share links and Google Docs/Sheets/Slides links (public) are supported.

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

Optional: upload documents from your computer (stored in a key-value store of your account and passed to the Actor as URLs).

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

Auto: OCR only pages without a text layer (scanned PDFs) and images. Always: OCR every PDF page. Never: skip OCR (scanned pages come out empty). OCR pages are billed with the extra OCR event.

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

Tesseract language codes joined with +, for example eng+spa (default), deu, fra, por, ita, chi_sim, jpn, rus. Up to 5. English and Spanish are built in; others are downloaded on first use.

## `enableChunking` (type: `boolean`):

Add a `chunks` array: overlapping text pieces with pageStart/pageEnd and the heading path, ready for a vector database.

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

Target maximum size of a chunk. Blocks (paragraphs, tables) are kept whole when they fit.

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

Characters repeated from the end of the previous chunk. Must be smaller than the chunk size.

## `includeText` (type: `boolean`):

Add a `text` field with the document as plain text (no Markdown syntax).

## `includePages` (type: `boolean`):

Add a `pages` array with the Markdown of every page / slide / sheet / chapter separately.

## `maxItems` (type: `integer`):

Maximum number of documents to convert in this run.

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

Convert only the first N pages of each PDF / image. 0 converts everything (up to 5,000 pages).

## `maxRowsPerSheet` (type: `integer`):

Large sheets are cut after this many rows.

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

Larger downloads are skipped.

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

Used to open password-protected PDFs. Stored encrypted, never logged.

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

Only used to download your files. The Actor downloads directly first and escalates to Apify datacenter and then residential proxies only if the host blocks it.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://raw.githubusercontent.com/mozilla/pdf.js/master/web/compressed.tracemonkey-pldi-09.pdf"
    }
  ],
  "ocr": "auto",
  "ocrLanguages": "eng+spa",
  "enableChunking": false,
  "chunkSize": 1000,
  "chunkOverlap": 100,
  "includeText": true,
  "includePages": false,
  "maxItems": 1,
  "maxPagesPerDocument": 3,
  "maxRowsPerSheet": 10000,
  "maxFileSizeMb": 200,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One item per converted document

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

Pages processed, OCR pages, failures

# 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": [
        {
            "url": "https://raw.githubusercontent.com/mozilla/pdf.js/master/web/compressed.tracemonkey-pldi-09.pdf"
        }
    ],
    "ocr": "auto",
    "maxItems": 1,
    "maxPagesPerDocument": 3,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fabrikit/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": [{ "url": "https://raw.githubusercontent.com/mozilla/pdf.js/master/web/compressed.tracemonkey-pldi-09.pdf" }],
    "ocr": "auto",
    "maxItems": 1,
    "maxPagesPerDocument": 3,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("fabrikit/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": [
    {
      "url": "https://raw.githubusercontent.com/mozilla/pdf.js/master/web/compressed.tracemonkey-pldi-09.pdf"
    }
  ],
  "ocr": "auto",
  "maxItems": 1,
  "maxPagesPerDocument": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call fabrikit/document-to-markdown --silent --output-dataset

```

## MCP server setup

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