PDF to Markdown for LLMs & RAG Chunks - No Login icon

AI & documents · Coming soon

PDF to Markdown: Clean Markdown and Chunks for LLMs

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.

Built and tested. Prices will be shown after release.

Coming soon to the Apify StoreFollow us on Apify

What you get, for example

urlhttps://arxiv.org/pdf/1706.03762
finalUrlhttps://arxiv.org/pdf/1706.03762
titleAttention Is All You Need

How it will work: 3 steps

  1. Follow us on ApifyThe 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 StartNo 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 resultsWhen 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.

This tool is an Apify Actor: a small program that runs on Apify. You can also run it on a schedule, for example every Monday. New to it? Read how it works, step by step and the 8 words you need first.

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.

Who uses it

RAG developers

Load papers, reports and manuals into a vector database with page numbers for citations.

Data analysts

Pull tables out of public reports and paste them into a sheet.

AI agent builders

Give a model the text of a PDF in a format it reads well.

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.

Details

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
TaskInputKey fieldsNext step
RAG over research papersarXiv links in pdfUrlschunks[].text, headingPath, pageStartEmbed chunks into your vector database; cite pages in answers.
Table extraction from reportsreport links, maxPages: 300tables[].cells, tables[].pageLoad cells into a sheet or a database.
Agent reads a PDFone link from the conversationmarkdown, title, tokenEstimatePut the Markdown into the model context.
Digitize old scansscan links, ocr: "auto"markdown, pages[].ocrConfidenceCheck pages with low confidence by hand.
Tips for better results
  • 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 and good to know

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

For developers

Sample result

One result from a real test run, in JSON. Real values change from run to run. In Excel or CSV each field is a column.

one 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"
}
Data fields (27)
FieldTypeMeaning
urlstringInput URL as given.
finalUrlstringURL after redirects; null when the file was not downloaded.
statusstringcomplete, partial (some pages failed or were not processed), not_found (HTTP 404/410) or error.
titlestringTitle from the PDF metadata, else the top-level heading of the text layer (never from OCR); null when neither exists.
pageCountintegerTotal pages in the PDF; null if unknown.
pagesProcessedintegerPages read successfully.
pagesTruncatedbooleantrue when maxPages cut the page selection.
markdownstringMarkdown 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).
markdownUrlstringLink to the full Markdown file in the run key-value store when it was too large for the row; null otherwise.
charCountintegerCharacters in markdown.
tokenEstimateintegerApproximate tokens of markdown (characters / 4).
tablesarrayDetected tables: index, page, rows, columns, markdown (GFM) and cells (rows of strings; null only when dropped to fit the row size limit).
chunksarrayRetrieval chunks: index, text (Markdown), pageStart, pageEnd, headingPath (headings above the chunk), tokens (approximate), charCount.
chunkSizeintegerTarget chunk size used, in approximate tokens.
ocrUsedbooleantrue when at least one page was read with OCR.
textPagesintegerPages taken from the PDF text layer. Charged as one document event per started 50 of them (1–50 pages: 1 event, 51–100: 2).
ocrPagesintegerPages read with OCR, each charged as one ocr-page event per selected OCR language.
ocrLanguagesarrayOCR languages used, when any page was OCRed; null otherwise.
pagesarrayPer page: pageNumber, method (text, ocr or none), charCount, tables, ocrConfidence (0–100, OCR only), error.
fileSizeBytesintegerDownloaded size in bytes.
contentTypestringContent-Type header sent by the server.
metadataobjectPDF metadata as reported by the file: author, subject, keywords, creator, producer, creationDate, modificationDate, pdfVersion. Not verified.
encryptedbooleantrue when the PDF is encrypted (it may still be readable without a password).
errorobjectError 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.
warningsarrayNotes such as truncated pages or pages without a text layer.
fetchedAtstringWhen the file was downloaded (ISO 8601); null when it was not.
processedAtstringWhen the row was written (ISO 8601).
Input settings (8)

The main fields of the input form. With the API you send them as JSON.

SettingName in the APITypeExample value
PDF URLs requiredpdfUrlsarray["https://arxiv.org/pdf/1706.03762"]
Chunk size (tokens)chunkSizeinteger500
Chunk overlap (tokens)chunkOverlapinteger50
OCRocrstring"auto"
OCR languageslanguagesarray["eng"]
Pagespagesstring
Max pages per documentmaxPagesinteger50
Max file size (MB)maxFileSizeMbinteger50

Use cases with this tool

Each guide shows all the steps and the other tools.

Start on Apify

Make a free Apify account: you get $5 of free usage every month. Missing a tool? Suggest it. We read every idea, but we do not promise to build it.

Apify links on this page are affiliate links. We may earn a commission. You pay the same price. Affiliate disclosure