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

Convert PDF, DOCX, PPTX, XLSX, EPUB, HTML, CSV and TXT into clean Markdown plus optional token-sized chunks with page numbers for RAG and LLM pipelines. Optional OCR for scanned PDFs. Processes only files you supply.

- **URL**: https://apify.com/steady_kahikatea/document-to-markdown.md
- **Developed by:** [Nick Hellmich](https://apify.com/steady_kahikatea) (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

Pay per event

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

## Document to Markdown & RAG Chunks (PDF, DOCX, PPTX, XLSX, HTML, EPUB)

Convert PDFs, Word, PowerPoint, Excel, HTML, EPUB, CSV and text files into **clean Markdown**, with optional **token-sized chunks for RAG / LLM pipelines** and OCR for scanned pages. Upload files or paste links, including Google Docs/Sheets/Slides, Google Drive, Dropbox and GitHub links, which are converted automatically.

### Quick start

1. Click **Start**. The prefilled input converts the IRS W-9 form (a 6-page PDF) so you can see real output in about 10 seconds for $0.012.
2. Replace it with your own files: **upload**, or paste public links.
3. Open the **Output** tab. There's one row per document with the Markdown, the page count, token count and chunks.

### What you get

| Field | Description |
|---|---|
| `markdown` | Clean Markdown with headings, lists, tables, and `<!-- page N -->` markers for PDFs |
| `chunks` | When `chunkSize` > 0: token-sized chunks with `pageStart`/`pageEnd` and token counts |
| `pageCount`, `tokenCount`, `detectedFormat`, `metadata` | Useful for budgeting embeddings |
| `charged`, `note`, `error` | `charged: false` rows cost nothing; `error` says what to do |

Sample row (trimmed):

```json
{ "source": "https://www.irs.gov/pub/irs-pdf/fw9.pdf", "ok": true, "detectedFormat": "pdf", "pageCount": 6,
  "tokenCount": 7412, "chunkCount": 15, "charged": true,
  "markdown": "<!-- page 1 -->\nForm W-9 (Rev. March 2024) Request for Taxpayer Identification Number and Certification ...",
  "chunks": [{ "index": 0, "text": "Form W-9 ...", "tokens": 512, "pageStart": 1, "pageEnd": 1 }] }
```

### Input tips

- **Links that work:** direct file links plus share links from Google Docs/Sheets/Slides (exported as DOCX/XLSX/PPTX), Google Drive files, Dropbox and GitHub file pages. The file must be shared as **"Anyone with the link"**. Private links return a clear, **uncharged** error.
- **RAG chunks:** set `chunkSize` (for example 512) and `chunkOverlap` (for example 64). The tokenizer is `cl100k_base` (OpenAI) or `o200k_base`.
- **Scanned PDFs:** turn on **OCR** and pick the language (OCR pages are billed separately).

### Pricing (pay per event)

| Event | Price |
|---|---|
| Document processed (includes the first 5 pages) | $0.01 |
| Extra page beyond 5 | $0.002 |
| OCR page | $0.01 |

Examples: a 2-page DOCX costs **$0.01**; a 15-page PDF costs **$0.03**; a 50-page report costs **$0.10**. **Failed documents are never charged**, and that includes dead links, sign-in pages and unsupported files. Bronze, Silver and Gold Apify plans get **10/20/30% off**. You can cap any run with **Maximum cost per run**.

### Limits

Up to 50 documents per run, 50 MB per file and 200 pages per document by default (500 max, set with `maxPages`). The output stays in your run's dataset under Apify's normal retention. Documents are processed in memory and never logged.

### FAQ

**Does it work with Google Docs?** Yes. Paste the normal share link; the document must be shared with "Anyone with the link".
**Why did I get "LOGIN_OR_VIEWER_PAGE"?** The link pointed at a sign-in or viewer page, not the file. Make it public or paste a direct download link. You weren't charged.
**Tables?** DOCX, XLSX, PPTX and HTML tables become Markdown tables. PDF tables come out as text in reading order.
**Images or charts?** Their text is extracted only via OCR; images themselves aren't described.
**Can an AI agent call it?** Yes, through the Apify API or MCP server, and it supports x402 payments. See the API tab for ready-to-paste code.

### Integrations: no-code and AI

#### Zapier: new Google Drive file → Markdown chunks → your RAG knowledge base

1. **Trigger:** Google Drive → *New File in Folder*. Share the folder as "Anyone with the link" (or use a direct download link).
2. **Action:** Apify → *Run Actor* → `Document to Markdown & RAG Chunks`, with run synchronously turned on (small files finish in about 10 s). Input: `{"fileUrls": ["{{Alternate Link}}"], "chunkSize": 512, "chunkOverlap": 64}`.
3. **Search:** Apify → *Fetch dataset items* using the run's `defaultDatasetId`.
4. **Action:** send `chunks[].text` to your vector store (Pinecone, Qdrant or Supabase via Webhooks by Zapier), or save `markdown` to Notion or Google Docs.

#### Make: email attachments → Markdown in Google Sheets

*Gmail Watch Emails* → *Apify Run an Actor* (run synchronously: Yes) → *Apify Get Dataset Items* (Dataset ID = `defaultDatasetId`) → *Google Sheets Bulk Add Rows* (`source`, `pageCount`, `tokenCount`, `markdown`).

#### n8n: nightly re-index of a docs folder

Install the Apify node (`@apify/n8n-nodes-apify`, or search "Apify" on n8n Cloud) → *Schedule Trigger* → *Apify: Run an Actor and get dataset* → *Split Out* `chunks` → *Embeddings + Vector Store* nodes. In an **AI Agent** node, add the *Apify Tool* so the agent can convert a file the user pastes into chat.

#### LangChain (Python): load documents straight into a vector index

```python
from langchain_apify import ApifyWrapper
from langchain_core.documents import Document
docs = ApifyWrapper().call_actor(
    actor_id="steady_kahikatea/document-to-markdown",
    run_input={"fileUrls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf"], "chunkSize": 512},
    dataset_mapping_function=lambda item: Document(page_content=item.get("markdown") or "",
                                                   metadata={"source": item["source"], "pages": item.get("pageCount")}),
).load()
```

For an agent, use `ApifyActorsTool("steady_kahikatea/document-to-markdown")`.

#### LlamaIndex: one Document per chunk, with page numbers

```python
from llama_index.core import Document
from llama_index.readers.apify import ApifyActor
items = ApifyActor("<APIFY_TOKEN>").load_data(
    actor_id="steady_kahikatea/document-to-markdown",
    run_input={"fileUrls": ["https://example.com/handbook.pdf"], "chunkSize": 512},
    dataset_mapping_function=lambda it: Document(text=it.get("markdown") or "", metadata={"source": it["source"]}))
```

**Sample input → output:** `{"fileUrls": ["https://example.com/handbook.pdf"], "chunkSize": 512}` → `{"ok": true, "pageCount": 12, "tokenCount": 6120, "chunks": [{"index": 0, "text": "# Employee Handbook ...", "tokens": 512, "pageStart": 1, "pageEnd": 2}], "charged": true}`. Cost: $0.024.

> **Tip:** Zapier's synchronous *Run Actor* times out after 30 seconds. For anything longer, run it asynchronously and start a second Zap from the **Finished Actor run** trigger. Make's synchronous timeout depends on your plan; use **Watch Actor Runs** for long jobs.

**Also works with:** the Apify API ("API" tab), the Apify MCP server for Claude/Cursor-style agents, and x402 payments.

**Docs used:** [Zapier](https://docs.apify.com/platform/integrations/zapier) · [Make](https://docs.apify.com/platform/integrations/make) · [n8n](https://docs.apify.com/platform/integrations/n8n) · [LangChain](https://docs.apify.com/platform/integrations/langchain) · [LlamaIndex](https://docs.apify.com/platform/integrations/llama-index)

### For AI agents

```
Actor: steady_kahikatea/document-to-markdown
Input: {"fileUrls": ["https://example.com/file.pdf"], "chunkSize": 512}
Output: one dataset row per document: markdown, chunks[], pageCount, tokenCount, charged, error
```

### Related

Need the reverse? **Markdown & HTML to PDF / DOCX Generator** (steady_kahikatea/markdown-html-to-pdf-docx).

Feedback or a file that converts badly? Open an issue on the Issues tab. If this Actor saves you time, a rating helps others find it.

# Actor input Schema

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

Upload documents or provide public HTTP(S) URLs. Supported: PDF, DOCX, PPTX, XLSX, EPUB, HTML, CSV, TXT/MD. Max 50 MB per file, 50 documents per run.

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

If > 0, also split each document into overlapping chunks of this many tokens, each tagged with pageStart/pageEnd. 0 = Markdown only.

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

Tokens shared between consecutive chunks. Must be smaller than chunk size.

## `tokenEncoding` (type: `string`):

tiktoken encoding used for token counts and chunking. cl100k_base (GPT-4/3.5, close for most embedding models) or o200k_base (GPT-4o family).

## `includeTokenCounts` (type: `boolean`):

Add tokenCount to each document and chunk.

## `enableOcr` (type: `boolean`):

Run Tesseract OCR on PDF pages that have no extractable text (scans/images). Billed per OCR'd page. Slower.

## `ocrLanguage` (type: `string`):

Tesseract language for OCR.

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

Stop after this many pages (PDF/PPTX) or ~3,000-character page-equivalents (other formats). Caps cost on very large files.

## `documents` (type: `array`):

Optional. Objects with either {"url": "https://..."} or {"keyValueStoreId": "...", "key": "..."}. Combined with the files above. Most users only need the upload field above.

## Actor input object example

```json
{
  "chunkSize": 512,
  "chunkOverlap": 64,
  "tokenEncoding": "cl100k_base",
  "includeTokenCounts": true,
  "enableOcr": false,
  "ocrLanguage": "eng",
  "maxPages": 200,
  "documents": [
    {
      "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    }
  ]
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "chunkSize": 512,
    "documents": [
        {
            "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("steady_kahikatea/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 = {
    "chunkSize": 512,
    "documents": [{ "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf" }],
}

# Run the Actor and wait for it to finish
run = client.actor("steady_kahikatea/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 '{
  "chunkSize": 512,
  "documents": [
    {
      "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    }
  ]
}' |
apify call steady_kahikatea/document-to-markdown --silent --output-dataset

```

## MCP server setup

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