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

PDF to Markdown converter and PDF text extractor for RAG and AI agents. Turn PDFs, scanned PDFs, DOCX, HTML and images into clean Markdown or text with page-aware chunks. OCR runs only on scanned pages. $3 per 1,000 documents, failed files free.

- **URL**: https://apify.com/hanielazevedo/document-to-markdown-ocr.md
- **Developed by:** [Haniel Azevedo](https://apify.com/hanielazevedo) (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 $3.00 / 1,000 document converteds

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

**English** · [Português](#português)

**PDF to Markdown converter and PDF text extractor: turn any PDF, scanned PDF, Word file, web page or image into clean Markdown or plain text that an LLM can read, with OCR only where a page really needs it.**

Supported formats: **PDF, scanned PDF, DOCX, ODT, RTF, EPUB, HTML, TXT/MD and images (PNG, JPG, TIFF, WEBP, BMP, GIF)**. Each document becomes one dataset item with metadata, optional **RAG chunks** (heading path and page range), a downloadable `.md` file, and a **machine-readable error code** if something goes wrong.

- 📄 **PDF text layer first**, page by page. Tesseract 5 OCR runs **only** on scanned or image-only pages, or on every page if you ask for it.
- 🧾 **Scans and photos**: invoices, receipts, Brazilian *notas fiscais*, contracts and IDs (English, Portuguese, Spanish, French, German, Italian, Dutch).
- 📝 **Office and web formats** via Pandoc: headings, lists, links and **tables as GitHub-flavoured Markdown**. Navigation, cookie banners, footers and sidebars are stripped from HTML.
- ✂️ **RAG chunking**: heading-aware chunks with overlap, approximate token counts, character offsets and `pageStart`/`pageEnd`.
- 🛡️ **Reliable at scale**: one bad file never fails the run. Hard size, page and time limits, SSRF protection, and stable error codes for automatic retries.
- 💸 **$3 per 1,000 documents**, plus $0.01 per OCR page. **Failed documents are free.** PDF pages with a text layer are never charged as OCR.

### Who it's for

- **AI and RAG builders** loading document libraries into Pinecone, Weaviate, Qdrant, pgvector or LangChain/LlamaIndex pipelines, with page citations.
- **AI agents and LLM apps** (GPT, Claude, Grok, Llama) that need clean Markdown instead of noisy PDF text, which also uses fewer tokens.
- **Back-office automation**: invoices, receipts and *notas fiscais* (DANFE) OCR'd and passed to an extraction prompt or regex.
- **Docs and knowledge-base teams** moving DOCX, ODT, EPUB and HTML to Markdown for docs sites, wikis, Notion or Obsidian.
- **Legal, compliance and research**: bulk text from contracts, tenders, court filings and papers, ready for search and review.

### Try it

Click **Start** without changing anything. The default input converts a real 15-page research paper (PDF) and a Wikipedia article (HTML):

```json
{
  "urls": [
    "https://arxiv.org/pdf/1706.03762v7",
    "https://en.wikipedia.org/wiki/Markdown"
  ]
}
```

It finishes in about 10 seconds and returns 2 items: about 38,000 characters of Markdown from the PDF, with `# Attention Is All You Need`, `### Abstract` and the numbered section headings, and the Wikipedia page as Markdown with its tables. No OCR is needed, so the run costs **$0.006**. An empty input `{}` does the same. Then replace the URLs with your own, or upload files in the **Files** field.

### How to convert PDF to Markdown (step by step)

1. Open the **Input** tab and paste one or more PDF, DOCX or web page links into **Document URLs**, or upload files in **Upload files**.
2. Keep **Output format** on `markdown` (or choose `text` for plain text).
3. Leave **OCR mode** on `auto`: pages with a text layer are read directly and only scanned pages go through OCR. Set **OCR languages** if your scans aren't English or Portuguese.
4. For RAG, turn on **Split into RAG chunks** and set the chunk size and overlap.
5. Optional: set **Maximum cost per run** in the run options to cap spend.
6. Click **Start**. When the run finishes, open the **Output** tab, download the `.md` files from the key-value store, or export the dataset as JSON, CSV or Excel.

### Input

Use any combination of these:

| Field | What it does |
|---|---|
| `urls` | Public http(s) links. The format is detected from the file content, not the extension. |
| `files` | Files uploaded in the Apify Console (stored in a key-value store). |
| `keyValueStoreId` (+ `keyValueStoreKeys`) | Convert every record in one of your key-value stores (up to 1,000), or only the keys you list. The Actor gets read-only access to that store only. |
| `outputFormat` | `markdown` (default) or `text`. |
| `ocrMode` | `auto` (default: OCR only pages with no usable text), `always`, or `never`. |
| `ocrLanguages` | Tesseract codes joined with `+`. Default `eng+por`. |
| `maxPages` | Convert only the first N pages per document (this caps OCR cost). |
| `enableChunking`, `chunkSize`, `chunkOverlap` | RAG chunks. Defaults: off, 1,500 and 150 characters. |
| `includeContentInDataset` | Set to `false` to keep only metadata and the `.md` file link in the dataset. |
| `maxFileSizeMb`, `timeoutSecondsPerDocument`, `maxConcurrency` | Limits. Defaults: 50 MB, 600 s, 2 documents in parallel. |

```json
{
  "urls": [
    "https://example.com/contract.docx",
    "https://example.com/scanned-invoice.jpg"
  ],
  "outputFormat": "markdown",
  "ocrMode": "auto",
  "ocrLanguages": "eng+por",
  "maxPages": 200,
  "enableChunking": true,
  "chunkSize": 1500,
  "chunkOverlap": 150
}
```

API example (synchronous, returns the dataset items):

```bash
curl -X POST "https://api.apify.com/v2/acts/hanielazevedo~document-to-markdown-ocr/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" -H 'Content-Type: application/json' \
  -d '{"urls":["https://arxiv.org/pdf/1706.03762v7"]}'
```

### Use with AI agents (MCP) and Python

**MCP:** add this Actor as a tool in Claude Desktop, Cursor, VS Code or any MCP client through the Apify MCP server: `https://mcp.apify.com?tools=hanielazevedo/document-to-markdown-ocr` (sign in with your Apify account or pass your API token). The agent sees the input schema and can run the Actor and read the dataset itself.

**Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("hanielazevedo/document-to-markdown-ocr").call(run_input={
    "urls": [
        "https://arxiv.org/pdf/1706.03762v7"
    ],
    "outputFormat": "markdown",
    "enableChunking": True
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

Each item has the Markdown in `item["markdown"]` and, with chunking on, the RAG chunks in `item["chunks"]`. The `run-sync-get-dataset-items` endpoint above does the same in one HTTP call.

### Sample output

One item per document. This is the PDF item from a run with the default input (Markdown shortened, the rest unchanged). The full Markdown is also saved as a `.md` file in the run's key-value store and linked in `outputFileUrl`.

```json
{
  "index": 0,
  "url": "https://arxiv.org/pdf/1706.03762v7",
  "source": "url",
  "filename": "1706.03762v7.pdf",
  "mimeType": "application/pdf",
  "detectedFormat": "pdf",
  "bytes": 2215244,
  "status": "success",
  "format": "markdown",
  "title": null,
  "pages": 15,
  "pagesProcessed": 15,
  "ocrPagesUsed": 0,
  "ocrConfidence": null,
  "characters": 38296,
  "words": 5804,
  "markdown": "### Provided proper attribution is provided, Google hereby grants permission to reproduce the tables and figures in this paper solely for use in journalistic or scholarly works.\n\n# Attention Is All You Need\n\nAshish Vaswani∗\nGoogle Brain\navaswani@google.com\n\n…\n\n### Abstract\n\nThe dominant sequence transduction models are based on complex recurrent or convolutional neural networks that include an encoder and a decoder. …\n\n### Introduction\n\n…",
  "text": null,
  "outputFileKey": "0001-1706.03762v7.md",
  "outputFileUrl": "https://api.apify.com/v2/key-value-stores/<run-store-id>/records/0001-1706.03762v7.md?signature=…",
  "chunkCount": null,
  "chunks": null,
  "warnings": [],
  "error": null,
  "errorCode": null,
  "durationMs": 2075,
  "charged": {"document-converted": 1, "ocr-page": 0}
}
```

The Wikipedia item from the same run: `"detectedFormat": "html"`, `"title": "Markdown - Wikipedia"`, 42,527 characters, Markdown tables, and one warning: `{"code": "IMAGES_REMOVED", "message": "2 embedded image(s) removed (alt text kept). …"}`.

With `enableChunking: true`, each item also has chunks such as:

```json
{"index": 0, "text": "# Attention Is All You Need\n\n…", "chars": 1480, "approxTokens": 370,
 "headings": ["Attention Is All You Need"], "startChar": 0, "endChar": 1480, "pageStart": 1, "pageEnd": 1}
```

A failed document still produces an item, so you can match inputs to outputs:

```json
{"url": "https://example.com/broken.pdf", "status": "failed", "errorCode": "CORRUPT_FILE",
 "error": {"code": "CORRUPT_FILE", "message": "PDF could not be opened: …", "retryable": false},
 "charged": {"document-converted": 0, "ocr-page": 0}}
```

The `OUTPUT` record in the key-value store holds the run totals: `total`, `succeeded`, `partial`, `failed`, `ocrPages`, `characters` and `stoppedByBudget`.

#### Status values

- `success`: fully converted.
- `partial`: converted with something missing, for example OCR stopped by your max run cost or a per-document timeout. The text that was extracted is returned and charged as one document.
- `failed`: nothing usable was extracted. Never charged.

#### Error codes (`errorCode`, document not charged)

| Code | Meaning | Retry? |
|---|---|---|
| `INVALID_INPUT` / `INVALID_URL` | Empty entry, not http(s), or credentials in the URL | no |
| `BLOCKED_URL` | Resolves to a private, loopback, link-local or cloud-metadata address (SSRF protection) | no |
| `DNS_ERROR` | The hostname does not resolve | maybe |
| `DOWNLOAD_FAILED` / `DOWNLOAD_TIMEOUT` | Network or TLS error, too many redirects, or too slow | yes |
| `NOT_FOUND` | HTTP 404/410, or the key-value store record does not exist | no |
| `ACCESS_DENIED` | The run cannot read that key-value store: pick it in the *Key-value store* field so read access is granted | no |
| `HTTP_ERROR` | Any other non-2xx response (`retryable: true` for 408/429/5xx) | see flag |
| `FILE_TOO_LARGE` / `EMPTY_FILE` | Bigger than `maxFileSizeMb`, or 0 bytes | no |
| `UNSUPPORTED_FORMAT` | For example legacy `.doc`, `.xls`, ZIP or video | no |
| `CORRUPT_FILE` / `ENCRYPTED_PDF` | Cannot be parsed, or password-protected | no |
| `TOO_MANY_PAGES` | Above the 2,000-page hard limit | no |
| `CONVERSION_TIMEOUT` / `CONVERSION_FAILED` | The converter ran out of time before any output, or failed unexpectedly | maybe |
| `NO_TEXT_FOUND` | Nothing extractable (for example a scan with `ocrMode: never`) | no |
| `BUDGET_EXCEEDED` | Your *Maximum cost per run* was reached before this document | yes, with a higher limit |

#### Warning codes (`warnings[].code`)

`PAGES_TRUNCATED`, `OCR_SKIPPED_OCR_MODE_NEVER`, `OCR_LIMITED_BY_BUDGET`, `OCR_PAGE_FAILED`, `OCR_LOW_CONFIDENCE` (mean below 60), `OCR_LANGUAGE_UNSUPPORTED`, `TIMEOUT_PARTIAL_OUTPUT`, `CONTENT_TRUNCATED_IN_DATASET` (the full file is still in `outputFileUrl`), `CHUNKS_MOVED_TO_KEY_VALUE_STORE` (see `chunksUrl`), `IMAGES_REMOVED`, `LOW_TEXT_YIELD`.

### How much does it cost to convert PDF to Markdown?

Pay per event:

| Event | Price | When |
|---|---|---|
| `apify-actor-start` | $0.00005 | Once per run, per GB of memory (default 1 GB) |
| `document-converted` | **$0.003** | Each document converted (`success` or `partial`). **Failed documents are free.** |
| `ocr-page` | **$0.01** | Each page or image actually run through OCR. Pages with a text layer are free. |

There is no separate charge for platform compute. Worked examples:

| Job | Calculation | Cost |
|---|---|---|
| The default input (1 PDF + 1 web page) | 2 × $0.003 | **$0.006** |
| 1,000 digital PDFs, DOCX or HTML files | 1,000 × $0.003 | **$3.00** |
| 1,000 single-page scanned invoices (JPG or scanned PDF) | 1,000 × ($0.003 + $0.01) | **$13.00** |
| One 300-page digital PDF | page count doesn't matter with a text layer | **$0.003** |
| One 50-page scanned PDF with `maxPages: 10` | $0.003 + 10 × $0.01 | **$0.103** |

Set **Maximum cost per run** in the run options to cap spend. The Actor reserves budget before each OCR page, never charges beyond your limit, and marks what it skipped (`OCR_LIMITED_BY_BUDGET` or `BUDGET_EXCEEDED`).

### Limits

- File size: 50 MB by default, 100 MB maximum. Pages per document: 2,000 maximum. Timeout per document: 600 s by default, 3,600 s maximum.
- Up to 10,000 URLs per run, and up to 1,000 records per key-value store.
- OCR renders pages at about 300 DPI, capped at 40 MP per page, at roughly 1 page per second with the default 1 GB memory. Use 2–4 GB for large OCR batches.
- Dataset items larger than about 8 MB keep the full content only in the key-value store file (with a warning).
- Not supported: legacy binary `.doc`/`.xls`/`.ppt`, PPTX and XLSX, handwriting, and math formula reconstruction. Images inside documents are dropped (alt text is kept). PDF tables without ruling lines come out as text in reading order, not as Markdown tables.
- Only public URLs are fetched. Private network and cloud-metadata addresses are blocked on every redirect hop.

### How it compares

| | This Actor | Typical alternatives on the Store |
|---|---|---|
| Price per digital document | $0.003, any page count | Two other PDF converter Actors checked on 31 Oct 2026: $0.005 per PDF plus a $0.005 start fee, and $0.02 per PDF plus $0.001 per page |
| OCR | $0.01 per OCR'd page, only on pages with no usable text layer | $0.015 per OCR page in the first of those two |
| Formats | PDF, scans, images, DOCX, ODT, RTF, EPUB, HTML, TXT | Often PDF-only or images-only |
| Failures | Per-document error codes, free, never fail the run | Often a generic error or a failed run |
| RAG | Heading-aware chunks with page ranges | Often absent or fixed-size |

### FAQ

**What happens when a file fails?** That document gets `status: "failed"`, an `errorCode` from the table above and a `retryable` flag, and it is not charged. The other documents keep going, and the run still succeeds. An input with no documents at all fails straight away with an `Input error` message.

**What are the limits?** 50 MB per file by default (100 MB maximum), 2,000 pages per document, 600 s per document by default, 10,000 URLs per run. See [Limits](#limits). Use `maxPages` to convert only the first pages of large scans.

**Can I schedule it or watch a folder?** There is no built-in monitor mode. Each run converts the documents you give it. For recurring jobs, create an Apify **Schedule** or **Task** that points at a key-value store or URL list, or trigger runs from a webhook in your own pipeline.

**Where do the documents come from, and is it legal?** Only from the URLs, uploads or key-value stores you supply. The Actor does not crawl or search the web. You are responsible for having the right to process the files. Your data is processed inside your own Apify run, kept only in that run's storage under your account's retention settings, and not used for training.

**Which output formats do I get?** Markdown (default) or plain text, inline in the dataset (JSON, CSV, Excel, XML or RSS export) and as a `.md`/`.txt` file per document. RAG chunks are optional.

**Does it integrate with my stack?** Yes: the Apify API and SDKs (Python, JavaScript), `run-sync-get-dataset-items` for one-call conversion, webhooks, Zapier, Make, n8n, LangChain and LlamaIndex Apify loaders, and the Apify MCP server for AI agents. It is pay-per-event only, needs limited permissions and has no Standby.

**Why did a PDF use OCR, and how good is it?** In `auto` mode, a page is OCR'd only when it has no text layer, almost no text, or garbled text (broken font encodings). Tesseract 5 handles Portuguese accents (ç, ã, é) well on clean scans (about 95% mean confidence on our test invoices). Low-quality photos get the `OCR_LOW_CONFIDENCE` warning. Use `ocrMode: never` to turn OCR off.

**Are tables preserved? Which licences are used?** Tables in DOCX, ODT, HTML and EPUB become GitHub pipe tables (merged cells are flattened). PDF tables are detected where ruling lines exist; otherwise their text is kept in reading order. Only permissive open-source tools run in-process (pdfminer.six, pdfplumber, pypdfium2, Pillow, httpx, Tesseract). Pandoc (GPL-2.0+) runs as a separate, unmodified program. No AGPL components. See `NOTICE.md`.

### Other Actors by Haniel

- [SAM.gov Scraper: Federal Contract Opportunities Monitor](https://apify.com/hanielazevedo/samgov-contract-opportunities): New SAM.gov contract opportunities, RFPs and sources sought by NAICS, set-aside and agency. No API key. $2 per 1,000 notices.
- [Consulta CNPJ em Lote: Brazil CNPJ Lookup (Receita Federal)](https://apify.com/hanielazevedo/brazil-cnpj-company-lookup): Bulk Brazil CNPJ lookup: legal name, CNAE, partners (QSA), address, Simples Nacional and MEI. $1.20 per 1,000 companies.
- [Greenhouse, Lever & Ashby Jobs Scraper: New Jobs Monitor](https://apify.com/hanielazevedo/greenhouse-lever-ashby-new-jobs-monitor): Jobs from Greenhouse, Lever and Ashby career pages, with a monitor mode that returns only new postings. $1.50 per 1,000 jobs.
- [Floor Plan Takeoff: PDF Room Areas, Wall LF & Door Counts](https://apify.com/hanielazevedo/floor-plan-takeoff): Construction takeoff from vector floor-plan PDFs: room SF, wall linear feet, door counts and a check overlay. $2.50 per measured sheet.

***

### Português

**Conversor de PDF para Markdown e extrator de texto: transforme qualquer PDF, PDF escaneado, arquivo Word, página web ou imagem em Markdown limpo ou texto simples para LLMs, com OCR só onde a página realmente precisa.**

Formatos: **PDF, PDF escaneado, DOCX, ODT, RTF, EPUB, HTML, TXT/MD e imagens (PNG, JPG, TIFF, WEBP, BMP, GIF)**. Cada documento vira um item do dataset com metadados, **chunks para RAG** opcionais (títulos e intervalo de páginas), um arquivo `.md` para download e um **código de erro legível por máquina** quando algo dá errado.

- 📄 **Camada de texto do PDF primeiro**, página por página. O OCR (Tesseract 5) roda **só** nas páginas escaneadas ou sem texto, ou em todas, se você pedir.
- 🧾 **Digitalizações e fotos**: notas fiscais (DANFE), recibos, contratos e documentos, em português, inglês, espanhol, francês, alemão, italiano e holandês.
- 📝 **Formatos Office e web** via Pandoc: títulos, listas, links e **tabelas em Markdown (GFM)**. Em páginas HTML, menus, banners de cookies, rodapés e barras laterais são removidos.
- ✂️ **Chunking para RAG**: chunks por seção, com sobreposição, contagem aproximada de tokens e `pageStart`/`pageEnd`.
- 💸 **US$ 3 por 1.000 documentos**, mais US$ 0,01 por página com OCR. **Documentos com falha não são cobrados.**

#### Para quem é

- **Quem monta RAG e IA**: bibliotecas de documentos para Pinecone, Weaviate, Qdrant, pgvector, LangChain ou LlamaIndex, com citação de página.
- **Agentes de IA e apps com LLM** que precisam de Markdown limpo em vez de texto bagunçado de PDF (e gastam menos tokens).
- **Automação de backoffice**: OCR de notas fiscais, boletos e recibos para extrair campos com prompt ou regex.
- **Times de documentação**: DOCX, ODT, EPUB e HTML para Markdown (sites de docs, wikis, Notion, Obsidian).
- **Jurídico, compliance e pesquisa**: texto em massa de contratos, editais, processos e artigos.

#### Experimente

Clique em **Start** sem mudar nada. A entrada padrão converte um artigo científico real de 15 páginas (PDF) e um artigo da Wikipédia (HTML):

```json
{
  "urls": [
    "https://arxiv.org/pdf/1706.03762v7",
    "https://en.wikipedia.org/wiki/Markdown"
  ]
}
```

Termina em cerca de 10 segundos com 2 itens: cerca de 38 mil caracteres de Markdown do PDF, com `# Attention Is All You Need`, `### Abstract` e os títulos das seções, e a página da Wikipédia em Markdown com tabelas. Sem OCR, a execução custa **US$ 0,006**. A entrada vazia `{}` faz o mesmo. Depois troque as URLs pelas suas ou envie arquivos no campo **Files**.

#### Como converter PDF para Markdown (passo a passo)

1. Na aba **Input**, cole links de PDF, DOCX ou páginas web em **Document URLs**, ou envie arquivos em **Upload files**.
2. Mantenha **Output format** em `markdown` (ou `text` para texto simples).
3. Deixe **OCR mode** em `auto`: páginas com texto são lidas direto e só as escaneadas passam por OCR. Ajuste **OCR languages** se precisar.
4. Para RAG, ative **Split into RAG chunks** e defina tamanho e sobreposição dos chunks.
5. Opcional: defina *Maximum cost per run* para limitar o gasto.
6. Clique em **Start** e baixe os arquivos `.md` ou exporte o dataset em JSON, CSV ou Excel.

#### Entrada

`urls`, `files` (upload pelo Console) e/ou `keyValueStoreId` (+ `keyValueStoreKeys`). Opções: `outputFormat` (`markdown` ou `text`), `ocrMode` (`auto`, `always` ou `never`), `ocrLanguages` (padrão `eng+por`), `maxPages`, `enableChunking`, `chunkSize`, `chunkOverlap`, `includeContentInDataset`, `maxFileSizeMb`, `timeoutSecondsPerDocument` e `maxConcurrency`. Veja a tabela e o exemplo na seção em inglês.

#### Exemplo de saída

Um item por documento, com `url`, `filename`, `mimeType`, `pages`, `ocrPagesUsed`, `characters`, `words`, `markdown`/`text`, `chunks`, `warnings`, `error`/`errorCode`, `outputFileUrl` (link do `.md`) e `charged`. O JSON acima, em inglês, é o item real do PDF de uma execução com a entrada padrão: 15 páginas, 0 páginas com OCR, 38.296 caracteres, `"status": "success"`, `"charged": {"document-converted": 1, "ocr-page": 0}`.

#### Códigos de erro

Documentos com falha recebem `status: "failed"`, um `errorCode` e o campo `retryable`, e não são cobrados: `INVALID_URL`, `BLOCKED_URL` (endereço privado, proteção SSRF), `DNS_ERROR`, `DOWNLOAD_FAILED`/`DOWNLOAD_TIMEOUT` (tente de novo), `NOT_FOUND`, `ACCESS_DENIED`, `HTTP_ERROR`, `FILE_TOO_LARGE`/`EMPTY_FILE`, `UNSUPPORTED_FORMAT`, `CORRUPT_FILE`/`ENCRYPTED_PDF`, `TOO_MANY_PAGES`, `CONVERSION_TIMEOUT`/`CONVERSION_FAILED`, `NO_TEXT_FOUND` e `BUDGET_EXCEEDED`. O significado de cada um está na tabela em inglês.

#### Quanto custa converter PDF para Markdown?

| Evento | Preço | Quando |
|---|---|---|
| `apify-actor-start` | US$ 0,00005 | Uma vez por execução, por GB de memória (padrão 1 GB) |
| `document-converted` | **US$ 0,003** | Por documento convertido. **Falhas são grátis.** |
| `ocr-page` | **US$ 0,01** | Por página ou imagem que passou por OCR. Páginas com texto não pagam OCR. |

| Tarefa | Cálculo | Custo |
|---|---|---|
| Entrada padrão (1 PDF + 1 página web) | 2 × US$ 0,003 | **US$ 0,006** |
| 1.000 PDFs digitais, DOCX ou HTML | 1.000 × US$ 0,003 | **US$ 3,00** |
| 1.000 notas fiscais escaneadas de uma página | 1.000 × (US$ 0,003 + US$ 0,01) | **US$ 13,00** |
| Um PDF digital de 300 páginas | o número de páginas não importa | **US$ 0,003** |
| Um PDF escaneado de 50 páginas com `maxPages: 10` | US$ 0,003 + 10 × US$ 0,01 | **US$ 0,103** |

Use *Maximum cost per run* para limitar o gasto: o Actor nunca cobra acima do limite e marca o que ficou de fora (`OCR_LIMITED_BY_BUDGET` ou `BUDGET_EXCEEDED`).

#### Limites

Até 50 MB por arquivo (máximo 100 MB), 2.000 páginas por documento, 600 s por documento (máximo 3.600 s), 10.000 URLs por execução e 1.000 registros por key-value store. Não suporta `.doc`/`.xls`/`.ppt` antigos, PPTX, XLSX ou manuscritos. Imagens dentro dos documentos são descartadas (o texto alternativo é mantido). Tabelas de PDF sem linhas de grade saem como texto.

#### Perguntas frequentes

**E quando um arquivo falha?** O documento recebe `status: "failed"`, um `errorCode` e `retryable`, e não é cobrado. Os outros continuam e a execução termina com sucesso. Uma entrada sem nenhum documento falha logo com a mensagem `Input error`.

**Quais são os limites?** 50 MB por arquivo (até 100 MB), 2.000 páginas, 600 s por documento e 10.000 URLs por execução. Use `maxPages` para converter só as primeiras páginas de digitalizações grandes.

**Dá para agendar ou monitorar uma pasta?** Não há modo de monitoramento. Cada execução converte os documentos informados. Para rotinas, crie um **Schedule** ou **Task** da Apify apontando para um key-value store ou lista de URLs, ou dispare execuções por webhook.

**De onde vêm os documentos? É legal?** Só das URLs, uploads ou key-value stores que você informa; o Actor não navega nem busca na web. Você é responsável por ter o direito de processar os arquivos. Eles ficam só no armazenamento da sua execução, conforme a retenção da sua conta, e não são usados para treinamento.

**Quais formatos de saída?** Markdown (padrão) ou texto, no dataset (exportável em JSON, CSV, Excel, XML ou RSS) e como arquivo `.md`/`.txt` por documento. Chunks para RAG são opcionais.

**Integra com o quê?** API e SDKs da Apify (Python, JavaScript), `run-sync-get-dataset-items`, webhooks, Zapier, Make, n8n, loaders da Apify no LangChain e no LlamaIndex, e o servidor MCP da Apify para agentes de IA.

**Por que usou OCR? Funciona em português?** No modo `auto`, só quando a página não tem texto, tem pouquíssimo texto ou tem texto corrompido. Com `por`, a acentuação sai correta em digitalizações limpas (cerca de 95% de confiança média nos nossos testes).

**Licenças?** Apenas software livre permissivo em processo (MIT, Apache-2.0, BSD, HPND). O Pandoc (GPL-2.0+) roda como programa externo, sem modificações. Nenhum componente AGPL. Veja `NOTICE.md`.

#### Uso com agentes de IA (MCP) e Python

**MCP:** adicione este Actor como ferramenta no Claude Desktop, Cursor, VS Code ou outro cliente MCP pelo servidor MCP da Apify: `https://mcp.apify.com?tools=hanielazevedo/document-to-markdown-ocr`. O agente lê o schema de entrada, roda o Actor e lê o dataset.

**Python** (`pip install apify-client`): veja o exemplo na seção em inglês.

#### Outros Actors do Haniel

- [SAM.gov Scraper: Federal Contract Opportunities Monitor](https://apify.com/hanielazevedo/samgov-contract-opportunities): Novas oportunidades de contratos federais dos EUA no SAM.gov por NAICS, set-aside e órgão. Sem chave de API. US$ 2 por 1.000.
- [Consulta CNPJ em Lote: Brazil CNPJ Lookup (Receita Federal)](https://apify.com/hanielazevedo/brazil-cnpj-company-lookup): Consulta CNPJ em lote: razão social, CNAE, sócios (QSA), endereço, Simples Nacional e MEI. US$ 1,20 por 1.000 empresas.
- [Greenhouse, Lever & Ashby Jobs Scraper: New Jobs Monitor](https://apify.com/hanielazevedo/greenhouse-lever-ashby-new-jobs-monitor): Vagas de páginas de carreira Greenhouse, Lever e Ashby, com modo monitor que traz só as vagas novas. US$ 1,50 por 1.000 vagas.
- [Floor Plan Takeoff: PDF Room Areas, Wall LF & Door Counts](https://apify.com/hanielazevedo/floor-plan-takeoff): Levantamento de quantitativos de plantas em PDF vetorial: área dos ambientes, metros lineares de parede e portas. US$ 2,50 por prancha medida.

# Actor input Schema

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

Public http(s) links to documents: PDF, DOCX, ODT, RTF, EPUB, HTML, TXT/MD, PNG, JPG, TIFF, WEBP, BMP, GIF. Format is detected from the file content, not the extension. Private/internal addresses are blocked.

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

Upload one or more files (stored in your key-value store) or paste file URLs.

## `keyValueStoreId` (type: `string`):

Pick one of your key-value stores. Every record in it is converted (up to 1,000), unless you list specific keys below. The Actor gets read-only access to this store only.

## `keyValueStoreKeys` (type: `array`):

Optional: only convert these record keys from the selected key-value store.

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

Markdown keeps headings, lists, tables (GitHub-flavored) and links. Plain text strips all markup (tables become tab-separated).

## `ocrMode` (type: `string`):

auto = use the PDF text layer and OCR only pages that are scanned/image-only or have broken text (recommended). always = OCR every PDF page (for PDFs with garbage text layers). never = never OCR (scanned pages produce no text; images fail with NO_TEXT_FOUND). Each OCRed page is billed as one ocr-page event.

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

Tesseract language codes joined with +. Installed: eng, por, spa, fra, deu, ita, nld. Fewer languages = faster and more accurate.

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

Only convert the first N pages of each PDF / multi-page TIFF. Empty = all pages (hard limit 2,000). Useful to cap OCR cost.

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

Add heading-aware chunks (with heading path, character offsets and page range) to each item, ready for embedding.

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

Target maximum characters per chunk (about 4 characters per token).

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

Characters repeated from the end of the previous chunk.

## `includeContentInDataset` (type: `boolean`):

Put the Markdown/text into the dataset item. The full file is always saved to the key-value store (link in outputFileUrl). Turn off for very large documents if you only need the files.

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

Larger files are rejected with FILE_TOO_LARGE (not charged).

## `timeoutSecondsPerDocument` (type: `integer`):

Download + conversion time limit per document. If OCR runs out of time, the pages done so far are returned with a TIMEOUT_PARTIAL_OUTPUT warning.

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

How many documents are processed at the same time.

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

Advanced: records as "storeId/key" strings. Stores must be readable by the run (prefer the key-value store picker above).

## Actor input object example

```json
{
  "urls": [
    "https://arxiv.org/pdf/1706.03762v7",
    "https://en.wikipedia.org/wiki/Markdown"
  ],
  "outputFormat": "markdown",
  "ocrMode": "auto",
  "ocrLanguages": "eng+por",
  "enableChunking": false,
  "chunkSize": 1500,
  "chunkOverlap": 150,
  "includeContentInDataset": true,
  "maxFileSizeMb": 50,
  "timeoutSecondsPerDocument": 600,
  "maxConcurrency": 2
}
```

# Actor output Schema

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

One dataset item per input document: status, detected format, page and OCR counts, the extracted Markdown or text (or a link to the file when it is too large), optional chunks, warnings and error code. Failed documents are not charged.

## `files` (type: `string`):

Default key-value store keys. Each converted document is saved as NNNN-<name>.md or .txt (plus NNNN-<name>.chunks.json when chunks are too large for the dataset), alongside the OUTPUT summary.

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

OUTPUT record: documents total, succeeded, partial and failed, OCR pages used, characters extracted and whether the run stopped at your max cost.

# 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": [
        "https://arxiv.org/pdf/1706.03762v7",
        "https://en.wikipedia.org/wiki/Markdown"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("hanielazevedo/document-to-markdown-ocr").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": [
        "https://arxiv.org/pdf/1706.03762v7",
        "https://en.wikipedia.org/wiki/Markdown",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("hanielazevedo/document-to-markdown-ocr").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": [
    "https://arxiv.org/pdf/1706.03762v7",
    "https://en.wikipedia.org/wiki/Markdown"
  ]
}' |
apify call hanielazevedo/document-to-markdown-ocr --silent --output-dataset

```

## MCP server setup

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

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/sgRPAAkvYSJY39Tfd/builds/Gr0dBR2fxjyN7Avw9/openapi.json
