# PDF to Markdown Extractor & Document Parser: Tables, JSON, OCR (`fguiraud/document-to-markdown-tables`) Actor

Extract text and tables from PDF, Word, Excel, PowerPoint, HTML and scanned images. Returns LLM-ready Markdown, tables as JSON/CSV, RAG chunks and metadata. OCR for scanned PDFs, optional AI field extraction (invoices, forms). No API key; pay per document.

- **URL**: https://apify.com/fguiraud/document-to-markdown-tables.md
- **Developed by:** [Fernando Guiraud](https://apify.com/fguiraud) (community)
- **Categories:** AI, Developer tools, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 document processeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 PDF to Markdown Extractor & Document Parser do?

**Document to Markdown, Tables & JSON** converts **PDF, Word (DOCX), Excel (XLSX), PowerPoint (PPTX), HTML pages, legacy Excel (XLS), CSV, TXT and images** into clean, **LLM-ready Markdown**, **structured tables (JSON + CSV)**, plain text, metadata and **RAG chunks**. Scanned PDFs and images are read with **OCR** in 7 languages.

Use it as a **PDF extractor**, **PDF table extractor** (PDF to Excel/CSV/JSON), **document parser for RAG**, **DOCX/PPTX to Markdown converter** or **OCR API**, all in one Actor.

Give it a list of document URLs and get one structured JSON record per document. No API key and no setup. You **pay only for documents that are processed successfully**.

It runs on the Apify platform, so you also get an API, scheduling, integrations (Make, Zapier, n8n, LangChain, LlamaIndex), webhooks, monitoring, and access for AI agents through the [Apify MCP server](https://mcp.apify.com).

### Why use it?

- 🤖 **RAG and AI agents**: turn documents into Markdown with preserved headings, lists and tables, pre-split into chunks with page numbers.
- 📊 **Table extraction**: every table in a PDF, Word, PowerPoint or Excel file comes back as `header` + `rows` + ready-to-use `csv`, in its original position in the Markdown. This includes **academic-paper tables drawn with horizontal rules only** (LaTeX booktabs), which most PDF tools return as jumbled text. **Tables that continue over several pages** (with the header repeated on each page) are merged into one table.
- 🧾 **Scanned documents**: OCR runs **only on pages that need it** (auto mode), so native PDFs stay fast and cheap.
- 📰 **Clean reading order**: **two-column layouts** (papers, journals, forms) are read column by column, and **running headers, footers and page numbers are removed**, so RAG chunks are not polluted.
- 🧠 **AI field extraction (optional)**: ask for specific fields (invoice number, total, dates, parties, line items…) and get them as clean JSON matching your schema, powered by Claude with **your own Anthropic API key**.
- 🔗 **Paste any link**: Google Drive, Google Docs/Sheets/Slides, Dropbox, OneDrive/SharePoint and GitHub share links are converted to downloads automatically.
- 🕸️ **Whole document libraries**: point it at a web page (reports, filings, publications) and enable `followDocumentLinks` to convert every PDF/Office file linked from it.
- ✍️ **Faithful Markdown**: bullet and numbered lists and bold sub-headings in PDFs; bold, italic, links and nested lists in Word; **chart data from PowerPoint** as tables; OCR text re-flowed into real paragraphs.
- 🗂️ **One tool, many formats**: the file type is detected from the content, not the URL, so links without extensions work.
- 🛡️ **Predictable cost**: failed downloads and unsupported files are **never billed**, and the run stops cleanly when your spending limit is reached.

### How to convert a PDF to Markdown and tables

1. Click **Try for free**.
2. Paste one or more document URLs into **Document URLs**.
3. Choose what to return: Markdown, tables, text, per-page content and/or RAG chunks.
4. Click **Start** and download the results as JSON, CSV, Excel or HTML, or fetch them via API.

### Input

| Field | Description | Default |
|---|---|---|
| `sources` | Document URLs (PDF, DOCX, XLSX, XLS, PPTX, HTML, CSV, TXT, PNG, JPG, TIFF, BMP, GIF, WEBP) | — |
| `base64Files` | Files without a URL: `[{"fileName": "a.pdf", "content": "<base64>"}]` (ideal for AI agents) | — |
| `outputs` | Any of `markdown`, `tables`, `text`, `pages`, `chunks` | `markdown`, `tables` |
| `followDocumentLinks` / `maxLinkedDocuments` | Convert the documents linked from a web page instead of the page itself | `false` / 20 |
| `saveFiles` | Also save each document as a downloadable `.md` file, each table as a `.csv` file and all tables as an Excel workbook (`.xlsx`, one sheet per table); links in `files` | `false` |
| `pdfPassword` | Password for encrypted PDFs (stored as a secret) | — |
| `maxCharactersPerDocument` | Truncate text for AI agents with small context windows (0 = no limit) | 0 |
| `removeHeadersFooters` | Drop lines repeated at the top/bottom of most pages | `true` |
| `tableDetection` | `lines` (ruled tables, precise) or `text` (also borderless tables, experimental) | `lines` |
| `ocr` | `auto` (only scanned pages), `always`, `never` | `auto` |
| `ocrLanguages` | `eng`, `spa`, `deu`, `fra`, `por`, `ita`, `nld` | `eng` |
| `pageRange` | e.g. `1-5, 8` (PDF pages, slides or Excel sheets) | all |
| `chunkSize` / `chunkOverlap` | RAG chunk size and overlap in characters | 1000 / 100 |
| `maxPages`, `maxFileSizeMb`, `maxConcurrency` | Safety limits | 500, 50, 2 |

Example input:

```json
{
  "sources": [{ "url": "https://example.com/annual-report.pdf" }],
  "outputs": ["markdown", "tables", "chunks"],
  "ocr": "auto",
  "ocrLanguages": ["eng", "spa"]
}
```

### Output

One record per document. You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

```json
{
  "source": "https://example.com/annual-report.pdf",
  "status": "ok",
  "fileType": "pdf",
  "metadata": { "title": "Annual Report", "author": "ACME", "created": "2026-03-01T10:00:00", "pageCount": 12 },
  "stats": { "pagesProcessed": 12, "ocrPages": 0, "tables": 3, "characters": 18234 },
  "markdown": "# Annual Report\n\nThis report summarizes...\n\n| Year | Revenue | Region |\n| --- | --- | --- |\n| 2024 | 120 | EU |",
  "tables": [
    { "index": 0, "page": 1, "header": ["Year", "Revenue", "Region"], "rows": [["2024", "120", "EU"]], "csv": "Year,Revenue,Region\n2024,120,EU\n" }
  ],
  "chunks": [{ "index": 0, "text": "# Annual Report\n\nThis report...", "pageStart": 1, "pageEnd": 2, "tokens": 245 }],
  "warnings": []
}
```

Failed documents return `"status": "error"` with an `error` message and are not billed.

#### Data fields

| Field | Description |
|---|---|
| `markdown` | Whole document as Markdown: headings, paragraphs, lists, tables in reading order |
| `tables` | Each table as `header`, `rows`, `csv`, with its page/slide/sheet number (`pageEnd` for tables merged across pages) |
| `text` | Plain text |
| `pages` | Markdown per PDF page, slide or sheet, with an `ocr` flag |
| `chunks` | RAG chunks that respect paragraph boundaries, with `pageStart`/`pageEnd` and an approximate `tokens` count |
| `metadata` | Title, author, subject, creation/modification dates, page count, sheet names, detected `language` (ISO code) |
| `stats` | Pages processed, OCR pages, tables found, characters |
| `warnings` | Anything worth knowing (skipped pages, scanned pages with OCR disabled…) |

### How much does it cost to convert documents?

Pay-per-event pricing, with no monthly fee:

| Event | Price |
|---|---|
| Run start (per GB of memory, default 2 GB) | $0.001 |
| Document processed successfully | **$0.003** (about $3 per 1,000 documents) |
| Page read with OCR (scanned pages and images only) | **$0.01** |
| AI extraction (only with `extractionFields`/`extractionSchema`; Claude usage is billed to your own key) | $0.005 |

A run converting 100 native PDFs costs $0.002 + 100 × $0.003 = **$0.302**. A 5-page scanned PDF costs $0.002 + $0.003 + 5 × $0.01 = **$0.055**. Put many documents in one run to spread the start fee. Set **Max cost per run** in the run options, and the Actor stops cleanly when the limit is reached.

### Extract invoice, contract or form fields with AI

Add `extractionFields` (or a full `extractionSchema`) and your Anthropic API key. Each document then gets an `extracted` object with exactly those fields; missing values are `null`.

```json
{
  "sources": [{ "url": "https://example.com/invoice-1043.pdf" }],
  "extractionFields": ["invoice_number", "invoice_date", "vendor_name", { "name": "total", "type": "number" }],
  "extractionInstructions": "Dates as YYYY-MM-DD",
  "anthropicApiKey": "sk-ant-...",
  "extractionModel": "claude-sonnet-5"
}
```

```json
"extracted": { "invoice_number": "1043", "invoice_date": "2026-08-31", "vendor_name": "ACME Corp", "total": 1250.0 }
```

- Works on every supported format, including scanned PDFs (OCR runs first).
- Claude usage is billed by Anthropic to your key; this Actor adds one small **AI extraction** event per successful extraction.
- Use `extractionSchema` for nested data such as line items (`"type": "array"` of objects).

### Real-time API (Standby mode) for AI agents and apps

Need an answer in about a second instead of waiting for a run to start? Call the Actor's **Standby** endpoint. It stays warm and responds over plain HTTP, with the same pricing:

```bash
curl "https://<username>--document-to-markdown-tables.apify.actor/?url=https://example.com/report.pdf&outputs=markdown,tables"   -H "Authorization: Bearer <YOUR_APIFY_TOKEN>"
```

- `GET /?url=...&outputs=...&ocr=...&pageRange=...` returns one result object.
- `POST /` with the same JSON as the normal input (`sources`, `base64Files`, options) returns `{"results": [...]}`.

The exact URL is shown in the Actor's **Standby** tab.

### Use it with AI agents (MCP): Claude, ChatGPT, Cursor

This Actor works as a **tool for AI agents** through the [Apify MCP server](https://mcp.apify.com). Add it to Claude Desktop, Claude Code, Cursor, VS Code or any MCP client with this server URL:

```text
https://mcp.apify.com?tools=fguiraud/document-to-markdown-tables
```

Then just ask in plain language, for example:

- *"Convert this PDF to Markdown and give me its tables: https://example.com/annual-report.pdf"*
- *"Read these three invoices and return the invoice number, date and total of each"*
- *"Find all PDF reports linked on this page and summarise them"*

Smallest useful input for an agent:

```json
{
  "sources": [{ "url": "https://example.com/report.pdf" }],
  "outputs": ["markdown", "tables"],
  "maxCharactersPerDocument": 50000
}
```

- `maxCharactersPerDocument` keeps long documents inside the model's context window.
- `base64Files` lets an agent send a file it already has, without a public URL.
- Need a reply in about a second? Use the Standby endpoint above.

Agents pay per result like everyone else (Apify also supports agentic payments), so there is no subscription to set up.

### Tips

- Leave OCR on **auto**: it runs only on pages without a text layer.
- Use `pageRange` to process only the pages you need from large reports.
- Large batches are safe: downloads that fail with a temporary error (5xx, 429, network) are retried, and if the platform restarts or migrates the run, documents already processed are skipped, so they are never charged twice.
- For RAG, select `chunks` and tune `chunkSize` to your embedding model (e.g. 800–1500 characters).
- Tables with grid lines and tables with only horizontal rules (typical in scientific papers) are detected automatically. For tables with **no lines at all**, try `tableDetection: text`: it finds more tables, but may split columns imperfectly.

### FAQ and limitations

- **Which files are not supported?** Legacy Word and PowerPoint files (`.doc`, `.ppt`): save them as DOCX or PPTX first. Legacy Excel `.xls` and password-protected PDFs (with `pdfPassword`) are supported.
- **Private Google Drive files?** The file must be shared as "Anyone with the link"; otherwise it is reported as not accessible and is not billed.
- **Web pages**: static HTML is converted to Markdown with navigation, footers and cookie banners removed. JavaScript is not executed, so pages that render their content in the browser may come back mostly empty.
- **Layouts**: one- and two-column pages are supported. Three or more columns, and text inside figures, may come out in an imperfect order. Rotated text (for example margin stamps) is skipped.
- **Can I process local files?** Yes: send them in `base64Files` (up to ~9 MB of input per run), or upload them anywhere reachable by URL and use `sources`.
- **Privacy**: documents are processed in memory during the run and are not stored anywhere except in your own run's dataset.

Found a bug or need a new format? Open an issue in the **Issues** tab. Feedback is welcome.

# Actor input Schema

## `sources` (type: `array`):

Document URLs. Supported: PDF (native or scanned), DOCX, XLSX, XLS, PPTX, HTML pages, CSV, TXT and images. Share links from Google Drive, Google Docs/Sheets/Slides, Dropbox, OneDrive/SharePoint and GitHub are converted to downloads automatically (the file must be shared as 'Anyone with the link'). The real file type is detected from the content.

## `base64Files` (type: `array`):

For files that are not reachable by URL, e.g. from an AI agent or a local script: a list of objects {"fileName": "report.pdf", "content": "<base64>"}. A data: URI is also accepted as content. Keep the total input under ~9 MB; use URLs for bigger files.

## `followDocumentLinks` (type: `boolean`):

If a source URL is a web page, find links to PDF/DOCX/XLSX/XLS/PPTX/CSV files on it and convert those documents instead (e.g. a reports, filings or publications page). The page itself is not billed.

## `maxLinkedDocuments` (type: `integer`):

Limit for 'Also process documents linked from web pages'.

## `outputs` (type: `array`):

Fields to include for each document. 'markdown': whole document as LLM-ready Markdown (headings, lists, tables). 'tables': every table as header + rows + CSV. 'text': plain text. 'pages': Markdown per page/slide/sheet. 'chunks': Markdown split into RAG chunks with page numbers.

## `saveFiles` (type: `boolean`):

Save each document's Markdown as a .md file, each table as a .csv file and all tables together as an Excel workbook (.xlsx, one sheet per table) in the run's key-value store; the result includes their download links (handy without code: open the link, get the file).

## `tableDetection` (type: `string`):

'lines': detect tables drawn with borders/grid lines (precise, recommended). 'text': also detect borderless tables by aligning text columns (experimental: finds more tables, but columns can be split imperfectly).

## `removeHeadersFooters` (type: `boolean`):

Drop lines repeated at the top or bottom of most PDF pages (page numbers, document titles, confidentiality notices) so Markdown and RAG chunks stay clean.

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

'auto': run OCR only on scanned pages that contain no text (recommended). 'always': OCR every page, even if it has a text layer. 'never': no OCR (images and scanned pages return no text). OCR pages are billed separately.

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

Languages of the scanned text. Adding more languages is slightly slower.

## `pageRange` (type: `string`):

Pages to process, e.g. '1-5, 8'. Applies to PDF pages, PowerPoint slides and Excel sheets. Leave empty for all.

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

Safety limit on pages processed per document.

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

Maximum chunk length when 'RAG chunks' output is selected. Chunks follow paragraph boundaries.

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

Characters from the end of the previous chunk repeated at the start of the next one, to keep context.

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

Documents larger than this are skipped (not billed).

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

Password for encrypted PDFs (applies to all PDFs in the run).

## `maxCharactersPerDocument` (type: `integer`):

Truncate 'markdown' and 'text' to this many characters (0 = no limit). Useful for AI agents with limited context windows.

## `extractionFields` (type: `array`):

Pull specific values out of every document with Claude, e.g. \["invoice\_number", "invoice\_date", {"name": "total", "type": "number"}, "vendor\_name"]. Results appear in 'extracted'. Requires your Anthropic API key. Billed as one extra 'AI extraction' event per document (Claude usage is billed to your own Anthropic account).

## `extractionSchema` (type: `object`):

Instead of a field list, a full JSON Schema (type: object) for nested data, e.g. invoice line items. Every property is returned; missing values come back as null.

## `extractionInstructions` (type: `string`):

Optional guidance, e.g. 'Dates as YYYY-MM-DD; amounts without currency symbols'.

## `anthropicApiKey` (type: `string`):

Your key from console.anthropic.com. Stored as a secret input; used only to call Claude for this run.

## `extractionModel` (type: `string`):

'claude-opus-5': most accurate (default). 'claude-sonnet-5': cheaper, great for invoices/forms. 'claude-haiku-4-5': cheapest, for simple fields.

## `failOnError` (type: `boolean`):

Mark the run as FAILED when a document cannot be processed or yields no text. Useful for pipelines and monitoring. Successfully processed documents are still returned (and billed).

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

How many documents are processed at the same time.

## Actor input object example

```json
{
  "sources": [
    {
      "url": "https://raw.githubusercontent.com/jsvine/pdfplumber/stable/examples/pdfs/background-checks.pdf"
    },
    {
      "url": "https://raw.githubusercontent.com/ocrmypdf/OCRmyPDF/main/tests/resources/ccitt.pdf"
    }
  ],
  "followDocumentLinks": false,
  "maxLinkedDocuments": 20,
  "outputs": [
    "markdown",
    "tables"
  ],
  "saveFiles": false,
  "tableDetection": "lines",
  "removeHeadersFooters": true,
  "ocr": "auto",
  "ocrLanguages": [
    "eng"
  ],
  "maxPages": 500,
  "chunkSize": 1000,
  "chunkOverlap": 100,
  "maxFileSizeMb": 50,
  "maxCharactersPerDocument": 0,
  "extractionModel": "claude-opus-5",
  "failOnError": false,
  "maxConcurrency": 2
}
```

# Actor output Schema

## `results` (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 = {
    "sources": [
        {
            "url": "https://raw.githubusercontent.com/jsvine/pdfplumber/stable/examples/pdfs/background-checks.pdf"
        },
        {
            "url": "https://raw.githubusercontent.com/ocrmypdf/OCRmyPDF/main/tests/resources/ccitt.pdf"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fguiraud/document-to-markdown-tables").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 = { "sources": [
        { "url": "https://raw.githubusercontent.com/jsvine/pdfplumber/stable/examples/pdfs/background-checks.pdf" },
        { "url": "https://raw.githubusercontent.com/ocrmypdf/OCRmyPDF/main/tests/resources/ccitt.pdf" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("fguiraud/document-to-markdown-tables").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 '{
  "sources": [
    {
      "url": "https://raw.githubusercontent.com/jsvine/pdfplumber/stable/examples/pdfs/background-checks.pdf"
    },
    {
      "url": "https://raw.githubusercontent.com/ocrmypdf/OCRmyPDF/main/tests/resources/ccitt.pdf"
    }
  ]
}' |
apify call fguiraud/document-to-markdown-tables --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fguiraud/document-to-markdown-tables"
        }
    }
}
```

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/RyrejunFhzRwPMVqu/builds/BwpaN80ZWLMwmUSad/openapi.json
