# PDF Text Extractor & Document Parser: DOCX, XLSX, OCR, Markdown (`yabanana99/pdf-word-excel-to-markdown`) Actor

Parse Word, PowerPoint, Excel and PDF documents into clean Markdown and structured JSON: tables extracted cell-by-cell, document metadata, and RAG-ready chunks with heading paths. Built-in OCR for scans and images.

- **URL**: https://apify.com/yabanana99/pdf-word-excel-to-markdown.md
- **Developed by:** [Daniele Giovane](https://apify.com/yabanana99) (community)
- **Categories:** AI, Developer tools, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 document processeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

**Turn PDF, Word, Excel and PowerPoint files into clean Markdown and structured JSON** — with **tables extracted cell by cell**, document metadata, and **RAG-ready chunks that carry their heading path**. Built for mixed document batches where you feed in `.docx`, `.pptx`, `.xlsx`, `.pdf` (and scans) and get back one consistent, embeddable record per file.

Scanned pages and images are handled with built-in **Tesseract OCR**, but OCR and "PDF to text" are table stakes — the reason to use this Actor is the **Office formats, the structured cell-level tables, and the chunking** that you would otherwise have to build and host yourself.

### Why this Actor

- **Office formats as first-class citizens.** `.docx` heading/list styles become Markdown, `.pptx` gives one section per slide, `.xlsx` turns every sheet into a real table. Most document parsers focus on PDFs and treat Office as an afterthought.
- **Tables as data, not as blurry text.** Tables come back as arrays of cell rows (`rows: [["Region","Q1","Q2"], ...]`) *and* as a Markdown table. Merged Word cells are de-duplicated, spreadsheet formula cells without a cached value are flagged.
- **RAG chunks that keep their place.** Each chunk carries a `heading_path` (e.g. `["1 Introduction", "1.2 Scope"]`) and a `cl100k_base` token count. Headings and body-less sections (indexes, tables of contents) are preserved, not dropped. Heading levels are worked out across the **whole document**, not page by page, so a section on page 12 still nests under the title on page 1.
- **Runs in 1 GB, so you can run 16 at once.** Measured peak memory is 99 MB on the heaviest case tried (a 9-page PDF with OCR forced on every page). That is not a vanity metric: it is what decides how many runs fit inside your account's memory limit, and the Actor-start fee you pay is charged once up to 1 GB and then **once per extra GB** — so a parser that demands 4 GB costs you four start fees per run before it has read a byte.

Everything runs inside the Actor container. The only network traffic is downloading the source URLs you provide — see **[Your data](#your-data)**.

### Supported inputs

**PDF, DOCX, PPTX, XLSX, PNG, JPG, TIFF, TXT, CSV, MD, HTML.**

- **DOCX** — paragraphs with heading/list styles mapped to Markdown, inline tables (merged cells de-duplicated), core properties.
- **PPTX** — one section per slide (slide title becomes a heading), bullet text, embedded tables.
- **XLSX** — every sheet becomes a table plus a Markdown table; runaway sheets are capped (256 columns, 200k cells) and uncached formulas are flagged.
- **PDF** — native text extraction (PyMuPDF) with multi-column reading order, font-size heading detection, table detection (pdfplumber), and OCR fallback for scanned pages.
- **PNG / JPG / TIFF** — OCR (multi-frame TIFFs frame by frame).
- **TXT / CSV / MD / HTML** — plain text passthrough, CSV to table, Markdown kept as-is, HTML reduced to text (tags/scripts stripped with the stdlib parser).

Corrupted files, password-protected documents, oversized files and unreachable URLs never crash the run — the record is emitted with a `warnings` array explaining what happened.

### Input

Provide each document as a public URL, a base64 payload, or a key-value-store record. Up to **500 sources per run** — extra sources are skipped and reported both in the log and in a final `"source": "_meta"` dataset record stating how many were dropped.

```json
{
    "sources": [
        { "url": "https://arxiv.org/pdf/1706.03762" },
        { "base64": "JVBERi0xLjQK...", "filename": "invoice.pdf" },
        { "key": "uploaded-report.docx", "store_id": "abcDEF123" }
    ],
    "output_format": "all",
    "ocr": "auto",
    "ocr_language": "eng+ita",
    "extract_tables": true,
    "chunking": "by_headings",
    "chunk_size_tokens": 512,
    "chunk_overlap_tokens": 64,
    "max_file_size_mb": 50,
    "concurrency": 2
}
```

| Option | Values | Default | Notes |
| --- | --- | --- | --- |
| `output_format` | `text` / `markdown` / `json` / `all` | `all` | Controls which textual fields are populated |
| `ocr` | `auto` / `force` / `off` | `auto` | `auto` OCRs text-less and scanned-looking pages (see FAQ) |
| `ocr_language` | Tesseract codes, e.g. `eng+ita` | `eng+ita` | Languages not installed are skipped with a warning |
| `extract_tables` | boolean | `true` | Structured tables as arrays of cell rows |
| `chunking` | `off` / `by_headings` / `fixed_tokens` | `by_headings` | `by_headings` keeps a `heading_path` per chunk |
| `chunk_size_tokens` | 32–8192 | `512` | Oversized sections are sub-split |
| `chunk_overlap_tokens` | ≥ 0 | `64` | Capped at half the chunk size |
| `max_file_size_mb` | 1–100 | `50` | Bigger files are skipped with a warning |
| `concurrency` | 1–10 | `2` | Documents processed in parallel. Office, CSV and image sources scale with this; **PDFs are parsed one at a time** whatever you set, because the PDF engine is not safe to call from several threads at once |

### Output

One dataset record per document:

```json
{
    "source": "https://example.com/report.pdf",
    "doc_type": "pdf",
    "n_pages": 12,
    "text": "Quarterly Report\n\nRevenue grew by...",
    "markdown": "# Quarterly Report\n\n## Revenue\n\nRevenue grew by...",
    "tables": [
        { "page": 3, "rows": [["Region", "Q1", "Q2"], ["EMEA", "1.2M", "1.4M"]] }
    ],
    "metadata": { "title": "Quarterly Report", "author": "Finance Team" },
    "chunks": [
        {
            "text": "Revenue\nRevenue grew by...",
            "n_tokens": 118,
            "index": 0,
            "heading_path": ["Quarterly Report", "Revenue"]
        }
    ],
    "warnings": [],
    "processing": { "ocr_used": false, "ocr_pages": 0, "seconds": 1.87 }
}
```

`n_pages` is the page count for PDFs, slide count for PPTX, sheet count for XLSX, frame count for images, and the page count declared in document properties for DOCX (`null` when unavailable). Records larger than 8 MB have their `text`, `markdown` and `chunks` truncated (with a warning) rather than failing the run.

### Pricing (pay per event)

| Event | Price | When it is charged |
| --- | --- | --- |
| `apify-actor-start` | $0.005 | Once per run, charged by the platform. Apify covers the compute cost of the first 5 seconds of every run. |
| `document-processed` | $0.004 | Once per document that produced text, Markdown or tables. Empty, failed or skipped documents are **not** charged |
| `ocr-page-processed` | $0.012 | Once per page/image frame that went through OCR **and produced text** — blank OCR results are not charged |

Examples (single run):

- A 20-page native (non-scanned) PDF: `$0.005 + $0.004` = **$0.009**.
- A 20-page fully scanned PDF: `$0.005 + $0.004 + 20 × $0.012` = **$0.249**.
- A batch of 100 DOCX files in one run: `$0.005 + 100 × $0.004` = **$0.405**.

OCR is the expensive part because it is the expensive part of the compute — if your documents are native Office/PDF files, you essentially only pay the per-document fee.

### Honest comparison with alternatives

OCR and Markdown output are **not** what makes this Actor special — several tools do them well and cheaply. Here is where this Actor does and does not make sense:

| Alternative | Honest trade-off |
| --- | --- |
| **memo23 / flat-rate OCR PDF Actors** (e.g. ~$0.005 per PDF, ~23 OCR languages, passwords, page ranges, ~20 parallel) | For **OCR-heavy PDF workloads these are excellent and often cheaper than this Actor** — if scanned PDFs in many languages are your main job, use them. This Actor wins when the batch is mixed Office documents and you need cell-level tables and heading-aware RAG chunks, not just OCR text. |
| **LLM parsers** (LlamaParse, Reducto, …) | Usually better on very complex layouts (nested tables, handwriting) but 5–50× more expensive per page, and your documents are sent to a third-party LLM. This Actor is deterministic, cheaper, and keeps content on-platform. |
| **`unstructured` (open source)** | Very capable and free, but heavy to deploy (large image, many system deps) and you still need hosting, retries and an API around it. |
| **Running PDFium / Tesseract yourself** | Same underlying engines — this Actor is the glue (format detection, reading order, table normalization, chunking, warnings, storage and scheduling). If you already run that infrastructure, self-hosting is cheaper. |
| **Simple "PDF to text" Actors** | Cheaper if you only need raw text from native PDFs. Doc2Data earns its price on Office formats, structured tables and RAG chunks. |

### Your data

- **Processed in memory only.** Documents are parsed inside the Actor's container and are never written to any store except the dataset records you receive.
- **No document content is logged.** Logs contain source labels, page counts and warnings — never the extracted text.
- **No external API or LLM calls.** All parsing, OCR and tokenization run locally in the container, with permissively licensed engines: PDFium (BSD-3-Clause, the PDF engine from Chromium) and Tesseract (Apache-2.0). The `cl100k_base` tokenizer vocabulary is baked into the Docker image at build time, so **nothing is downloaded at run time** — the only outbound requests are fetching the source URLs *you* provide.
- **The author never sees your data.** There is no telemetry and no hidden persistence.
- **SSRF-guarded fetching.** Requests to internal/private addresses are refused — source URLs that resolve to private, loopback or link-local addresses are rejected, including across redirects.

### FAQ

**Which OCR languages are available?**
The image ships with English (`eng`), Italian (`ita`), French (`fra`), German (`deu`), Spanish (`spa`), Portuguese (`por`), Dutch (`nld`) and Polish (`pol`); combine them with `+`, e.g. `eng+ita`. Requested languages that are not installed are skipped and reported in `warnings`. Need another language preinstalled? Open an issue on the Actor.

**How are scanned PDFs detected?**
In `auto` mode a page is OCRed when it has almost no extractable text (fewer than 25 characters), or when it looks scanned — a raster image covering more than 15% of the page area with fewer than 200 characters of extractable text. A scan with a small digital header or timestamp is therefore still OCRed. Pages with under 25 characters that cannot be OCRed (OCR off, Tesseract unavailable, cap reached) are flagged in `warnings`. Use `force` to OCR every page, `off` to never OCR. OCR is capped at 300 pages per document, and only OCR pages that produce text are charged.

**Are password-protected documents supported?**
No — they are skipped with a `warnings` entry, and are not charged.

**What if a document hangs?**
Each document has a 180-second budget and each OCR page a 60-second one. OCR runs as a separate process, so a page that hangs is **actually killed** and the run continues; the record says so in `warnings`. A parse stuck inside the PDF engine itself still cannot be force-killed mid-call, so the input caps (file size, cell/column limits, 300 OCR pages per document, 500 sources per run) remain the hard protection. Timed-out documents are not charged.

**Is my data sent to any third party?**
No. See [Your data](#your-data). All parsing, OCR and tokenization happen inside the Actor container; the only outbound requests download the source URLs you provide.

**What tokenizer do chunk token counts use?**
OpenAI's `cl100k_base` (via `tiktoken`), baked into the image. If it is somehow unavailable at run time, a `len/4` approximation is used and a warning is added to the record — token counts are never silently fabricated.

**Does `output_format: "json"` still contain my text?**
`json` omits the top-level `text` and `markdown` fields, but `chunks` are the RAG payload and therefore still contain the chunk text. Set `chunking: "off"` if you want structured data (tables, metadata) with no text at all.

**What happens with very large spreadsheets?**
Sheets are capped at 256 columns, 200,000 cells and 10,000 rows per table (each with a warning), and Markdown tables render at most 200 rows to keep records manageable.

**Can I process files I upload to Apify storage?**
Yes — upload the file to a key-value store, then reference it as `{ "key": "my-file.pdf", "store_id": "..." }` (omit `store_id` to use the run's default store).

### You may also need

Same author, same principles: you pay for work that was actually done, nothing is stored outside your own run, and no third-party API ever sees your data.

- **[Email List Cleaner](https://apify.com/yabanana99/email-list-cleaner-verifier)** — deduplicate, verify and segment a whole email list from CSV or XLSX.
- **[Phone Number Cleaner](https://apify.com/yabanana99/phone-number-cleaner-validator)** — validate and format phone numbers in bulk to E.164, with line type and carrier.

# Actor input Schema

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

Documents to convert. Each item is an object with exactly one of: <b>url</b> (http/https link to the file), <b>base64</b> (base64-encoded file content, data URIs accepted), or <b>key</b> (record key in a key-value store, with optional <b>store\_id</b>). Add an optional <b>filename</b> (e.g. <code>report.pdf</code>) to help format detection when the URL has no extension. Supported formats: PDF, DOCX, PPTX, XLSX, PNG, JPG, TIFF, TXT, CSV, MD, HTML. Up to 500 sources per run.

## `output_format` (type: `string`):

Which textual representations to include in each dataset record. <b>text</b> = plain text only, <b>markdown</b> = Markdown only, <b>json</b> = structured data only (tables, metadata, chunks), <b>all</b> = everything. Tables, metadata and chunks are controlled by their own options and are always part of the record.

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

<b>auto</b> runs OCR on pages with almost no extractable text (fewer than 25 characters) and on scanned-looking pages (a raster image covering over 15% of the page with fewer than 200 characters of text), <b>force</b> runs OCR on every page, <b>off</b> disables OCR entirely. Only OCR pages that produce text are charged.

## `ocr_language` (type: `string`):

Tesseract language codes joined with <code>+</code>, e.g. <code>eng+ita</code>. The Docker image ships with English, Italian, French, German, Spanish, Portuguese, Dutch and Polish. Requested languages that are not installed are skipped with a warning.

## `extract_tables` (type: `boolean`):

Detect tables and include them in the structured <code>tables</code> field as arrays of cell rows. For PDFs this runs a dedicated table-detection pass (pdfplumber).

## `chunking` (type: `string`):

How to split the document into RAG-ready chunks. <b>by\_headings</b> splits at Markdown headings and keeps a <code>heading\_path</code> per chunk (oversized sections are sub-split), <b>fixed\_tokens</b> uses fixed-size token windows with overlap, <b>off</b> disables chunking.

## `chunk_size_tokens` (type: `integer`):

Maximum tokens per chunk (cl100k\_base tokenizer). Range 32–8192.

## `chunk_overlap_tokens` (type: `integer`):

Token overlap between consecutive chunks when a section is sub-split. Capped at half the chunk size.

## `max_file_size_mb` (type: `integer`):

Files larger than this are skipped with a warning instead of failing the run.

## `concurrency` (type: `integer`):

How many documents to process in parallel. Parsing is CPU-bound (Python GIL), so values above 4 mainly pay off on OCR-heavy batches; higher values also use more memory. Range 1–10.

## Actor input object example

```json
{
  "sources": [
    {
      "base64": "JVBERi0xLjcKJcK1wrYKJSBXcml0dGVuIGJ5IE11UERGIDEuMjguMgoKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvSW5mbzw8L1Byb2R1Y2VyKE11UERGIDEuMjguMik+Pj4+CmVuZG9iagoKMiAwIG9iago8PC9UeXBlL1BhZ2VzL0NvdW50IDEvS2lkc1s0IDAgUl0+PgplbmRvYmoKCjMgMCBvYmoKPDwvRm9udDw8L2hlbHYgNSAwIFI+Pj4+CmVuZG9iagoKNCAwIG9iago8PC9UeXBlL1BhZ2UvTWVkaWFCb3hbMCAwIDU5NSA4NDJdL1JvdGF0ZSAwL1Jlc291cmNlcyAzIDAgUi9QYXJlbnQgMiAwIFIvQ29udGVudHNbNiAwIFIgNyAwIFIgOCAwIFIgOSAwIFIgMTAgMCBSIDExIDAgUiAxMiAwIFJdPj4KZW5kb2JqCgo1IDAgb2JqCjw8L1R5cGUvRm9udC9TdWJ0eXBlL1R5cGUxL0Jhc2VGb250L0hlbHZldGljYS9FbmNvZGluZy9XaW5BbnNpRW5jb2Rpbmc+PgplbmRvYmoKCjYgMCBvYmoKPDwvTGVuZ3RoIDk0L0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp42hXIsQ5AUAxA0b1f0T/QlrYkYpBYbJJuYvEQA4PF93tyl5sDD/QBjJRjdEFXwbihOPfrRa4xDpxbZVdjF69MXSx5I6SSn+z4VWj1LKWxbdmSabfECEPABB8hgBXZCmVuZHN0cmVhbQplbmRvYmoKCjcgMCBvYmoKPDwvTGVuZ3RoIDEyNy9GaWx0ZXIvRmxhdGVEZWNvZGU+PgpzdHJlYW0KeNpFjDEKQkEMRPucIjdwk91MFMRCsLET0onV/7tYaGHj+c3fRqZ5MG+GPnQOEi4ZYVd2UY437Z799WURjsH3ozXscfCqxSsEqxcsMC1oGKhuWGHo3ma/mWW6npTLpG2ffpO0c+M6zfFv8skASH4t80eT+ukRV7oE3egHLiklCgplbmRzdHJlYW0KZW5kb2JqCgo4IDAgb2JqCjw8L0xlbmd0aCAxMjEvRmlsdGVyL0ZsYXRlRGVjb2RlPj4Kc3RyZWFtCnjaHYyxCgJBDET7fEX+wEtud3IBsRBs7IR0YuOyi4UWNn6/2UtgmDdDQl86BwkvucKmDC8cHzq8+vvHIhyD70cImhnE3FZd0g1TPKfHwJwGR0fVtrduBVt2HSO12pZc86LPP5lMyjy57Lqaaz894kqXoBv9Ab+qIfsKZW5kc3RyZWFtCmVuZG9iagoKOSAwIG9iago8PC9MZW5ndGggOTEvRmlsdGVyL0ZsYXRlRGVjb2RlPj4Kc3RyZWFtCnjaVYsxCoBADAT7vCI/8BLNBkEsBBs7IZ1Y3mGhhY3v9zqR6YYZumkKEk4VYVeGKcdFzZHPh0U4Cm+DKQyOHgVZ04cZMnrvvP3ZWjvqkd1g4x4LzUErvfuzF40KZW5kc3RyZWFtCmVuZG9iagoKMTAgMCBvYmoKPDwvTGVuZ3RoIDgzL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp42uMq5HIK4TJUMABCQwVzIwUzYyOFkFwu/YzUnDIFQ0OFkDSFaBuTVLM0cyNzEzMLIwN0aGxobGSMIWKUbGxibGBsYBcb4sXlGsIVyAUAvRwWlQplbmRzdHJlYW0KZW5kb2JqCgoxMSAwIG9iago8PC9MZW5ndGggODIvRmlsdGVyL0ZsYXRlRGVjb2RlPj4Kc3RyZWFtCnja4yrkcgrhMlQwAEJDBXMjBTNDI4WQXC79jNScMgVDQ4WQNIVoG1NjszRzU3MTMwsjA0xobGlsgS5ilGxsaGxqbGAXG+LF5RrCFcgFALKRFnkKZW5kc3RyZWFtCmVuZG9iagoKMTIgMCBvYmoKPDwvTGVuZ3RoIDgzL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp42uMq5HIK4TJUMABCQwVzIwVTSyOFkFwu/YzUnDIFQ0OFkDSFaBtTczNTc2NzEyMDTGhsaGxibIwmYmqUbGxgbGRsYBcb4sXlGsIVyAUApN0WNwplbmRzdHJlYW0KZW5kb2JqCgp4cmVmCjAgMTMKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDQyIDAwMDAwIG4gCjAwMDAwMDAxMjAgMDAwMDAgbiAKMDAwMDAwMDE3MiAwMDAwMCBuIAowMDAwMDAwMjEzIDAwMDAwIG4gCjAwMDAwMDAzNTkgMDAwMDAgbiAKMDAwMDAwMDQ0OCAwMDAwMCBuIAowMDAwMDAwNjEwIDAwMDAwIG4gCjAwMDAwMDA4MDYgMDAwMDAgbiAKMDAwMDAwMDk5NiAwMDAwMCBuIAowMDAwMDAxMTU1IDAwMDAwIG4gCjAwMDAwMDEzMDcgMDAwMDAgbiAKMDAwMDAwMTQ1OCAwMDAwMCBuIAoKdHJhaWxlcgo8PC9TaXplIDEzL1Jvb3QgMSAwIFIvSURbPEMzQjZDMkE4QzM5MkMyQjlDMzk4NUFDMkIxNTY0MkMyPjwwQjY5OTAzRDVBMjE3MTI1QzI2NTIyRkM2QjkxRDNDNj5dPj4Kc3RhcnR4cmVmCjE2MTAKJSVFT0YK",
      "filename": "sample-report.pdf"
    }
  ],
  "output_format": "all",
  "ocr": "auto",
  "ocr_language": "eng+ita",
  "extract_tables": true,
  "chunking": "by_headings",
  "chunk_size_tokens": 512,
  "chunk_overlap_tokens": 64,
  "max_file_size_mb": 50,
  "concurrency": 2
}
```

# Actor output Schema

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

One record per source document: text, markdown, structured tables, metadata, RAG chunks, warnings and processing info.

## `documentsCsv` (type: `string`):

The same records flattened to CSV, for spreadsheets.

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

Browse the parsed documents in the Apify Console.

# 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": [
        {
            "base64": "JVBERi0xLjcKJcK1wrYKJSBXcml0dGVuIGJ5IE11UERGIDEuMjguMgoKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvSW5mbzw8L1Byb2R1Y2VyKE11UERGIDEuMjguMik+Pj4+CmVuZG9iagoKMiAwIG9iago8PC9UeXBlL1BhZ2VzL0NvdW50IDEvS2lkc1s0IDAgUl0+PgplbmRvYmoKCjMgMCBvYmoKPDwvRm9udDw8L2hlbHYgNSAwIFI+Pj4+CmVuZG9iagoKNCAwIG9iago8PC9UeXBlL1BhZ2UvTWVkaWFCb3hbMCAwIDU5NSA4NDJdL1JvdGF0ZSAwL1Jlc291cmNlcyAzIDAgUi9QYXJlbnQgMiAwIFIvQ29udGVudHNbNiAwIFIgNyAwIFIgOCAwIFIgOSAwIFIgMTAgMCBSIDExIDAgUiAxMiAwIFJdPj4KZW5kb2JqCgo1IDAgb2JqCjw8L1R5cGUvRm9udC9TdWJ0eXBlL1R5cGUxL0Jhc2VGb250L0hlbHZldGljYS9FbmNvZGluZy9XaW5BbnNpRW5jb2Rpbmc+PgplbmRvYmoKCjYgMCBvYmoKPDwvTGVuZ3RoIDk0L0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp42hXIsQ5AUAxA0b1f0T/QlrYkYpBYbJJuYvEQA4PF93tyl5sDD/QBjJRjdEFXwbihOPfrRa4xDpxbZVdjF69MXSx5I6SSn+z4VWj1LKWxbdmSabfECEPABB8hgBXZCmVuZHN0cmVhbQplbmRvYmoKCjcgMCBvYmoKPDwvTGVuZ3RoIDEyNy9GaWx0ZXIvRmxhdGVEZWNvZGU+PgpzdHJlYW0KeNpFjDEKQkEMRPucIjdwk91MFMRCsLET0onV/7tYaGHj+c3fRqZ5MG+GPnQOEi4ZYVd2UY437Z799WURjsH3ozXscfCqxSsEqxcsMC1oGKhuWGHo3ma/mWW6npTLpG2ffpO0c+M6zfFv8skASH4t80eT+ukRV7oE3egHLiklCgplbmRzdHJlYW0KZW5kb2JqCgo4IDAgb2JqCjw8L0xlbmd0aCAxMjEvRmlsdGVyL0ZsYXRlRGVjb2RlPj4Kc3RyZWFtCnjaHYyxCgJBDET7fEX+wEtud3IBsRBs7IR0YuOyi4UWNn6/2UtgmDdDQl86BwkvucKmDC8cHzq8+vvHIhyD70cImhnE3FZd0g1TPKfHwJwGR0fVtrduBVt2HSO12pZc86LPP5lMyjy57Lqaaz894kqXoBv9Ab+qIfsKZW5kc3RyZWFtCmVuZG9iagoKOSAwIG9iago8PC9MZW5ndGggOTEvRmlsdGVyL0ZsYXRlRGVjb2RlPj4Kc3RyZWFtCnjaVYsxCoBADAT7vCI/8BLNBkEsBBs7IZ1Y3mGhhY3v9zqR6YYZumkKEk4VYVeGKcdFzZHPh0U4Cm+DKQyOHgVZ04cZMnrvvP3ZWjvqkd1g4x4LzUErvfuzF40KZW5kc3RyZWFtCmVuZG9iagoKMTAgMCBvYmoKPDwvTGVuZ3RoIDgzL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp42uMq5HIK4TJUMABCQwVzIwUzYyOFkFwu/YzUnDIFQ0OFkDSFaBuTVLM0cyNzEzMLIwN0aGxobGSMIWKUbGxibGBsYBcb4sXlGsIVyAUAvRwWlQplbmRzdHJlYW0KZW5kb2JqCgoxMSAwIG9iago8PC9MZW5ndGggODIvRmlsdGVyL0ZsYXRlRGVjb2RlPj4Kc3RyZWFtCnja4yrkcgrhMlQwAEJDBXMjBTNDI4WQXC79jNScMgVDQ4WQNIVoG1NjszRzU3MTMwsjA0xobGlsgS5ilGxsaGxqbGAXG+LF5RrCFcgFALKRFnkKZW5kc3RyZWFtCmVuZG9iagoKMTIgMCBvYmoKPDwvTGVuZ3RoIDgzL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp42uMq5HIK4TJUMABCQwVzIwVTSyOFkFwu/YzUnDIFQ0OFkDSFaBtTczNTc2NzEyMDTGhsaGxibIwmYmqUbGxgbGRsYBcb4sXlGsIVyAUApN0WNwplbmRzdHJlYW0KZW5kb2JqCgp4cmVmCjAgMTMKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDQyIDAwMDAwIG4gCjAwMDAwMDAxMjAgMDAwMDAgbiAKMDAwMDAwMDE3MiAwMDAwMCBuIAowMDAwMDAwMjEzIDAwMDAwIG4gCjAwMDAwMDAzNTkgMDAwMDAgbiAKMDAwMDAwMDQ0OCAwMDAwMCBuIAowMDAwMDAwNjEwIDAwMDAwIG4gCjAwMDAwMDA4MDYgMDAwMDAgbiAKMDAwMDAwMDk5NiAwMDAwMCBuIAowMDAwMDAxMTU1IDAwMDAwIG4gCjAwMDAwMDEzMDcgMDAwMDAgbiAKMDAwMDAwMTQ1OCAwMDAwMCBuIAoKdHJhaWxlcgo8PC9TaXplIDEzL1Jvb3QgMSAwIFIvSURbPEMzQjZDMkE4QzM5MkMyQjlDMzk4NUFDMkIxNTY0MkMyPjwwQjY5OTAzRDVBMjE3MTI1QzI2NTIyRkM2QjkxRDNDNj5dPj4Kc3RhcnR4cmVmCjE2MTAKJSVFT0YK",
            "filename": "sample-report.pdf"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("yabanana99/pdf-word-excel-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 = { "sources": [{
            "base64": "JVBERi0xLjcKJcK1wrYKJSBXcml0dGVuIGJ5IE11UERGIDEuMjguMgoKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvSW5mbzw8L1Byb2R1Y2VyKE11UERGIDEuMjguMik+Pj4+CmVuZG9iagoKMiAwIG9iago8PC9UeXBlL1BhZ2VzL0NvdW50IDEvS2lkc1s0IDAgUl0+PgplbmRvYmoKCjMgMCBvYmoKPDwvRm9udDw8L2hlbHYgNSAwIFI+Pj4+CmVuZG9iagoKNCAwIG9iago8PC9UeXBlL1BhZ2UvTWVkaWFCb3hbMCAwIDU5NSA4NDJdL1JvdGF0ZSAwL1Jlc291cmNlcyAzIDAgUi9QYXJlbnQgMiAwIFIvQ29udGVudHNbNiAwIFIgNyAwIFIgOCAwIFIgOSAwIFIgMTAgMCBSIDExIDAgUiAxMiAwIFJdPj4KZW5kb2JqCgo1IDAgb2JqCjw8L1R5cGUvRm9udC9TdWJ0eXBlL1R5cGUxL0Jhc2VGb250L0hlbHZldGljYS9FbmNvZGluZy9XaW5BbnNpRW5jb2Rpbmc+PgplbmRvYmoKCjYgMCBvYmoKPDwvTGVuZ3RoIDk0L0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp42hXIsQ5AUAxA0b1f0T/QlrYkYpBYbJJuYvEQA4PF93tyl5sDD/QBjJRjdEFXwbihOPfrRa4xDpxbZVdjF69MXSx5I6SSn+z4VWj1LKWxbdmSabfECEPABB8hgBXZCmVuZHN0cmVhbQplbmRvYmoKCjcgMCBvYmoKPDwvTGVuZ3RoIDEyNy9GaWx0ZXIvRmxhdGVEZWNvZGU+PgpzdHJlYW0KeNpFjDEKQkEMRPucIjdwk91MFMRCsLET0onV/7tYaGHj+c3fRqZ5MG+GPnQOEi4ZYVd2UY437Z799WURjsH3ozXscfCqxSsEqxcsMC1oGKhuWGHo3ma/mWW6npTLpG2ffpO0c+M6zfFv8skASH4t80eT+ukRV7oE3egHLiklCgplbmRzdHJlYW0KZW5kb2JqCgo4IDAgb2JqCjw8L0xlbmd0aCAxMjEvRmlsdGVyL0ZsYXRlRGVjb2RlPj4Kc3RyZWFtCnjaHYyxCgJBDET7fEX+wEtud3IBsRBs7IR0YuOyi4UWNn6/2UtgmDdDQl86BwkvucKmDC8cHzq8+vvHIhyD70cImhnE3FZd0g1TPKfHwJwGR0fVtrduBVt2HSO12pZc86LPP5lMyjy57Lqaaz894kqXoBv9Ab+qIfsKZW5kc3RyZWFtCmVuZG9iagoKOSAwIG9iago8PC9MZW5ndGggOTEvRmlsdGVyL0ZsYXRlRGVjb2RlPj4Kc3RyZWFtCnjaVYsxCoBADAT7vCI/8BLNBkEsBBs7IZ1Y3mGhhY3v9zqR6YYZumkKEk4VYVeGKcdFzZHPh0U4Cm+DKQyOHgVZ04cZMnrvvP3ZWjvqkd1g4x4LzUErvfuzF40KZW5kc3RyZWFtCmVuZG9iagoKMTAgMCBvYmoKPDwvTGVuZ3RoIDgzL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp42uMq5HIK4TJUMABCQwVzIwUzYyOFkFwu/YzUnDIFQ0OFkDSFaBuTVLM0cyNzEzMLIwN0aGxobGSMIWKUbGxibGBsYBcb4sXlGsIVyAUAvRwWlQplbmRzdHJlYW0KZW5kb2JqCgoxMSAwIG9iago8PC9MZW5ndGggODIvRmlsdGVyL0ZsYXRlRGVjb2RlPj4Kc3RyZWFtCnja4yrkcgrhMlQwAEJDBXMjBTNDI4WQXC79jNScMgVDQ4WQNIVoG1NjszRzU3MTMwsjA0xobGlsgS5ilGxsaGxqbGAXG+LF5RrCFcgFALKRFnkKZW5kc3RyZWFtCmVuZG9iagoKMTIgMCBvYmoKPDwvTGVuZ3RoIDgzL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp42uMq5HIK4TJUMABCQwVzIwVTSyOFkFwu/YzUnDIFQ0OFkDSFaBtTczNTc2NzEyMDTGhsaGxibIwmYmqUbGxgbGRsYBcb4sXlGsIVyAUApN0WNwplbmRzdHJlYW0KZW5kb2JqCgp4cmVmCjAgMTMKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDQyIDAwMDAwIG4gCjAwMDAwMDAxMjAgMDAwMDAgbiAKMDAwMDAwMDE3MiAwMDAwMCBuIAowMDAwMDAwMjEzIDAwMDAwIG4gCjAwMDAwMDAzNTkgMDAwMDAgbiAKMDAwMDAwMDQ0OCAwMDAwMCBuIAowMDAwMDAwNjEwIDAwMDAwIG4gCjAwMDAwMDA4MDYgMDAwMDAgbiAKMDAwMDAwMDk5NiAwMDAwMCBuIAowMDAwMDAxMTU1IDAwMDAwIG4gCjAwMDAwMDEzMDcgMDAwMDAgbiAKMDAwMDAwMTQ1OCAwMDAwMCBuIAoKdHJhaWxlcgo8PC9TaXplIDEzL1Jvb3QgMSAwIFIvSURbPEMzQjZDMkE4QzM5MkMyQjlDMzk4NUFDMkIxNTY0MkMyPjwwQjY5OTAzRDVBMjE3MTI1QzI2NTIyRkM2QjkxRDNDNj5dPj4Kc3RhcnR4cmVmCjE2MTAKJSVFT0YK",
            "filename": "sample-report.pdf",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("yabanana99/pdf-word-excel-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 '{
  "sources": [
    {
      "base64": "JVBERi0xLjcKJcK1wrYKJSBXcml0dGVuIGJ5IE11UERGIDEuMjguMgoKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFIvSW5mbzw8L1Byb2R1Y2VyKE11UERGIDEuMjguMik+Pj4+CmVuZG9iagoKMiAwIG9iago8PC9UeXBlL1BhZ2VzL0NvdW50IDEvS2lkc1s0IDAgUl0+PgplbmRvYmoKCjMgMCBvYmoKPDwvRm9udDw8L2hlbHYgNSAwIFI+Pj4+CmVuZG9iagoKNCAwIG9iago8PC9UeXBlL1BhZ2UvTWVkaWFCb3hbMCAwIDU5NSA4NDJdL1JvdGF0ZSAwL1Jlc291cmNlcyAzIDAgUi9QYXJlbnQgMiAwIFIvQ29udGVudHNbNiAwIFIgNyAwIFIgOCAwIFIgOSAwIFIgMTAgMCBSIDExIDAgUiAxMiAwIFJdPj4KZW5kb2JqCgo1IDAgb2JqCjw8L1R5cGUvRm9udC9TdWJ0eXBlL1R5cGUxL0Jhc2VGb250L0hlbHZldGljYS9FbmNvZGluZy9XaW5BbnNpRW5jb2Rpbmc+PgplbmRvYmoKCjYgMCBvYmoKPDwvTGVuZ3RoIDk0L0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp42hXIsQ5AUAxA0b1f0T/QlrYkYpBYbJJuYvEQA4PF93tyl5sDD/QBjJRjdEFXwbihOPfrRa4xDpxbZVdjF69MXSx5I6SSn+z4VWj1LKWxbdmSabfECEPABB8hgBXZCmVuZHN0cmVhbQplbmRvYmoKCjcgMCBvYmoKPDwvTGVuZ3RoIDEyNy9GaWx0ZXIvRmxhdGVEZWNvZGU+PgpzdHJlYW0KeNpFjDEKQkEMRPucIjdwk91MFMRCsLET0onV/7tYaGHj+c3fRqZ5MG+GPnQOEi4ZYVd2UY437Z799WURjsH3ozXscfCqxSsEqxcsMC1oGKhuWGHo3ma/mWW6npTLpG2ffpO0c+M6zfFv8skASH4t80eT+ukRV7oE3egHLiklCgplbmRzdHJlYW0KZW5kb2JqCgo4IDAgb2JqCjw8L0xlbmd0aCAxMjEvRmlsdGVyL0ZsYXRlRGVjb2RlPj4Kc3RyZWFtCnjaHYyxCgJBDET7fEX+wEtud3IBsRBs7IR0YuOyi4UWNn6/2UtgmDdDQl86BwkvucKmDC8cHzq8+vvHIhyD70cImhnE3FZd0g1TPKfHwJwGR0fVtrduBVt2HSO12pZc86LPP5lMyjy57Lqaaz894kqXoBv9Ab+qIfsKZW5kc3RyZWFtCmVuZG9iagoKOSAwIG9iago8PC9MZW5ndGggOTEvRmlsdGVyL0ZsYXRlRGVjb2RlPj4Kc3RyZWFtCnjaVYsxCoBADAT7vCI/8BLNBkEsBBs7IZ1Y3mGhhY3v9zqR6YYZumkKEk4VYVeGKcdFzZHPh0U4Cm+DKQyOHgVZ04cZMnrvvP3ZWjvqkd1g4x4LzUErvfuzF40KZW5kc3RyZWFtCmVuZG9iagoKMTAgMCBvYmoKPDwvTGVuZ3RoIDgzL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp42uMq5HIK4TJUMABCQwVzIwUzYyOFkFwu/YzUnDIFQ0OFkDSFaBuTVLM0cyNzEzMLIwN0aGxobGSMIWKUbGxibGBsYBcb4sXlGsIVyAUAvRwWlQplbmRzdHJlYW0KZW5kb2JqCgoxMSAwIG9iago8PC9MZW5ndGggODIvRmlsdGVyL0ZsYXRlRGVjb2RlPj4Kc3RyZWFtCnja4yrkcgrhMlQwAEJDBXMjBTNDI4WQXC79jNScMgVDQ4WQNIVoG1NjszRzU3MTMwsjA0xobGlsgS5ilGxsaGxqbGAXG+LF5RrCFcgFALKRFnkKZW5kc3RyZWFtCmVuZG9iagoKMTIgMCBvYmoKPDwvTGVuZ3RoIDgzL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp42uMq5HIK4TJUMABCQwVzIwVTSyOFkFwu/YzUnDIFQ0OFkDSFaBtTczNTc2NzEyMDTGhsaGxibIwmYmqUbGxgbGRsYBcb4sXlGsIVyAUApN0WNwplbmRzdHJlYW0KZW5kb2JqCgp4cmVmCjAgMTMKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDQyIDAwMDAwIG4gCjAwMDAwMDAxMjAgMDAwMDAgbiAKMDAwMDAwMDE3MiAwMDAwMCBuIAowMDAwMDAwMjEzIDAwMDAwIG4gCjAwMDAwMDAzNTkgMDAwMDAgbiAKMDAwMDAwMDQ0OCAwMDAwMCBuIAowMDAwMDAwNjEwIDAwMDAwIG4gCjAwMDAwMDA4MDYgMDAwMDAgbiAKMDAwMDAwMDk5NiAwMDAwMCBuIAowMDAwMDAxMTU1IDAwMDAwIG4gCjAwMDAwMDEzMDcgMDAwMDAgbiAKMDAwMDAwMTQ1OCAwMDAwMCBuIAoKdHJhaWxlcgo8PC9TaXplIDEzL1Jvb3QgMSAwIFIvSURbPEMzQjZDMkE4QzM5MkMyQjlDMzk4NUFDMkIxNTY0MkMyPjwwQjY5OTAzRDVBMjE3MTI1QzI2NTIyRkM2QjkxRDNDNj5dPj4Kc3RhcnR4cmVmCjE2MTAKJSVFT0YK",
      "filename": "sample-report.pdf"
    }
  ]
}' |
apify call yabanana99/pdf-word-excel-to-markdown --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yabanana99/pdf-word-excel-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/I5TOQ5dN85MAMredL/builds/o6xF8tlJEVkHhTAnc/openapi.json
