# PDF to Markdown & Text Extractor with OCR (`rod_analytics/doc-to-markdown`) Actor

PDF to Markdown and text extractor with OCR. Convert PDF, Word, Excel, PowerPoint, HTML and scanned documents to clean Markdown with metadata and RAG ready chunks for LLMs and AI agents.

- **URL**: https://apify.com/rod_analytics/doc-to-markdown.md
- **Developed by:** [Rod Services](https://apify.com/rod_analytics) (community)
- **Categories:** AI, Developer tools
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 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.
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

**PDF to Markdown and text extractor with OCR: also Word, Excel, PowerPoint, HTML and scanned images.**
Paste a file link, get clean Markdown, page-aware RAG chunks and metadata for LLMs. $3 per 1,000 documents, failed files are free.

### What you get

For every document:

- Clean **Markdown** that keeps headings, lists and tables, without running headers and page numbers.
- **OCR on scanned pages**, automatic, Tesseract in 35 languages. Only pages without a text layer are OCRed.
- **RAG ready chunks** with page numbers and section headings, sized for your embedding model.
- **Metadata**: title, author, dates, page count, PDF version, HTML meta tags.
- Optional **page range** for PDFs, for example `1-5, 10, 20-`, and optional extracted images.

Supported formats: PDF, DOCX, XLSX, XLS, PPTX, HTML, EPUB, CSV, JSON, XML, TXT, Markdown, Outlook MSG, PNG, JPG, TIFF, WEBP, BMP and GIF. Google Drive, Google Docs, Sheets and Slides, Dropbox and GitHub share links are turned into direct downloads. Everything runs inside your Apify run. No third party AI service sees your files.

Typical uses:

- **LLM ingestion and RAG.** Turn PDF libraries into chunks for pgvector, Pinecone, Qdrant, Weaviate or Chroma.
- **AI agents and MCP tools.** One call that reads any PDF, Word file or web page as Markdown.
- **Scanned PDFs and images.** Contracts, invoices and old reports become searchable text.
- **Tables.** PDF tables with ruled lines, Word tables and Excel sheets become Markdown tables.

### Quick start

1. Open the **Input** tab.
2. Add links in **File URLs**, or upload a file with **Upload a file**.
3. Keep **OCR** on `auto` unless the files are all scans.
4. Click **Start**. The example PDF converts in about 5 seconds. Results are in the **Output** tab.

From code, one HTTP call runs the Actor and returns the rows:

```bash
curl -X POST "https://api.apify.com/v2/acts/rod_analytics~doc-to-markdown/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"fileUrls": ["https://www.govinfo.gov/content/pkg/USCODE-2011-title17/pdf/USCODE-2011-title17-chap1-sec107.pdf"], "chunkSize": 800}'
```

With LangChain, load the chunks as documents:

```python
from apify_client import ApifyClient
from langchain_core.documents import Document

client = ApifyClient("APIFY_TOKEN")
run = client.actor("rod_analytics/doc-to-markdown").call(
    run_input={"fileUrls": ["https://example.com/report.pdf"], "chunkSize": 800}
)
docs = [
    Document(page_content=c["text"], metadata={"source": item["sourceUrl"], "page": c["page"]})
    for item in client.dataset(run["defaultDatasetId"]).iterate_items()
    for c in item["chunks"]
]
```

### Input example

```json
{
  "fileUrls": [
    "https://www.govinfo.gov/content/pkg/USCODE-2011-title17/pdf/USCODE-2011-title17-chap1-sec107.pdf",
    "https://example.com/report.docx"
  ],
  "ocr": "auto",
  "languages": ["eng", "deu"],
  "chunkSize": 800,
  "chunkOverlap": 80,
  "pages": "1-3"
}
```

| Field                  | Default   | What it does                                                                                     |
| ---------------------- | --------- | ------------------------------------------------------------------------------------------------ |
| `fileUrls`             |           | Direct links to documents. Apify key-value store record URLs work too.                           |
| `uploadedFile`         |           | One file uploaded from your computer in Console.                                                 |
| `keyValueStoreRecords` | `[]`      | Record keys in this run's key-value store, or `storeId/key`. Useful when another Actor calls it. |
| `ocr`                  | `auto`    | `auto` OCRs only scanned pages. `force` OCRs every page. `off` never OCRs.                       |
| `languages`            | `["eng"]` | Tesseract codes, for example `eng`, `deu`, `fra`, `spa`, `lit`, `pol`, `rus`, `jpn`, `chi_sim`.  |
| `chunkSize`            | `1000`    | Approximate tokens per chunk. `0` turns chunking off.                                            |
| `chunkOverlap`         | `100`     | Approximate tokens repeated between chunks.                                                      |
| `includeImages`        | `false`   | Save embedded images to the key-value store and link them in the Markdown.                       |
| `maxPages`             | `0`       | Only the first N pages or slides. `0` is all, but text PDFs stop at 300 pages.                   |
| `pages`                | empty     | PDF page range such as `1-5, 10, 20-`. Empty means all pages.                                    |
| `saveMarkdownFiles`    | `false`   | Also save each document as a `.md` file.                                                         |
| `maxFileSizeMb`        | `100`     | Skip bigger downloads.                                                                           |
| `maxConcurrency`       | `2`       | Documents converted in parallel.                                                                 |

### Output sample

Real result of the prefilled example, shortened. Download the dataset as JSON, CSV, Excel or HTML.

```json
{
  "sourceUrl": "https://www.govinfo.gov/content/pkg/USCODE-2011-title17/pdf/USCODE-2011-title17-chap1-sec107.pdf",
  "fileName": "USCODE-2011-title17-chap1-sec107.pdf",
  "mimeType": "application/pdf",
  "pages": 5,
  "pagesProcessed": 5,
  "title": "§ 107. Limitations on exclusive rights: Fair use",
  "markdown": "by two or more authors, a waiver of rights under this paragraph made by one such author waives such rights for all such authors.\n\n(2) Ownership of the rights conferred by subsection (a)...",
  "markdownLength": 36292,
  "chunks": [
    {
      "index": 0,
      "text": "by two or more authors, a waiver of rights under this paragraph...",
      "tokensApprox": 845,
      "page": 1,
      "heading": null
    }
  ],
  "chunksCount": 13,
  "metadata": {
    "creator": "Federal Digital System, U. S. Government Publishing Office",
    "createdAt": "2019-10-14T08:51:38+00:00",
    "pdfVersion": "1.5"
  },
  "ocrPagesCount": 0,
  "warnings": [],
  "error": null
}
```

| Field                     | Description                                                                   |
| ------------------------- | ----------------------------------------------------------------------------- |
| `fileName`, `mimeType`    | File name from the URL or headers, type checked against the content           |
| `pages`, `pagesProcessed` | Pages for PDF, slides for PPTX, frames for images, and how many were read     |
| `title`                   | Document title from metadata or the first heading                             |
| `markdown`, `markdownUrl` | Clean Markdown, and the full `.md` file for documents over 200,000 characters |
| `chunks`                  | `index`, `text`, `tokensApprox`, `page`, `heading`                            |
| `metadata`                | Author, subject, keywords, created and modified dates, word count, HTML meta  |
| `ocrPagesCount`           | Pages that went through OCR                                                   |
| `warnings`, `error`       | Notes such as skipped pages, and why a document failed. Failures are free     |

The Output tab has three views: **Overview**, **Markdown** and **RAG chunks** with one row per chunk.

### Pricing

Pay per event. You pay only for converted documents and OCR pages. Failed downloads, broken files and password protected PDFs are free.

| Event                        | Free tier | Bronze  | Silver  | Gold    |
| ---------------------------- | --------- | ------- | ------- | ------- |
| Document converted           | $0.003    | $0.0027 | $0.0024 | $0.0021 |
| OCR page, only scanned pages | $0.01     | $0.009  | $0.008  | $0.007  |
| Run start, per GB of memory  | $0.001    | $0.001  | $0.001  | $0.001  |

- 1,000 text PDFs cost $3 on the free tier and $2.10 on Gold.
- A 100 page scanned book costs about $1.
- Your discount tier follows your Apify subscription plan.
- Set **Maximum cost per run** in Run options. The Actor never goes over it. Documents that do not fit are skipped, and scanned pages that do not fit are left out with a warning.

### FAQ

**Does it work with scanned PDFs?** Yes. Pages without a text layer are rendered and read with Tesseract OCR. Handwriting and very low quality scans give weak results.

**Can I convert only some pages?** Yes. Set **PDF page range**, for example `1-5, 10, 20-`. Only those pages are read and OCRed. For slides and TIFF frames use **Max pages**.

**Are password protected PDFs supported?** No. They return an error and are not charged. PDFs that only restrict printing or copying open normally.

**What if a link opens a login or error page?** The document fails with a clear error and is not charged. This happens with private Drive or Dropbox files and expired links. Share files with "Anyone with the link".

**Is there a page limit?** Text PDFs over 300 pages stop at page 300 when `maxPages` is `0`, with a warning. Set `maxPages` to the page count you need, for example `1000`. Scanned PDFs have no such limit, since you pay per OCR page.

**How accurate are headings and tables?** Headings come from font sizes, tables from ruled lines. Complex forms and borderless tables can come out as plain text.

**Tips.** Keep OCR on `auto`. List only the OCR languages you need. For big OCR jobs give the run more memory: 4 GB is about 2.7 times faster, 1 GB is the cheapest per page.

**Is it legal?** You must have the right to process the documents you submit.

Found a bug or need a feature? Open an issue in the **Issues** tab.

### Related Actors

| Actor                                                                                                               | What it does                                          |
| ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| [Website Content Crawler to Markdown for RAG & LLMs](https://apify.com/rod_analytics/website-to-markdown)           | Crawl a site and get clean Markdown for RAG and LLMs  |
| [SEO Audit Tool: Site Crawler, Broken Links & Score](https://apify.com/rod_analytics/seo-site-audit)                | Crawl a site for broken links and SEO issues          |
| [PageSpeed Insights & Lighthouse Bulk Checker](https://apify.com/rod_analytics/lighthouse-audit)                    | Core Web Vitals and Lighthouse scores for many URLs   |
| [Tech Stack Detector: Wappalyzer & BuiltWith Alternative](https://apify.com/rod_analytics/tech-stack-detector)      | See which CMS, analytics and frameworks a site uses   |
| [Bulk Domain Lookup: DNS, WHOIS, SPF, DMARC & SSL](https://apify.com/rod_analytics/domain-security-audit)           | DNS, mail security and SSL checks for domain lists    |
| [Speech to Text: Audio, Video & Podcast Transcriber](https://apify.com/rod_analytics/media-transcriber)             | Transcribe audio, video and podcasts                  |
| [Email Validator & Verifier: Bulk MX & Disposable Check](https://apify.com/rod_analytics/bulk-email-verifier)       | Clean email lists before you send                     |
| [Website Contact Scraper: Emails, Phones & Social Links](https://apify.com/rod_analytics/company-contact-extractor) | Emails, phones and social links from company websites |
| [VIES VAT Checker & EU Company Registry Lookup](https://apify.com/rod_analytics/eu-company-lookup)                  | Validate EU VAT numbers and look up companies         |
| [Companies House Scraper: UK Company Search & Officers](https://apify.com/rod_analytics/uk-companies-house)         | UK company profiles, officers and filings             |
| [EU TED Tenders Scraper & Alerts: Public Procurement](https://apify.com/rod_analytics/ted-tenders)                  | EU public tenders with daily alerts                   |
| [Greenhouse, Lever & Ashby Job Scraper: 7 ATS in One Feed](https://apify.com/rod_analytics/multi-ats-jobs-api)      | Jobs from 7 ATS platforms in one feed                 |
| [GitHub Scraper: Repo Stats, Stars, Trending & Search](https://apify.com/rod_analytics/github-repo-stats)           | Stars, activity and trending GitHub repos             |

# Changelog

This Actor's version history is a separate document: https://apify.com/rod_analytics/doc-to-markdown/changelog.md

# Actor input Schema

## `fileUrls` (type: `array`):

Direct links to documents: PDF, DOCX, XLSX, XLS, PPTX, HTML, EPUB, CSV, JSON, XML, TXT, images (PNG, JPG, TIFF, WEBP). Google Drive, Google Docs/Sheets/Slides, Dropbox and GitHub share links are converted to direct downloads. Shared files must be open to anyone with the link. Apify key-value store record URLs work when they are signed (recordPublicUrl).

## `uploadedFile` (type: `string`):

Upload one document from your computer. Apify stores it in a key-value store and passes its URL to the Actor. For many files, use File URLs.

## `keyValueStoreRecords` (type: `array`):

Keys of records that hold files in this run's default key-value store, for example when another Actor calls this one. Use `storeId/key` or `username~store-name/key` for other stores. The Actor runs with limited permissions, so for your own stores prefer the record URL with its signature in File URLs.

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

`auto` runs OCR only on scanned pages that have no text layer. `force` runs OCR on every page. `off` never runs OCR. Each OCR page is billed as an extra event.

## `languages` (type: `array`):

Tesseract language codes for OCR, for example `eng`, `deu`, `fra`, `spa`, `lit`. Several languages slow OCR down. Installed: eng, deu, fra, spa, ita, por, nld, pol, ces, slk, slv, hrv, hun, ron, bul, ell, lit, lav, est, fin, swe, dan, nor, rus, ukr, tur, ara, heb, hin, jpn, kor, chi_sim, chi_tra, vie, ind.

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

Approximate chunk size for RAG in tokens (1 token is about 4 characters). Chunks respect headings, paragraphs and tables. Set 0 to skip chunking.

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

Approximate overlap between neighbouring chunks in tokens. Capped at half of the chunk size.

## `includeImages` (type: `boolean`):

Extract embedded images from PDF, DOCX and PPTX into the key-value store and link them from the Markdown. HTML image links are kept as absolute URLs. Off by default to keep the text clean for LLMs.

## `maxPages` (type: `integer`):

Process only the first N pages of each PDF, slides of each PPTX or frames of each TIFF. 0 means all pages, except that text PDFs stop at 300 pages. Set a higher number to convert longer text PDFs.

## `pages` (type: `string`):

Convert only these PDF pages, for example `1-5, 10, 20-` (20- means page 20 to the end). Empty means all pages. Pages that do not exist are ignored. Only applies to PDFs. Use Max pages for slides and TIFF frames.

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

Also store every document as a .md file in the key-value store. Documents longer than 200,000 characters are always stored there, and the dataset keeps a truncated copy.

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

Skip downloads larger than this.

## `maxConcurrency` (type: `integer`):

How many documents are converted in parallel. OCR is CPU heavy, so give the run more memory when you raise this.

## Actor input object example

```json
{
  "fileUrls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ],
  "keyValueStoreRecords": [],
  "ocr": "auto",
  "languages": [
    "eng"
  ],
  "chunkSize": 1000,
  "chunkOverlap": 100,
  "includeImages": false,
  "maxPages": 0,
  "saveMarkdownFiles": false,
  "maxFileSizeMb": 100,
  "maxConcurrency": 2
}
```

# Actor output Schema

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

No description

## `overview` (type: `string`):

No description

## `chunks` (type: `string`):

No description

## `files` (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 = {
    "fileUrls": [
        "https://www.govinfo.gov/content/pkg/USCODE-2011-title17/pdf/USCODE-2011-title17-chap1-sec107.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("rod_analytics/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 = { "fileUrls": ["https://www.govinfo.gov/content/pkg/USCODE-2011-title17/pdf/USCODE-2011-title17-chap1-sec107.pdf"] }

# Run the Actor and wait for it to finish
run = client.actor("rod_analytics/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 '{
  "fileUrls": [
    "https://www.govinfo.gov/content/pkg/USCODE-2011-title17/pdf/USCODE-2011-title17-chap1-sec107.pdf"
  ]
}' |
apify call rod_analytics/doc-to-markdown --silent --output-dataset

```

## MCP server setup

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