PDF to Markdown for RAG & LLMs (tables, no API key needed)
Pricing
from $3.00 / 1,000 document pages
PDF to Markdown for RAG & LLMs (tables, no API key needed)
Convert PDFs to clean structured Markdown for RAG and LLM ingestion. Preserves tables. Flat $0.004/page, no API key required.
Pricing
from $3.00 / 1,000 document pages
Rating
0.0
(0)
Developer
Fabio Suizu
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
PDF to Markdown for RAG & LLMs — Tables Preserved, $0.004/page, No API Key Needed
Turn any PDF URL into clean, structured Markdown that is ready to feed into RAG pipelines, vector databases, and LLM prompts — with zero setup.
Paste PDF links. Press Start. Get Markdown with real headings, lists, and actual Markdown tables instead of collapsed text soup. That's it.
No LlamaParse key. No Reducto contract. No OpenAI account. No monthly subscription. You pay a flat $0.004 per page through Apify — everything else is included.
Why this Actor?
Every serious RAG or agent pipeline hits the same wall: PDFs. Naive extractors (pypdf, pdftotext) destroy tables, shuffle multi-column layouts, and drop heading structure — and your retrieval quality dies with them. The dedicated parsing APIs (LlamaParse, Reducto, Unstructured) fix that, but they make you sign up for another account, another API key, another bill.
This Actor runs on a managed document-parsing backend — the key is built in.
| This Actor | LlamaParse | Reducto | DIY (pypdf & friends) | |
|---|---|---|---|---|
| Requires your own API key | No — works out of the box | Yes (LlamaCloud key + credits) | Yes (sales contact) | — |
| Price per page | $0.004 flat | $0.003–$0.056 depending on mode | ~5× more | "Free" + your engineering time |
| Tables | Real Markdown tables | Mode-dependent | Yes | Usually destroyed |
| Headings & structure | Proper #/## hierarchy | Yes | Yes | Flat text |
| Setup | None — paste URLs | Account, credits, SDK | Contract, integration | Code, maintenance |
| Billing | Pay per event — only pages actually converted | Credit packs | Enterprise contract | — |
A 20-page contract costs $0.08. A 300-page annual report costs $1.20. Failed documents are never charged.
What it does
- Downloads each PDF from any direct URL — public links, presigned S3/GCS URLs, or files stored in an Apify key-value store. Files are validated as real PDFs before processing (an HTML viewer page instead of a raw file produces a clear error, not garbage output).
- Parses the document with layout awareness: reading order is reconstructed (multi-column pages come out in the right sequence), headings become
#/##/###, lists stay lists, and tables are emitted as GitHub-flavored Markdown tables — the single biggest quality lever for RAG over business documents. - Outputs Markdown twice: inline in the dataset item (for API consumers and agents) and as a downloadable
.mdfile in the run's key-value store (for humans and downstream jobs). - Keeps going on failure: a dead link or corrupt file produces an
{ url, error }item; the rest of the batch is processed and only successful pages are charged.
Input
{"sources": ["https://slicedinvoices.com/pdf/wordpress-pdf-invoice-plugin-sample.pdf","https://example.com/reports/annual-report-2025.pdf"],"maxPages": 500}
| Field | Type | Default | Description |
|---|---|---|---|
sources | array | (required) | URLs of PDF files: direct links or Apify key-value store record URLs |
maxPages | integer | 500 | Safety cap per document; larger documents produce an error item and are never charged |
Output
One dataset item per source. Here is the result for the sample invoice above:
{"url": "https://slicedinvoices.com/pdf/wordpress-pdf-invoice-plugin-sample.pdf","filename": "wordpress-pdf-invoice-plugin-sample.pdf","pages": 1,"char_count": 731,"charged_pages": 1,"markdown": "# Invoice\n\n**From:** Sliced Invoices...","files": {"md": "https://api.apify.com/v2/key-value-stores/<storeId>/records/001-slicedinvoices.com-pdf-wordpress-pdf-invoice-plugin.md"}}
And the Markdown itself — note the tables survive as tables:
# Invoice**From:** Sliced Invoices — Suite 5A-1204, 123 Somewhere Street, Your City AZ 12345**To:** Test Business — 123 Somewhere St, Melbourne VIC 3000| Invoice Number | Order Number | Invoice Date | Due Date | Total Due || -------------- | ------------ | ---------------- | ---------------- | --------- || INV-3337 | 12345 | January 25, 2016 | January 31, 2016 | $93.50 || Service | Description | Hrs/Qty | Rate/Price | Adjust | Sub Total || ---------- | ------------------------------------ | ------- | ---------- | ------ | --------- || Web Design | New website design and development | 1.00 | $85.00 | 0.00% | $85.00 || Sub Total | $85.00 || --------- | ---------- || Tax | $8.50 || **Total** | **$93.50** |
That is exactly what you want in a vector store: an LLM can answer "what is the total due and when?" from this chunk directly, no layout guessing.
If a source fails (dead link, not a PDF, over the page cap), the run keeps going and the failed source produces:
{ "url": "https://example.com/broken-link.pdf", "error": "Could not download the file: HTTP 404" }
Pricing
$0.004 per converted page, minimum 1 page per document. Billed via Apify's pay-per-event — no subscription, no credit packs, no compute-unit math.
| Workload | Cost |
|---|---|
| 1-page invoice | $0.004 |
| 20-page contract | $0.08 |
| 300-page annual report | $1.20 |
| 1,000 × 2-page invoices | $8.00 |
| Same annual report on LlamaParse Premium (~$0.045/page) + a LlamaCloud account | $13.50 and two invoices |
Documents that fail to download or convert are never charged. Documents over your maxPages cap are never charged.
Use it via API
Run the Actor from any language using the Apify API:
curl -X POST "https://api.apify.com/v2/acts/<username>~pdf-to-markdown-for-rag/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{ "sources": ["https://slicedinvoices.com/pdf/wordpress-pdf-invoice-plugin-sample.pdf"] }'
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('<username>/pdf-to-markdown-for-rag').call({sources: ['https://slicedinvoices.com/pdf/wordpress-pdf-invoice-plugin-sample.pdf'],});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items[0].markdown);
Python (drop-in for a RAG ingestion job):
from apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("<username>/pdf-to-markdown-for-rag").call(run_input={"sources": ["https://slicedinvoices.com/pdf/wordpress-pdf-invoice-plugin-sample.pdf"]})for item in client.dataset(run["defaultDatasetId"]).list_items().items:chunks = split_markdown(item["markdown"]) # your chunkervector_store.add(chunks, metadata={"source": item["url"]})
Use it from AI agents (MCP)
This Actor works out of the box with the Apify MCP server, so agents built on Claude, ChatGPT, Cursor, or any MCP-compatible framework can call it as a tool:
{"mcpServers": {"apify": {"url": "https://mcp.apify.com/sse?actors=<username>/pdf-to-markdown-for-rag"}}}
Your agent can turn any PDF a user shares into LLM-ready Markdown with no API key juggling inside the agent — this Actor needs none, and pay-per-event billing means you are only ever charged for pages actually converted.
FAQ
Do I need a LlamaParse / Reducto / OpenAI API key? No. That is the point of this Actor. Parsing runs on a managed backend whose credentials are bundled with the Actor. You only need your Apify account.
Why Markdown and not plain text or JSON? Markdown is the lingua franca of LLM ingestion: heading hierarchy gives your chunker natural split points, tables stay machine-readable, and every major framework (LangChain, LlamaIndex, Haystack) has first-class Markdown loaders. Plain text loses structure; per-block JSON makes you rebuild it.
How are tables handled? Tables are detected from the page layout and emitted as GitHub-flavored Markdown tables (pipes and header rows), not as space-aligned text. Merged or deeply nested table cells are flattened on a best-effort basis.
Does it work on scanned PDFs? Digital (text-based) PDFs give the best results. Scanned pages are processed through the backend's visual document understanding, so typical scanned invoices and contracts work; very low-quality scans may reduce accuracy.
Which languages are supported? The parser is language-agnostic for Latin-script languages and handles the major non-Latin scripts (CJK, Cyrillic, Arabic) as long as the PDF carries proper text or a legible scan.
How long does a conversion take? Simple digital PDFs process in seconds. Heavy, scan-based, or very large documents can take up to ~20 seconds per page; each document has a generous 20-minute processing budget.
What are the size limits?
Up to 1 GB per file download and maxPages pages per document (default 500, configurable up to 10,000). Anything over a limit produces an error item and is never charged.
What happens when one URL in my batch is broken?
The run continues. The broken URL produces a { url, error } item in the dataset; all other documents are processed and only successful pages are charged.
Where is my Markdown file?
Twice in the run: inline in the dataset item (markdown field) and as a downloadable .md file in the run's key-value store — each item links to it under files.md.
Can I convert DOCX, PPTX, or HTML? This Actor is tuned for PDF. For other formats, print/export them to PDF first (most converters do this losslessly) and pass the resulting URL.
Is my document stored? Documents are processed transiently and deleted from the worker after conversion. The resulting Markdown lives in your Apify dataset/key-value store, under your control.
Related use cases
- Build RAG knowledge bases from contracts, reports, manuals, and research papers
- Feed clean document context to AI agents (Claude, ChatGPT, custom LLM apps) via MCP
- Extract invoice and financial-statement tables into readable, diffable Markdown
- Preprocess PDF corpora for fine-tuning or embedding pipelines
- Archive PDFs as searchable, greppable, version-controllable Markdown