# PDF & DOCX to Markdown and Text Extractor with OCR (`uriii/document-to-markdown`) Actor

Convert PDF, Word (DOCX), PowerPoint, Excel, CSV and scanned images to clean Markdown or plain text. PDF OCR, tables as JSON, metadata and RAG-ready chunks. Multi-column PDFs, Google Drive and Dropbox links.

- **URL**: https://apify.com/uriii/document-to-markdown.md
- **Developed by:** [Oriol Bayó Llavall](https://apify.com/uriii) (community)
- **Categories:** AI, Developer tools, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 document 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

### What does Document to Markdown do?

**Document to Markdown** converts **PDF, Word (DOCX), PowerPoint (PPTX), Excel (XLSX), CSV, TXT/Markdown and images** into clean **Markdown or plain text**, ready for LLMs, RAG pipelines, search indexes and data analysis.

Give it a list of document URLs and get back structured JSON with:

- 📝 **Full text as Markdown** – headings, lists and tables preserved
- 📊 **Tables** as Markdown tables *and* as structured rows (`tables[].rows`)
- 📰 **Correct reading order for multi-column PDFs** (papers, forms, brochures)
- 🔍 **OCR for scanned PDFs and images** (9 languages) – only pages without a text layer are OCR'd in *auto* mode
- ✂️ **RAG-ready chunks** with configurable size, overlap and page references (`pageStart` / `pageEnd`)
- 🏷️ **Metadata** – title, author, subject, creation/modification dates, sheet names
- 🔗 **Google Drive, Google Docs/Sheets/Slides and Dropbox share links** work out of the box
- 💸 **No proxies, no browser** – fast and cheap

### PDF to Markdown, DOCX to text and PDF OCR in one Actor

| You want to… | Use these settings |
|---|---|
| **Convert PDF to Markdown** (keep headings, lists and tables) | `outputFormat: "markdown"` |
| **Convert DOCX to text** (Word to plain text) | `outputFormat: "text"` |
| **OCR scanned PDFs** and images (PDF OCR) | `ocr: "auto"` (or `"always"`) and the document languages in `ocrLanguages` |
| **Extract text from PDF** page by page | `splitBy: "page"` |
| **Extract tables from PDF** or Excel as JSON | `extractTables: true` → `tables[].rows` |
| **Chunk documents for RAG** and embeddings | `splitBy: "chunk"`, `chunkSize`, `chunkOverlap` |

One input, any mix of formats: PDF, DOCX, PPTX, XLSX, CSV, TXT and images in the same run.

### Why use it?

- **Build a RAG chatbot or AI agent** over PDFs, manuals, contracts or reports
- **Feed documents to LLMs** (ChatGPT, Claude, Gemini, Llama) as compact Markdown instead of raw PDFs
- **Index documents** in vector databases such as Pinecone, Qdrant, Weaviate, Chroma or pgvector
- **Extract tables** from PDF reports and spreadsheets into JSON/CSV/Excel
- **Digitize scanned documents** with OCR
- **Automate document pipelines** with the Apify API, Make, Zapier, n8n or LangChain

### Supported formats

| Format | Extensions | Notes |
|---|---|---|
| PDF | `.pdf` | Text, headings, tables, multi-column layouts, OCR for scanned pages |
| Word | `.docx` | Headings, lists, tables, metadata |
| PowerPoint | `.pptx` | One page per slide, titles, bullet levels, tables, optional speaker notes |
| Excel | `.xlsx` | One page per sheet, each sheet as a table |
| CSV / TSV | `.csv`, `.tsv` | Delimiter auto-detected |
| Text | `.txt`, `.md` | Markdown headings preserved |
| Images | `.png`, `.jpg`, `.tiff`, `.webp`, `.gif`, `.bmp` | OCR (multi-page TIFF supported) |

Legacy binary Office formats (`.doc`, `.xls`, `.ppt`) are not supported – save them as `.docx`, `.xlsx` or `.pptx` first. Password-protected PDFs are reported as errors.

### How much does it cost?

You only pay for documents that are processed successfully – **failed downloads and unsupported files are free**.

| Event | Price |
|---|---|
| Document processed | $0.002 per document |
| Page processed | $0.0005 per page (= $0.50 per 1,000 pages) |
| OCR page | additional $0.002 per OCR'd page |

A *page* is a PDF page, a slide or a sheet. For DOCX, CSV and text files every 3,000 characters count as one page.

**Examples:** a 10-page PDF costs **$0.007**. 1,000 single-page invoices cost **$2.50**. A 20-page scanned contract with OCR costs **$0.052**.

Use **Max pages per document** to cap the cost of very large files. Apify's free plan includes monthly credits, so you can try the Actor for free.

### How to use it

1. Click **Try for free**.
2. Add document URLs in **Document URLs** (or upload a file).
3. Choose the **output format** (Markdown or plain text) and **one item per** document, page or chunk.
4. Click **Start** and download the results as JSON, CSV, Excel or HTML, or fetch them via the API.

### Input example

```json
{
    "startUrls": [
        { "url": "https://arxiv.org/pdf/1706.03762" },
        { "url": "https://drive.google.com/file/d/FILE_ID/view?usp=sharing" }
    ],
    "outputFormat": "markdown",
    "splitBy": "chunk",
    "chunkSize": 1500,
    "chunkOverlap": 150,
    "extractTables": true,
    "ocr": "auto",
    "ocrLanguages": ["eng", "spa"]
}
```

### Output example

**One item per document** (`splitBy: "document"`):

```json
{
    "url": "https://arxiv.org/pdf/1706.03762",
    "fileName": "1706.03762v7.pdf",
    "fileType": "pdf",
    "status": "success",
    "pageCount": 15,
    "processedPages": 15,
    "ocrPages": 0,
    "charCount": 38374,
    "metadata": {
        "creator": "LaTeX with hyperref",
        "producer": "pdfTeX-1.40.25",
        "createdAt": "2024-04-10T21:11:43"
    },
    "text": "## Attention Is All You Need\n\nAshish Vaswani∗ Google Brain ...\n\n### Abstract\n\nThe dominant sequence transduction models ...",
    "tables": [
        { "page": 4, "rows": [["Region", "Q1", "Q2"], ["North", "100", "120"], ["South", "80", "95"]] }
    ]
}
```

**One item per chunk** (`splitBy: "chunk"`) – ready to embed:

```json
{
    "url": "https://arxiv.org/pdf/1706.03762",
    "fileName": "1706.03762v7.pdf",
    "fileType": "pdf",
    "status": "success",
    "pageCount": 15,
    "chunkIndex": 3,
    "chunkCount": 37,
    "pageStart": 2,
    "pageEnd": 3,
    "text": "... attention mechanisms, dispensing with recurrence ..."
}
```

**Errors** never stop the run. Each failed document produces an item such as:

```json
{ "url": "https://example.com/report.doc", "status": "error", "error": "Legacy Office format (.doc/.xls/.ppt) is not supported. Save the file as .docx/.xlsx/.pptx." }
```

Very large texts (over 4 million characters) are stored in the run's key-value store and linked in `textUrl`.

### Tips

- **RAG:** use `splitBy: "chunk"` with `chunkSize` 1,000–2,000 and `chunkOverlap` 10% of the chunk size.
- **Scanned PDFs:** keep OCR on *auto* and select the document languages for better accuracy.
- **Tables in PDFs:** tables drawn with cell borders are detected best. Tables without borders are kept as text in reading order.
- **Google Drive / Dropbox:** the file must be shared as *anyone with the link can view*.

### Integrations and API

Run the Actor from your code with the [Apify API](https://docs.apify.com/api/v2), the Python or JavaScript client, or connect it to **Make, Zapier, n8n, LangChain, LlamaIndex** and other tools through Apify integrations. It also works with AI agents via the Apify MCP server.

### FAQ

**Is my data stored?** Documents are processed in memory during the run. Results are saved only in your own Apify storage, under your account's data retention settings.

**Can it read handwriting?** OCR is optimized for printed text. Handwriting accuracy is limited.

**Something not working?** Open an issue in the **Issues** tab with the document URL (if public) and we will fix it quickly.

# Actor input Schema

## `startUrls` (type: `array`):

Direct links to PDF, DOCX, PPTX, XLSX, CSV, TXT/MD or image files (PNG, JPG, TIFF, WEBP). Google Drive, Google Docs/Sheets/Slides and Dropbox share links are converted automatically (the file must be shared as 'anyone with the link'). You can also upload a file here.

## `outputFormat` (type: `string`):

Markdown keeps headings, lists and tables (best for LLMs and RAG). Plain text has no formatting; tables become tab-separated rows.

## `splitBy` (type: `string`):

Document: one item with the full text. Page: one item per page/slide/sheet. Chunk: overlapping text chunks with page references, ready for embeddings and vector databases.

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

Maximum characters per chunk. Only used when splitting by chunk. ~1,500 characters is roughly 350-400 tokens.

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

Characters repeated from the end of the previous chunk so context is not lost at the boundaries. Max half of the chunk size.

## `extractTables` (type: `boolean`):

Detect tables and output them as Markdown tables in the text and as structured rows in the 'tables' field.

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

Auto: OCR only PDF pages without a text layer (scans) and images. Always: OCR every page. Off: never OCR (images will fail). OCR pages are billed separately.

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

Languages present in scanned documents. Selecting only the languages you need is faster and more accurate.

## `includeSlideNotes` (type: `boolean`):

Append speaker notes to each slide's text.

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

Process only the first N pages/slides/sheets of each document. 0 = all pages. Useful to control cost on very large files.

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

Files larger than this are skipped with an error item.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://arxiv.org/pdf/1706.03762"
    }
  ],
  "outputFormat": "markdown",
  "splitBy": "document",
  "chunkSize": 1500,
  "chunkOverlap": 150,
  "extractTables": true,
  "ocr": "auto",
  "ocrLanguages": [
    "eng"
  ],
  "includeSlideNotes": false,
  "maxPagesPerDocument": 0,
  "maxFileSizeMb": 100
}
```

# Actor output Schema

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

No description

# 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 = {
    "startUrls": [
        {
            "url": "https://arxiv.org/pdf/1706.03762"
        }
    ]
};

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

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

```

## MCP server setup

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