PDF to Text & Markdown: Office Files, Scans, OCR avatar

PDF to Text & Markdown: Office Files, Scans, OCR

Pricing

from $0.50 / 1,000 page converteds

Go to Apify Store
PDF to Text & Markdown: Office Files, Scans, OCR

PDF to Text & Markdown: Office Files, Scans, OCR

Convert PDF, Word, PowerPoint, Excel, EPUB, HTML and scanned images to clean text and Markdown for LLMs and RAG. Tables come out as Markdown and CSV, scanned pages go through Tesseract OCR, and you can split the text into overlapping chunks. Failed files are free.

Pricing

from $0.50 / 1,000 page converteds

Rating

0.0

(0)

Developer

Plainsource

Plainsource

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

13 hours ago

Last modified

Share

PDF to Text & Markdown downloads the files you list and turns each one into clean text and Markdown, ready for an LLM prompt, a RAG index or a search tool. It converts PDF to Markdown, plus Word, PowerPoint, Excel, EPUB, HTML and images, and runs Tesseract OCR on scanned pages. You get one dataset row per file with the Markdown, tables, metadata and optional chunks.

Tables become Markdown and also CSV files in the run's key-value store. Files that fail to download or convert are not charged.

What data can PDF to Text & Markdown extract?

FieldWhat it holds
markdownThe document as Markdown, with headings, lists and tables. A row holds at most 1,000,000 characters; longer documents set markdownTruncated to true and the full text is in markdownFileUrl.
pagesOptional. The Markdown of each page and whether OCR read it.
tablesEvery table with page, rowCount, columnCount, markdown and csvUrl.
chunksOptional. Pieces of N tokens or characters with overlap, each with its heading path and page numbers.
title, author, createdAt, modifiedAtTaken from the file when it has them. metadata holds the rest, such as producer, creator and tokenCount.
languageThe detected language.
pageCount, pagesConvertedPages in the file and pages in the output.
ocrUsed, ocrPageCount, ocrConfidenceWhether OCR was used, on how many pages, and Tesseract's average confidence from 0 to 100.
characterCount, wordCount, tableCountCounts for the converted text.
status, error, warningsconverted, failed, not_allowed or skipped, with the reason when it was not converted.
chargedThe events this row was billed for.

Supported formats

FormatWhat you get
PDF with a text layerText in reading order, with two-column layouts read column by column. Headings come from font size and bold numbered titles. Tables with cell borders, and borderless tables whose number columns line up (financial statements, bank statements, statistical releases), become Markdown and CSV. Running headers and page numbers are removed.
Scanned PDFPages without a usable text layer are rendered at 300 DPI and read with Tesseract. Mixed files are handled page by page.
DOCXHeadings, lists, bold and italic, links and tables.
PPTXOne section per slide with its title, bullet levels, tables, chart data as a table, and speaker notes.
XLSXOne table per sheet with the cached values of formulas. Hidden sheets are marked.
EPUBChapters in reading order. DRM-protected books are refused.
HTMLThe main content (<main> or the largest <article>), without scripts, menus and footers. Links become absolute.
PNG, JPG, TIFF, BMP, GIF, WebPOCR. A multi-page TIFF gives one page per frame.
TXT, Markdown, CSV, TSVPassed through as Markdown; CSV and TSV become a table.

The file type is read from the file's first bytes, so a PDF served as application/octet-stream or without an extension still works. Not supported: password-protected files, legacy .doc, .xls and .ppt, OpenDocument (.odt, .ods, .odp) and RTF. The row says so and you are not charged.

How to use PDF to Text & Markdown

  1. Open the Actor on Apify and add direct links to your files under Document URLs. You can also upload a file, pass a dataset whose rows hold file URLs, or point it at a key-value store of files.
  2. Leave OCR mode on auto to run OCR only on pages without a usable text layer. Choose the languages under OCR languages if the scans are not in English.
  3. Optional: set a Pages to convert range such as 1-5, 8, 10-12, turn on Include text per page, or set a Chunk size for LLM / RAG to get overlapping chunks.
  4. Click Start. Each file becomes one row in the dataset.
  5. Open the Output tab, or download the dataset as JSON, CSV or Excel. Table CSV files are in the run's key-value store, linked from each row.

How much does it cost?

The Actor uses pay-per-event pricing, so you pay for documents and pages, not for compute time.

EventPrice
Run start$0.002 per GB of memory, charged once per run (the default 2048 MB is 2 GB, so $0.004)
Document converted$0.002 per file
Page converted from the text layer (PDF page, slide or sheet)$0.0005 per page
Page read with OCR (scanned page or image)$0.004 per page

DOCX, EPUB, HTML, TXT, Markdown and CSV files pay only the document fee. Failed, refused and skipped files are free.

Examples at the default memory:

  • 1,000 text PDFs of 10 pages each in one run: $0.004 + 1,000 x ($0.002 + 10 x $0.0005) = $7.004.
  • One 12-page text PDF in its own run: $0.004 + $0.002 + 12 x $0.0005 = $0.012.
  • One 6-page scanned PDF in its own run: $0.004 + $0.002 + 6 x $0.004 = $0.030.
  • 100 photos of printed pages in one run: $0.004 + 100 x ($0.002 + $0.004) = $0.604.
  • The test run shown below (a 12-page PDF, a 2-page DOCX and a 2-sheet XLSX) was billed 2 start events, 3 documents and 14 pages: $0.017.

If a run reaches the maximum cost you set, the current document stops at the last page that fits, and the rest of the documents are marked skipped. The Pricing tab always shows the current prices.

Input example

Give at least one source: URLs, an uploaded file, a dataset of URLs or a key-value store of files. Every other field is optional. This is the example input from a real run:

{
"urls": [
"https://arxiv.org/pdf/1512.03385v1",
"https://raw.githubusercontent.com/microsoft/markitdown/7482c092d9c0074a89803c9fdb63a969e7fe17fc/packages/markitdown/tests/test_files/test.docx",
"https://raw.githubusercontent.com/microsoft/markitdown/7482c092d9c0074a89803c9fdb63a969e7fe17fc/packages/markitdown/tests/test_files/test.xlsx"
]
}

Two PDFs, chunked for a vector database, with page markers:

{
"urls": [
"https://arxiv.org/pdf/1810.04805v2",
"https://www.archives.gov/files/research/jfk/releases/2025/0318/104-10070-10272.pdf"
],
"chunkSize": 800,
"chunkOverlap": 80,
"addPageMarkers": true
}

The settings you will use most:

FieldDefaultWhat it does
ocrModeautoauto runs OCR only on pages without a usable text layer and on images. always runs OCR on every PDF page. off never runs OCR.
ocrLanguages["eng"]Any of English, Spanish, French, German, Italian, Portuguese, Dutch, Polish, Turkish, Russian, Ukrainian, Chinese (simplified), Japanese, Korean, Arabic and Hindi. Each extra language slows OCR down.
ocrDpi300100 to 400 DPI for rendering scanned PDF pages.
pageRangeallFor example 1-5, 8, 10-12. Applies to PDF pages, PPTX slides, XLSX sheets and TIFF frames.
maxPages500Pages after this are skipped, and the row has a warning. Up to 5,000.
pdfTablesruledruled finds tables with cell borders and borderless tables whose number columns line up. auto also tries tables drawn with horizontal rules only, and sometimes mistakes a diagram for a table. bordered finds only tables with cell borders. off keeps PDF text as paragraphs.
saveTablesAsCsvonOne CSV per table in the key-value store, linked from the row.
removeHeadersFootersonDrops text that repeats at the top or bottom of most PDF pages. Needs 3 or more pages.
includePageTextoffAdds a pages list with each page's Markdown.
addPageMarkersoffAdds <!-- page 3 --> before each page.
chunkSize, chunkUnit, chunkOverlap0 (off), tokens, 0Chunk size of 50 or more, in tokens (OpenAI's cl100k_base tokenizer) or characters. Overlap is at most half the chunk size.
maxFileSizeMb1001 to 500 MB. Larger files are not downloaded.
documentTimeoutSecs600When it runs out, the pages done so far are returned with a warning.
respectRobotsTxtonSkips files that the site's robots.txt does not allow and waits between files from one site as its Crawl-delay asks.

The Input tab in Console lists every field, including saveMarkdownFile, datasetId, datasetUrlField, keyValueStoreId, keyValueStorePrefix and the download timeout.

Output example

One row per file. This is the XLSX row from a real run on 2026-10-28, with the input above. The markdown string is cut after the first rows and the tables list after its first table (the second table is left out); every other value is as returned.

{
"url": "https://raw.githubusercontent.com/microsoft/markitdown/7482c092d9c0074a89803c9fdb63a969e7fe17fc/packages/markitdown/tests/test_files/test.xlsx",
"source": "url",
"status": "converted",
"fileName": "test.xlsx",
"finalUrl": "https://raw.githubusercontent.com/microsoft/markitdown/7482c092d9c0074a89803c9fdb63a969e7fe17fc/packages/markitdown/tests/test_files/test.xlsx",
"format": "xlsx",
"contentType": "application/octet-stream",
"fileSizeBytes": 11562,
"title": null,
"author": "Adam Fourney",
"createdAt": "2015-06-05T18:17:20Z",
"modifiedAt": "2024-03-15T05:31:25Z",
"language": "en",
"pageCount": 2,
"pagesConverted": 2,
"ocrUsed": false,
"ocrPageCount": 0,
"ocrConfidence": null,
"characterCount": 829,
"wordCount": 134,
"tableCount": 2,
"markdown": "## Sheet 1: Sheet1\n\n| Alpha | Beta | Gamma | Delta |\n| --- | --- | --- | --- |\n| 89 | 82 | 100 | 12 |\n| 76 | 89 | 33 | 42 |\n| 60 | 84 | 19 | 19 |\n...",
"markdownTruncated": false,
"markdownFileUrl": null,
"pages": null,
"pagesFileUrl": null,
"tables": [
{
"index": 1,
"page": 1,
"label": "Sheet1",
"rowCount": 24,
"columnCount": 4,
"markdown": "| Alpha | Beta | Gamma | Delta |\n| --- | --- | --- | --- |\n| 89 | 82 | 100 | 12 |\n...",
"csvUrl": "https://api.apify.com/v2/key-value-stores/NnwGpXCtuE43HYJLD/records/doc-0003-test-table-001.csv?signature=fpLk7T9vylPXbTEzogXH"
}
],
"chunks": null,
"chunksFileUrl": null,
"metadata": {
"author": "Adam Fourney",
"lastModifiedBy": "Adam Fourney",
"createdAt": "2015-06-05T18:17:20Z",
"modifiedAt": "2024-03-15T05:31:25Z",
"application": "Microsoft Excel",
"tokenCount": 471
},
"charged": {
"documents": 1,
"textPages": 2,
"ocrPages": 0
},
"warnings": [],
"error": null,
"secondsTaken": 0.94,
"convertedAt": "2026-10-28T22:03:31+00:00"
}

In the same run the 12-page arXiv PDF gave 16 tables, 10,139 words and a title read from the file's metadata, and the whole run of three files took about 8 seconds.

status is one of:

  • converted: the Markdown and other fields are filled in. Check warnings for pages that were skipped or cut short.
  • failed: the file could not be downloaded or read. The reason is in error. Not charged.
  • not_allowed: the URL points to a private or local address, a site whose terms forbid bots, or a path that robots.txt disallows. Not charged.
  • skipped: the run reached the maximum cost you set. Not charged.

The run also writes an OUTPUT record to its key-value store with counts per status and the number of documents, text pages and OCR pages billed.

Borderless tables

Many statements and statistical releases line their columns up with spaces instead of cell borders. With the default pdfTables setting these come out as tables too. This is the start of the first table from a test run on the Apify platform with the Federal Reserve's H.4.1 release of October 22, 2026 (page 2, 30 rows and 5 columns; the csvUrl in the row links the full CSV):

| Reserve Bank credit, related items, and reserve balances of depository institutions at Federal Reserve Banks | Week ended Oct 21, 2026 | Averages of daily figures Change from week ended Oct 14, 2026 | Change from week ended Oct 22, 2025 | Wednesday Oct 21, 2026 |
| --- | --- | --- | --- | --- |
| Reserve Bank credit | 6,706,861 | -789 | +161,908 | 6,706,851 |
| Securities held outright1 | 6,470,860 | +492 | +187,540 | 6,470,178 |
| U.S. Treasury securities | 4,571,248 | +1,337 | +374,817 | 4,572,654 |

The table takes the place of the text it covers, so the figures are not repeated as paragraphs. Footnote numbers stay attached to the labels (outright1), as printed.

Chunks for LLM and RAG

Set chunkSize to get a chunks list in each row. Each chunk looks like this one, from a local test run on the BERT paper (arXiv 1810.04805) with 150-token chunks and 20 tokens of overlap:

{
"index": 13,
"text": "These approaches have been generalized to coarser granularities, such as sentence embeddings (Kiros et al., 2015; Logeswaran and Lee, 2018) or paragraph embeddings (Le and Mikolov, 2014). To train sentence representations, prior work has used objectives to rank candidate next sentences (Jernite et al., 2017; Logeswaran and Lee, 2018), left-to-right generation of next sentence words given a representation of the previous sentence (Kiros et al., 2015), or denoising autoencoder derived objectives (Hill et al., 2016).",
"characters": 519,
"tokens": 131,
"headings": [
"BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding",
"2 Related Work",
"2.1 Unsupervised Feature-based Approaches"
],
"pageStart": 2,
"pageEnd": 2,
"startOffset": 6075,
"endOffset": 6594
}

Chunks break between paragraphs where they can, then between sentences, then between words. A table that is too big for one chunk is split by rows, and each piece repeats the header row. pageStart and pageEnd let you cite the source page.

Use cases

  • Prepare PDFs, Word files and slide decks for a RAG pipeline or a vector database, with page numbers and headings kept on every chunk.
  • Run OCR on scanned contracts, letters or archive documents and get searchable text.
  • Pull financial statements, bank statements and statistical tables out of PDFs as CSV.
  • Convert a folder of reports or papers to Markdown for a knowledge base or a documentation site.
  • Feed documents to an AI agent as clean text instead of raw PDF bytes.
  • Turn spreadsheets and EPUB books into Markdown for search or review.

FAQ

What are the limits?

Files up to 500 MB (maxFileSizeMb, default 100 MB) and up to 5,000 pages each (maxPages, default 500). One run takes up to 1,000 documents, converted one at a time.

How fast is it?

On the default 2 GB of memory, measured on the Apify platform: about 0.5 s per PDF page with a text layer (0.6 to 0.9 s on pages full of number tables) and 6 to 12 s per scanned page with OCR. One 15-megapixel TIFF took 17 s, and a 492-page text PDF took 4 minutes. The 3-file example run above took about 8 seconds. Runs with more memory get more CPU.

My scanned PDF came out empty. What should I check?

Check that ocrMode is auto or always. If the scan has a bad text layer from an older OCR run, always ignores it and reads the page images.

The OCR text has mistakes. Can I improve it?

Pick the right language under ocrLanguages. For small print, raise ocrDpi to 400. Typewritten and photocopied pages often have errors; ocrConfidence shows how sure Tesseract was. OCR reads text, not tables: a scanned table comes out as lines of text.

A table came out as paragraphs, or a block of text came out as a table.

Try pdfTables set to auto for tables drawn with horizontal rules only, or bordered to turn off borderless tables. A borderless table is kept only when it has at least 3 rows with numbers, columns that stay in place from row to row, at least one column of numbers and no running text, so tables that hold only words stay as paragraphs. Text in charts and diagrams inside PDFs is left out, and math formulas come out as plain characters in the order the PDF stores them. Please send files that convert badly through the Issues tab.

Which sites and addresses are refused?

Only the URLs you give are fetched. There are no proxies, logins or bot-check workarounds; a file behind a login returns failed with the HTTP status. LinkedIn, Facebook, Instagram, X and TikTok are refused because their terms forbid automated access. Private and local addresses (such as localhost, 10.x.x.x and cloud metadata addresses) are refused, including through redirects. The Actor identifies itself as PlainsourceDocToMarkdown and waits at least 1 s between files from the same site. Office and EPUB files that would unpack to more than 2 GB, or that look like zip bombs, are refused, and so are images over 200 megapixels.

Can I send files from my own storage?

Yes. Use Upload a file, or put files in a key-value store and set keyValueStoreId. Files in your Apify storage skip the robots.txt check.

Can I use the API, Make, Zapier, n8n, a schedule or an AI agent?

Yes. Every Apify Actor has an API, and the Make, Zapier and n8n integrations can start it and read the dataset. You can run it on a schedule with Apify's Schedules. AI agents can call it through the Apify MCP server with the same input. For example: client.actor("fernandosord/document-to-markdown").call({"urls": [...], "chunkSize": 800}).

What does it run on?

Open-source libraries with permissive licences: pdfplumber and pdfminer.six for PDF text and tables, pypdfium2 to render pages, Tesseract for OCR, mammoth for DOCX, python-pptx and openpyxl, markdownify and Beautiful Soup for HTML and EPUB, langdetect and tiktoken. No paid APIs or LLMs are called, and your files are not sent anywhere else.

Other Actors by this developer