# PDF to Markdown: Clean Markdown and Chunks for LLMs

> PDF to Markdown from a public link: headings, lists, tables and ready RAG chunks with page numbers and token counts. OCR for scans. No login or key.

URL: https://cliqtomedia.com/actors/pdf-to-markdown/

Category: AI-ready content & documents · Status: coming soon · Apify profile: https://apify.com/cliqtomedia?fpr=cliqto

Paste a PDF link and get Markdown with real headings, lists and tables. The text is also cut into chunks for embeddings. Each chunk knows its pages and headings.

## How to use it: 3 steps

1. **Follow us on Apify.** The tool is built and waits in the queue for the Apify Store. Apify is the cloud platform where our tools run.
2. **Paste your input and press Start.** No code needed: you fill in a form (the input). It already has a working example. For example: `https://arxiv.org/pdf/1706.03762` in PDF URLs. Each start is one run.
3. **Download the results.** When the run ends, the results are in a table (the dataset). Open Output, press Export and pick Excel or CSV. JSON and the API are there too.

## What it does

Convert public PDF links into clean Markdown for LLMs and RAG: real headings, lists, GitHub-style tables and ready-made chunks with page numbers and token counts. Scanned pages are read with OCR.

- Downloads the PDF from the public URL.
- Reads the text layer of each page and keeps the layout: big text becomes `#` headings, bullets become lists, and grids of words become Markdown tables.
- Reads scanned pages (pages without a text layer) with OCR.
- Cuts the Markdown into chunks for embeddings. A new heading always starts a new chunk, and every chunk knows its pages and the headings above it.
- Saves one row per PDF in the Dataset.

### Good for

- Load research papers, reports or manuals into a vector database for RAG.
- Give an AI agent the text of a PDF as Markdown, with tables it can read.
- Pull tables out of public reports and forms as Markdown or as cells.
- Read old scanned documents that have no text layer.

### Not a good fit

- PDFs behind a login, a paywall or a private network. Only public `http(s)` links work.
- Password-protected PDFs. The Actor reports them and does not try to open them.
- Formulas, charts and pictures. The text around them is kept, the images are not described.
- Uploading files from your computer. Put the file at a public URL first.

## Price

Prices will be shown after release.

## Questions

### Why is this better than plain PDF text?

You keep the structure. Headings, lists and tables stay readable for a model.

### Does it describe charts and images?

No. The text around them is kept, but pictures and formulas are not described.

### Can it open a password PDF?

No. It reports the file as protected and does not try to open it.

### How big can a PDF be?

Up to 300 pages and 100 MB per file. You can send up to 100 links per run.

### Do I need a login or an API key for the PDFs?

No. The Actor reads public links. You only need an Apify account to run it.

### How is this different from a plain PDF text extractor?

A plain extractor gives one long text. This Actor keeps headings, lists and tables as Markdown and also returns chunks with pages and the headings above them, so you can load them into a vector database without your own splitter.

### Why did I pay for OCR on a PDF?

Some pages had no text layer (scans), so they were read by OCR. Set `ocr: "off"` if you never want OCR.

### Is the output always perfect?

No. Text-layer PDFs are converted well; complex tables and poor scans can have mistakes. Check `pages[].ocrConfidence` and the `warnings`.

### Do you keep my documents?

No. The PDF files are deleted at the end of the run. The rows stay in your own Apify storage.

## Data fields

- `url` (string): Input URL as given.
- `finalUrl` (string): URL after redirects; null when the file was not downloaded.
- `status` (string): complete, partial (some pages failed or were not processed), not_found (HTTP 404/410) or error.
- `title` (string): Title from the PDF metadata, else the top-level heading of the text layer (never from OCR); null when neither exists.
- `pageCount` (integer): Total pages in the PDF; null if unknown.
- `pagesProcessed` (integer): Pages read successfully.
- `pagesTruncated` (boolean): true when maxPages cut the page selection.
- `markdown` (string): Markdown of all processed pages: # headings, lists, GFM tables. Each page starts with an <!-- page N --> marker. null on error, or when the row would exceed the Dataset item limit (then the full Markdown is at markdownUrl).
- `markdownUrl` (string): Link to the full Markdown file in the run key-value store when it was too large for the row; null otherwise.
- `charCount` (integer): Characters in markdown.
- `tokenEstimate` (integer): Approximate tokens of markdown (characters / 4).
- `tables` (array): Detected tables: index, page, rows, columns, markdown (GFM) and cells (rows of strings; null only when dropped to fit the row size limit).
- `chunks` (array): Retrieval chunks: index, text (Markdown), pageStart, pageEnd, headingPath (headings above the chunk), tokens (approximate), charCount.
- `chunkSize` (integer): Target chunk size used, in approximate tokens.
- `ocrUsed` (boolean): true when at least one page was read with OCR.
- `textPages` (integer): Pages taken from the PDF text layer. Charged as one document event per started 50 of them (1–50 pages: 1 event, 51–100: 2).
- `ocrPages` (integer): Pages read with OCR, each charged as one ocr-page event per selected OCR language.
- `ocrLanguages` (array): OCR languages used, when any page was OCRed; null otherwise.
- `pages` (array): Per page: pageNumber, method (text, ocr or none), charCount, tables, ocrConfidence (0–100, OCR only), error.
- `fileSizeBytes` (integer): Downloaded size in bytes.
- `contentType` (string): Content-Type header sent by the server.
- `metadata` (object): PDF metadata as reported by the file: author, subject, keywords, creator, producer, creationDate, modificationDate, pdfVersion. Not verified.
- `encrypted` (boolean): true when the PDF is encrypted (it may still be readable without a password).
- `error` (object): Error for this row: {code, message}, e.g. NOT_FOUND, FILE_TOO_LARGE, UNSUPPORTED_FILE_TYPE, PDF_PASSWORD_REQUIRED, DOWNLOAD_TIMEOUT, DEADLINE_REACHED, OUTPUT_TOO_LARGE; null when there is none.
- `warnings` (array): Notes such as truncated pages or pages without a text layer.
- `fetchedAt` (string): When the file was downloaded (ISO 8601); null when it was not.
- `processedAt` (string): When the row was written (ISO 8601).

## Input settings

- `pdfUrls` (array, required): Up to 100 public http(s) URLs of PDF files. No login, cookies or private networks; password-protected PDFs are reported, not opened. Duplicates are processed once. Files are deleted when the run ends.
- `chunkSize` (integer): Target size of each chunk in approximate tokens (characters / 4). A new heading always starts a new chunk; tables stay whole unless they alone are bigger, then they split by rows with the header repeated. 100–4000, default 500 (fits most embedding models).
- `chunkOverlap` (integer): Approximate tokens of text repeated at the start of the next chunk in the same section, so sentences cut at a boundary keep context. Default 50 (or a quarter of a small chunk size); at most half the chunk size.
- `ocr` (string): auto (default): use the PDF text layer and run OCR only on pages without usable text (scans). on: OCR every page. off: text layer only; scanned pages give no text. Text-layer pages are covered by one per-document charge; OCR is charged per page and per selected language.
- `languages` (array): Languages of the document, used by OCR only. Up to 3. Each OCR page is charged once per selected language, so pick only the languages the document uses. Default: English.
- `pages` (string): Optional page selection for every document, e.g. 1-3, 5, 10- (10 to the end). Empty: all pages, up to Max pages per document.
- `maxPages` (integer): Upper limit of processed pages per document (after the page selection). Extra pages are skipped, not charged, and reported in warnings. 1–300, default 50.
- `maxFileSizeMb` (integer): Larger files are not downloaded and get error FILE_TOO_LARGE. 1–100, default 50.

## Step by step on Apify

1. Open the Input tab.
2. Keep the example link in PDF URLs (`https://arxiv.org/pdf/1706.03762`) or add your own PDF links, one per line.
3. Keep the default settings: chunks of about 500 tokens, OCR Auto.
4. Click Start.
5. Open the Output tab. The Documents view shows the Markdown and tables; the Chunks view shows the chunks.

## Use cases

| Task | Input | Key fields | Next step |
|---|---|---|---|
| RAG over research papers | arXiv links in `pdfUrls` | `chunks[].text`, `headingPath`, `pageStart` | Embed chunks into your vector database; cite pages in answers. |
| Table extraction from reports | report links, `maxPages: 300` | `tables[].cells`, `tables[].page` | Load cells into a sheet or a database. |
| Agent reads a PDF | one link from the conversation | `markdown`, `title`, `tokenEstimate` | Put the Markdown into the model context. |
| Digitize old scans | scan links, `ocr: "auto"` | `markdown`, `pages[].ocrConfidence` | Check pages with low confidence by hand. |

## Tips

- Use the ready chunks as they are. A new heading always starts a new chunk.
- For big scanned files, raise the run timeout or pick only some pages. OCR takes about 10 seconds per page.
- Treat the token count as a guess. It is characters divided by 4, not a real tokenizer.

## Limits

If the run is close to its timeout, the Actor stops taking new pages, saves what it has with `status: "partial"`, and lists the documents it did not reach with `DEADLINE_REACHED`. You pay only for pages that were read.

- No login, cookies, paywalls or private networks. No password cracking.
- No image, chart or formula description. Text in pictures is read only by OCR on scanned pages.
- Tables without lines are found by the columns of words. Merged or very complex cells can shift.
- Tokens are an estimate (characters / 4), not the count of a specific tokenizer.
- Files are deleted when the run ends. Only the Markdown and the summary are kept in your storage.
- Text PDFs: about 3 seconds for a 15-page paper; 100 PDFs (535 pages) in about 45 seconds; a 300-page report with 1,281 tables in about a minute.
- OCR: about 10 seconds per page at 1 GB, up to 30 seconds for dense pages. A 36-page scan takes about 6 minutes. The default run timeout is 30 minutes (about 90–180 OCR pages); for bigger scans raise the timeout or use `pages`.
- Up to 100 URLs per run, up to 300 pages and 100 MB per document.

## Sample result

```json
{
  "url": "https://arxiv.org/pdf/1706.03762",
  "finalUrl": "https://arxiv.org/pdf/1706.03762",
  "status": "complete",
  "title": "Attention Is All You Need",
  "pageCount": 15,
  "pagesProcessed": 15,
  "pagesTruncated": false,
  "markdown": "<!-- page 1 -->\n\n…\n\n# Attention Is All You Need\n\n…\n\n<!-- page 2 -->\n\n## 1 Introduction\n\nRecurrent neural networks, long short-term memory [13] and gated recurrent [7] neural networks …",
  "markdownUrl": null,
  "charCount": 41793,
  "tokenEstimate": 10449,
  "tables": [
    {
      "index": 1,
      "page": 6,
      "rows": 6,
      "columns": 4,
      "markdown": "| Layer Type | Complexity per Layer | Sequential | Maximum Path Length |\n| --- | --- | --- | --- |\n…",
      "cells": [["Layer Type", "Complexity per Layer", "Sequential", "Maximum Path Length"], ["Self-Attention", "2 · O(n d)", "O(1)", "O(1)"]]
    }
  ],
  "chunks": [
    {
      "index": 8,
      "text": "### 3.2 Attention\n\nAn attention function can be described as mapping a query and a set of key-value pairs to an output …",
      "pageStart": 3,
      "pageEnd": 4,
      "headingPath": ["Attention Is All You Need", "3 Model Architecture", "3.2 Attention"],
      "tokens": 136,
      "charCount": 543
    }
  ],
  "chunkSize": 500,
  "ocrUsed": false,
  "textPages": 15,
  "ocrPages": 0,
  "ocrLanguages": null,
  "pages": [
    { "pageNumber": 1, "method": "text", "charCount": 2886, "tables": 0, "ocrConfidence": null, "error": null }
  ],
  "fileSizeBytes": 2215244,
  "contentType": "application/pdf",
  "metadata": { "creator": "LaTeX with hyperref", "producer": "pdfTeX-1.40.25", "pdfVersion": "1.5" },
  "encrypted": false,
  "error": null,
  "warnings": [],
  "fetchedAt": "2026-10-15T18:15:47.458Z",
  "processedAt": "2026-10-15T18:15:47.633Z"
}
```

## Use cases with this tool

- [One piece of content, ten new posts](https://cliqtomedia.com/actors/use-cases/content-repurposing/): Turn videos, podcasts, PDFs and articles into clean text. Make posts, notes and AI drafts from it.

## Similar tools

- [PDF & Image to Text OCR for AI Agents - No Login](https://cliqtomedia.com/actors/pdf-text-ocr/)
- [DOI Metadata & Bibliography Enrichment](https://cliqtomedia.com/actors/doi-bibliography-enrichment/)
- [YouTube Subtitles & Transcript Scraper API](https://cliqtomedia.com/actors/youtube-transcript-scraper/)
- [RSS & Atom Feed Reader / Monitor](https://cliqtomedia.com/actors/rss-atom-feed-reader-monitor/)
- [WordPress Posts & Pages Scraper](https://cliqtomedia.com/actors/wordpress-posts-pages-scraper/)
- [News Article Extractor: RSS, URLs & Clean Text](https://cliqtomedia.com/actors/news-article-extractor/)
