# PDF, DOCX & XLSX to Text (`humble-echidna/document-to-text`) Actor

Convert PDF, Word (DOCX) and Excel (XLSX) files from URLs into plain text, Markdown for LLMs and RAG, tables as arrays, and metadata. Scanned PDF pages and images are read with OCR in 32 languages. Pay per document, plus per OCR page.

- **URL**: https://apify.com/humble-echidna/document-to-text.md
- **Developed by:** [Michael Costa](https://apify.com/humble-echidna) (community)
- **Categories:** Developer tools, AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 documents

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

### What does PDF, DOCX & XLSX to Text do?

**PDF, DOCX & XLSX to Text** converts **PDF, Word and Excel documents to text**. Give it document links, including
**scanned PDFs** and images (read with OCR); you get the **plain text**, the same content as **Markdown**, **tables
as rows**, and the document's metadata.

PDF text is read with a fast engine by default. Turn on **Extract PDF tables** when you need a PDF's tables as rows
and as Markdown tables; Word and Excel tables always come back.

Markdown keeps Word headings and lists, turns tables into Markdown tables and marks each PDF page, ready to chunk
for RAG or paste into an LLM prompt. The file type is read from the file itself, so links like
`.../download?id=123` work as long as they lead straight to the file.

**Try it in one click:** the input comes pre-filled with three small US government documents: IRS Form W-9 (PDF,
6 pages), a USPTO patent application template (DOCX) and a BLS survey template (XLSX, 3 sheets). That's 3
documents, about $0.015 (3 × $0.005, plus $0.00005 for the run start). **Then replace them with your own document
links.**

### What data does PDF, DOCX & XLSX to Text return?

| Field | Example | Notes |
|---|---|---|
| `text` | `Form W-9\n(Rev. March 2024)\n...` | The plain text, in reading order. |
| `markdown` | `<!-- page 1 -->\n\nForm W-9\n...` | Headings, lists and tables as Markdown; PDF pages marked. |
| `tables` | `[{"page": 1, "sheet": null, "index": 1, "rows": [[...]]}]` | Tables as rows: Word and Excel always, PDF with **Extract PDF tables** on; Excel cells keep numbers and true/false. |
| `format` | `pdf` | `pdf`, `docx`, `xlsx`, or the image format. |
| `title`, `author` | `Form W-9 (Rev. March 2024)`, `SE:W:CAR:MP` | From the document's metadata; `null` if none. |
| `createdAt`, `modifiedAt` | `2024-03-06T13:18:13Z` | |
| `pageCount`, `pagesExtracted` | `6`, `6` | PDF pages (DOCX: what Word saved; `null` for Excel). |
| `sheetNames` | `null` | Excel only. |
| `wordCount`, `tableCount` | `6272`, `0` | |
| `ocrPages`, `ocrConfidence` | `[]`, `null` | Pages read with OCR (the ones you pay for) and how sure OCR was. |
| `fileName`, `fileSizeBytes` | `fw9.pdf`, `140815` | |
| `truncated`, `truncatedReasons` | `false`, `[]` | Whether part of the document was left out, and why. |

One result per document. The full list is under [Output](#output).

### How much does it cost to convert PDF, Word and Excel to text?

You pay per document converted: **$5.00 per 1,000 documents** (up to 100 pages each),
plus **$0.30 per 1,000 table pages** (PDF pages read with **Extract PDF tables** on),
plus **$10.00 per 1,000 OCR pages** (each scanned PDF page or image read with OCR that had text),
plus $0.00005 each time a run starts.

**PDF tables are opt-in, and charged per page, because they are what costs to run.** Finding tables means reading
every character and line on the page with a layout-aware parser: on a 100-page PDF that took 15-20 times the compute
of reading its text. Plain text stays cheap for everyone who doesn't need rows; Word and Excel tables are always
included in the document price.

- **The example below:** 3 documents × $0.005 = $0.015, plus the start fee. With **Extract PDF tables** on, the W-9's
  6 pages add 6 × $0.0003 = $0.0018.
- **A 20-page PDF with tables:** $0.005 + 20 × $0.0003 = $0.011. **A 10-page scanned PDF:** one document and 10 OCR
  pages, $0.005 + $0.10 = $0.105.
- **A month, for example:** 200 documents: 200 × $0.005 = $1.00; 50 of them are 10-page PDFs read with tables, 500
  table pages × $0.0003 = $0.15; 20 are 3-page scans, 60 OCR pages × $0.01 = $0.60: **$1.75** plus the run starts.
- **Caps:** **Max documents per run** in the input, and **Maximum cost per run** in the run options. The run stops
  cleanly at whichever comes first. Table and OCR pages count against the maximum cost too: before a PDF is
  downloaded with tables on, the table pages it can need (up to **Max pages per PDF**) are reserved against what's
  left, and before a document's scanned pages are read, they are too (after the documents already in progress). If
  what's left doesn't cover them all, only the first pages are read and `truncatedReasons` says so. Each document
  is counted against the limit before it's downloaded (and the count given back if it fails), so a capped run never
  downloads documents or reads pages it can't return.

**Never charged:** pages with a text layer as OCR pages (they're never OCR'd), blank scanned pages, table pages when
**Extract PDF tables** is off (or when a PDF's structure needs the text-only fallback reader), and documents
that fail (password-protected without the right password, unsupported, too large, missing, no text even after OCR,
none of the pages in **Pages to read**, or disallowed by the site's `robots.txt`), nor their pages. If you don't want OCR, turn off **Read scanned pages with OCR**: scanned PDFs and
images are then reported, not charged.

The OCR page price is set from what OCR measurably costs to run: a scanned page is read at 300 DPI, and a dense page
of text costs several times more compute than a typical photo. For single images, our [Image to Text OCR](https://apify.com/humble-echidna/image-ocr) actor is
cheaper.

### How to convert a PDF, Word or Excel file to text

1. Open PDF, DOCX & XLSX to Text and click **Try for free** (or **Start** if you're signed in).
2. Put direct links to your files in **Document URLs**, one per line.
3. Optional: set **Max pages per PDF** or **Pages to read** (e.g. `1-10`), give a **PDF password**, turn **Extract
   PDF tables** or **Read scanned pages with OCR** on or off, and pick the **Languages** of scanned text.
4. Click **Start**, then open the **Output** tab and export as JSON, CSV or Excel.

### Example: a PDF, a Word file and an Excel workbook

The pre-filled input:

```json
{"urls": ["https://www.irs.gov/pub/irs-pdf/fw9.pdf",
          "https://www.uspto.gov/sites/default/files/documents/Initial-Filing-Template-August-2025.docx",
          "https://www.bls.gov/respondents/oes/template.xlsx"],
 "maxPagesPerDocument": 50, "extractTables": false, "ocr": true, "languages": ["eng"]}
```

The PDF's result (real output from a local run on 2026-09-25; `text` and `markdown` shortened, `tables` left out):

```json
{
  "id": "60084aa5127e297e8b972cbe",
  "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "fileName": "fw9.pdf",
  "format": "pdf",
  "fileSizeBytes": 140815,
  "title": "Form W-9 (Rev. March 2024)",
  "createdAt": "2024-03-06T13:18:13Z",
  "pageCount": 6,
  "pagesExtracted": 6,
  "wordCount": 6272,
  "tableCount": 0,
  "imageOnlyPages": [],
  "ocrPages": [],
  "text": "Form W-9\n(Rev. March 2024)\nRequest for Taxpayer\nIdentification Number and Certification\nDepartment...",
  "markdown": "<!-- page 1 -->\n\nForm W-9\n(Rev. March 2024)\nRequest for Taxpayer\n...",
  "truncated": false
}
```

The Excel workbook came back with its 3 sheet names and 3 tables (one per sheet, as rows); the Word file with its
text (258 words). None of the three needed OCR. With **Extract PDF tables** on, the W-9 also comes back with its 4
tables (`tableCount: 4`), as rows and as Markdown tables in place.

### Input

| Field | What it does |
|---|---|
| **Document URLs** | Direct links to the files, one per line (PDF, DOCX or XLSX, or an image: PNG, JPEG, WebP, TIFF, GIF, BMP). |
| Max pages per PDF | Read at most this many pages of each PDF, from the first (default 50, at most 100). `pageCount` still gives the whole document's length. |
| Pages to read (PDF) | Only these pages of each PDF, e.g. `1-10` or `1-3, 7, 20-` (`20-` = page 20 to the end). Max pages per PDF still applies. |
| PDF password | The password that opens your password-protected PDFs (stored encrypted; the same one is tried on each PDF that needs one). |
| Extract PDF tables | Find PDF tables and return them as rows and as Markdown tables (default off; slower, and each page read this way is a table page). Word and Excel tables always come back. |
| Read scanned pages with OCR | Read scanned PDF pages and images with OCR (default on). Each page read is an OCR page. |
| Languages | The language(s) of the scanned text, up to 4 (default English). |
| Max documents per run | Cap the number of documents converted in one run. |

```json
{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
    "https://example.com/files/price-list.xlsx"
  ],
  "maxPagesPerDocument": 50,
  "pages": "1-20",
  "extractTables": true,
  "ocr": true,
  "languages": ["eng"]
}
```

### Output

One result per document. Fields a format doesn't have are `null`.

```json
{
  "id": "60084aa5127e297e8b972cbe",
  "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "finalUrl": "https://www.irs.gov/pub/irs-pdf/fw9.pdf",
  "fileName": "fw9.pdf",
  "format": "pdf",
  "fileSizeBytes": 140815,
  "title": "Form W-9 (Rev. March 2024)",
  "author": "SE:W:CAR:MP",
  "createdAt": "2024-03-06T13:18:13Z",
  "modifiedAt": "2024-03-06T13:18:13Z",
  "pageCount": 6,
  "pagesExtracted": 6,
  "sheetNames": null,
  "wordCount": 6279,
  "tableCount": 4,
  "imageOnlyPages": [],
  "ocrPages": [],
  "ocrConfidence": null,
  "text": "W-9 Request for Taxpayer ...",
  "markdown": "<!-- page 1 -->\n\nW-9 Request for Taxpayer ...",
  "tables": [
    {"page": 1, "sheet": null, "index": 1, "rows": [["...", "..."]]}
  ],
  "truncated": false,
  "truncatedReasons": [],
  "scrapedAt": "2026-09-24T20:32:14Z"
}
```

- `id` is stable across runs (it comes from the URL), so you can use it to deduplicate or as a vector-store key.
- `tables`: `page` is set for PDFs, `sheet` for Excel; `index` counts tables within the document. PDF and Word cells
  are text; Excel cells keep numbers and true/false, and dates are ISO 8601 text. PDF tables are only looked for with
  **Extract PDF tables** on (the example above had it on).
- `pageCount`: PDF pages in the whole document; `pagesExtracted`: how many of them were read. Word documents have no fixed pages (they depend on fonts and
  paper), so for DOCX this is the count Word saved in the file, when it did. `null` for Excel.
- `imageOnlyPages`: PDF pages that are only pictures (scanned). With OCR on, their text is read by OCR.
- `ocrPages`: the pages whose text came from OCR (these are the OCR pages you pay for; an image is page 1), and
  `ocrConfidence` Tesseract's mean word confidence over them, 0-100 (`null` when no page needed OCR). A low confidence
  usually means a blurry or low-resolution scan, or the wrong language.
- `format`: `pdf`, `docx`, `xlsx`, or for images `png`, `jpeg`, `webp`, `tiff`, `gif` or `bmp`.
- `truncated` / `truncatedReasons`: whether part of the document was left out, and why (see the limits below).

### What it reads, and what it doesn't

- **PDF**: text and metadata. By default the text is read with PDFium (the engine inside Chrome's PDF viewer): fast,
  column by column on multi-column pages, with words hyphenated at line ends joined again. A table's cells come
  back as text in the order the PDF draws them, which for dense tables can be cell by cell rather than row by row.
  With **Extract PDF tables** on, the PDF is read with pdfplumber instead: tables drawn with ruling lines come back
  as rows and as Markdown tables where they sit, and the text is read line by line across the page (on two-column
  pages, that interleaves the columns). Tables drawn without lines, and tables on scanned pages, are returned as
  text. A PDF that only restricts printing or copying opens normally, as in any PDF viewer; one that needs a
  password to open is read with the **PDF password** you give, and otherwise reported as password-protected. Scanned
  pages are read with OCR at 300 DPI (Tesseract 5, LSTM engine), in the languages you pick.
- **Images** (PNG, JPEG, WebP, TIFF, GIF, BMP): read with OCR, the same way as our [Image to Text OCR](https://apify.com/humble-echidna/image-ocr) actor: one page
  per file (a multi-page TIFF is refused), the first frame of an animation, phone photos turned upright, up to 50
  megapixels.
- **Word (.docx)**: paragraphs, headings, lists, tables and content controls, in document order. Headers, footers,
  comments and footnotes are left out. Old `.doc` files aren't supported.
- **Excel (.xlsx)**: every worksheet's values. A formula gives the result Excel last saved; a workbook
  written by a program that never calculated its formulas has no saved result, so those cells are empty. Old `.xls`
  files aren't supported.

### Run it on a schedule, or from your own code

1. Save your input as a **task** (**Save as a new task**, top right of the actor page) and add it to a
   **schedule** (Console → Schedules → **Create new**): for example daily, if the same links are republished with
   new content (a price list, a monthly report). There's no "only changed" mode: every run converts, and charges
   for, every document you list. Each result's `id` stays the same from run to run for the same URL, so you can
   replace the old version.
2. Collect results: download the dataset as JSON, CSV or Excel; fetch the latest run's results from the API
   (`GET https://api.apify.com/v2/actor-tasks/<task id>/runs/last/dataset/items?status=SUCCEEDED&format=csv`, with
   your API token); let a webhook tell your system when a run succeeds; or connect it to Make, Zapier or n8n
   through Apify's integrations.

#### Can I use PDF, DOCX & XLSX to Text from an AI agent (MCP)?

Yes, through Apify's MCP server: add `https://mcp.apify.com?tools=humble-echidna/document-to-text` to your MCP
client (or let the agent find it with the server's actor search). The agent passes document links, e.g.
`{"urls": ["https://example.com/report.pdf"], "maxPagesPerDocument": 10}`, and reads `markdown` back.

### Who it's for

Teams that receive documents as links (supplier price lists, filings, reports, forms, contracts published online)
and need their text and tables in a database, a spreadsheet or an LLM pipeline, without opening each file.

### Why this one?

- **Three formats, one output shape.** PDF, Word and Excel come back with the same fields, so one pipeline handles
  all of them.
- **Fast text, tables when you want them.** Plain PDF text is read with a fast engine; turn on **Extract PDF
  tables** and a PDF table sits where it is on the page, as a Markdown table, instead of being flattened into lines
  of text, and comes back as rows too. Word and Excel tables always do (Excel numbers and true/false values keep
  their type).
- **Page ranges and passwords.** Read only the pages you need (`1-10`, `20-`), and open password-protected PDFs
  with your password.
- **Scanned pages get text too.** A PDF page that is only a picture is read with OCR, and its text goes where the
  page is, between the pages that already had text. Only those pages go through OCR, and only pages where OCR finds
  text are charged as OCR pages; a blank scanned page costs nothing.
- **Honest about what it can't read.** Password-protected PDFs are **reported as such and not charged**, instead of
  coming back as an empty result. The same goes for old `.doc` and `.xls` files, PowerPoint, links that lead to a web
  page instead of a file, and missing files.
- **No minimum.** The start fee is $0.00005, so converting a single document costs about half a cent.
- **Reliable.** Each document is parsed in its own process with a time limit and a memory limit, and each OCR page
  is read in its own process with its own limits, so one broken or huge file (or page) fails alone and never stops
  the rest of your run. The run log and the `RUN_STATS` record say exactly
  which document had a problem and why.

### Limits

- 50 MB per file, and about 1 GB of downloads per run (checked as each download starts). Each file gets 2 minutes to
  download, retries included; a server slower than that (about 420 KB/s for a 50 MB file) is reported, not charged.
- PDF: up to 100 pages per document (default 50), from the first page or from **Pages to read**.
- Up to 800,000 characters of text (and of Markdown) per document, and 800,000 characters of table cells.
- Excel: up to 20,000 rows per sheet, 500 columns and 500,000 cells per workbook.
- Word and Excel files that unpack to more than 300 MB are not opened, and neither is anything shaped like a ZIP
  bomb.
- Each document is parsed in its own process with a memory cap and a time limit: 1 minute for a PDF read without
  tables (it takes seconds even for 100 pages), 3 minutes with **Extract PDF tables** on and for Word, Excel and
  images, and 3 minutes per OCR page. With tables on, a dense 100-page PDF can take about 2-3 minutes at the default
  2 GB; for long, table-heavy PDFs run with 4 GB.
- OCR is the slow part, and it's limited by CPU, which Apify gives in proportion to memory (one core per 4 GB). A
  dense scanned page (a full tax form, about 1,000 words) took about 8 seconds on one core in our tests: about 16
  seconds per page with the default 2 GB, and a 20-page scan in about 5.5 minutes. OCR used less than 200 MB of memory,
  so more memory only makes it faster, up to the 4 GB maximum (one full core).

### robots.txt and politeness

- Before downloading a file, including every redirect hop, it checks that site's `robots.txt` and skips files it
  disallows. `robots.txt` is read once per site per run. Skipped files are listed in the log and in `RUN_STATS`
  (`blockedByRobots`), and are never charged.
- It identifies itself honestly, with the user agent `HumbleEchidnaApify/1.0 (+https://apify.com/humble-echidna)`.
  Site owners can allow or block it by name: `robots.txt` rules written for `User-agent: HumbleEchidnaApify` apply
  to it (otherwise the `*` rules do).
- **It also honours opt-outs from AI use.** Its output is meant for LLM and RAG pipelines, so when a site's
  `robots.txt` disallows a file for any of the well-known AI crawlers (GPTBot, CCBot, ClaudeBot, Google-Extended,
  PerplexityBot and the like), the file isn't downloaded. It's reported as opted out of AI use (`optedOutOfAI`) and
  not charged.
- It honours `Crawl-delay` and `Retry-After`, and never downloads more than 2 files at a time from one site.
- It only fetches public web addresses on the standard ports (80 and 443). It doesn't log in, and doesn't try to
  get around paywalls, logins or bot protection.

### FAQ

#### Can it read scanned PDFs or images?

Yes, with OCR (Tesseract), in 32 languages: pick the ones in your documents under **Languages**. Each page read by
OCR is charged as an OCR page. Handwriting, very small print and low-resolution scans read poorly; `ocrConfidence`
shows how sure OCR was.

**Languages:** English, German, French, Spanish, Italian, Portuguese, Dutch, Polish, Czech, Slovak, Swedish, Danish,
Norwegian, Finnish, Hungarian, Romanian, Turkish, Greek, Russian, Ukrainian, Bulgarian, Arabic, Hebrew, Hindi,
Bengali, Thai, Vietnamese, Indonesian, Japanese, Chinese (Simplified), Chinese (Traditional) and Korean.

#### Can it open password-protected PDFs?

Yes, if you give the password in **PDF password** (it's stored encrypted and only used to open the files inside
the run). Without it, or with a wrong one, they're reported as password-protected and not charged.

#### Should I turn on Extract PDF tables?

Only if you need a PDF's tables as rows or as Markdown tables. Without it, the table's text is still there, and the
rest of the PDF is read faster and, on two-column pages, in a better reading order. Word and Excel tables come back
either way.

#### Why does my link return "a web page, not a document"?

Some sites put a download page or a viewer in front of the file. Open the link in a browser, copy the address of
the file itself, and use that.

#### Something that used to work now fails. Why?

The run log names the document and what went wrong, and every other document in the run is unaffected. Please open
an issue with the input you used.

#### Is it legal to convert documents to text?

It only downloads the files you give it, from public web addresses, without logging in, and it follows each site's
`robots.txt`. Make sure you have the right to process the documents you send it.

### Related actors

| Actor | Use it when |
|---|---|
| [Image to Text OCR](https://apify.com/humble-echidna/image-ocr) | You have single images (photos, screenshots, one-page scans): $4.00 per 1,000 images, with line and word positions. |
| [Website & Page to Markdown for RAG and LLMs](https://apify.com/humble-echidna/page-to-markdown) | You have web pages (HTML) rather than files. |
| [Academic Papers Search: OpenAlex + Crossref](https://apify.com/humble-echidna/academic-papers) | You want papers: it finds them with their open-access `pdfUrl`, which you can pass here to get the text, where the host's robots.txt allows it. |

### Feedback and support

Found a bug, or a document that converts badly? Open an issue on the **Issues** tab with the link you used.

### Versions

Current version: **1.3**. See the Changelog tab for what changed in each version.

# Changelog

This Actor's version history is a separate document: https://apify.com/humble-echidna/document-to-text/changelog.md

# Actor input Schema

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

Direct links to the documents, one per line: PDF, Word (.docx) or Excel (.xlsx) files, or images (PNG, JPEG, WebP, TIFF, GIF, BMP; read with OCR), on public web addresses (http/https, standard ports 80 and 443). The file type is read from the file itself, so links without an extension work too. Links to a download page, a viewer or a login page don't: use the link to the file. The default is three small US government documents (a PDF, a DOCX and an XLSX).

## `maxPagesPerDocument` (type: `integer`):

Read at most this many pages of each PDF: 1 to 100, default 50, counted from the first page or from the start of pages. pageCount in the output is always the whole document's. Word and Excel files have no fixed pages and are read up to the size limits in the README.

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

PDFs only: read only these pages of each PDF, e.g. 1-10, or 1-3, 7, 20- (20- means page 20 to the end). Leave empty (the default) to read from the first page. maxPagesPerDocument still applies. Pages past a document's end are skipped; a PDF with none of the pages is reported, not charged.

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

The password that opens your password-protected PDFs (the same one is tried on every PDF in the run that needs one). Stored encrypted, and only used to open the file inside the run. PDFs that don't need a password are read as usual; a wrong password is reported, not charged. Leave empty if your PDFs aren't protected.

## `extractTables` (type: `boolean`):

Default false. When true, find tables in PDFs (from their ruling lines) and return them as arrays of rows and as Markdown tables in the markdown field. Off by default: the text is read with a much faster engine, and tables come back as plain text. Word and Excel tables are always returned (every Excel sheet is one table).

## `ocr` (type: `boolean`):

Default true: read the text of scanned PDF pages (pages that are only a picture) and of image files with OCR (Tesseract), in the languages set in languages. Each page read this way is charged as an OCR page, on top of the document; pages that already have text are never OCR'd. Set false to only take the text layer: scanned PDFs and images are then reported, not charged.

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

The language(s) of the scanned text, as Tesseract codes, at most 4, e.g. \["eng", "deu"] for English and German. Default \["eng"] (English). Used only for OCR (ocr true). Pick every language that appears in your scans; each extra language makes OCR slower.

## `maxResults` (type: `integer`):

Stop after this many converted documents in total, e.g. 20. Minimum 1; leave empty (the default) for no limit. The run also stops cleanly at the maximum cost per run you set in the run options, whichever comes first. Set it to keep a run small and quick.

## Actor input object example

```json
{
  "urls": [
    "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ],
  "maxPagesPerDocument": 50,
  "extractTables": false,
  "ocr": true,
  "languages": [
    "eng"
  ]
}
```

# Actor output Schema

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

No description

## `runStats` (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 = {
    "urls": [
        "https://www.irs.gov/pub/irs-pdf/fw9.pdf"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("humble-echidna/document-to-text").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://www.irs.gov/pub/irs-pdf/fw9.pdf"] }

# Run the Actor and wait for it to finish
run = client.actor("humble-echidna/document-to-text").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://www.irs.gov/pub/irs-pdf/fw9.pdf"
  ]
}' |
apify call humble-echidna/document-to-text --silent --output-dataset

```

## MCP server setup

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

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/NleJHNagS0qIfP0Nk/builds/kxvhnci9mh5tUlvBd/openapi.json
