PDF to Structured Markdown Converter avatar

PDF to Structured Markdown Converter

Pricing

from $7.64 / 1,000 document extracteds

Go to Apify Store
PDF to Structured Markdown Converter

PDF to Structured Markdown Converter

Convert PDFs into page-aware Markdown with typed headings, paragraphs, lists, links, simple tables, bounding boxes, and embedded-image references.

Pricing

from $7.64 / 1,000 document extracteds

Rating

0.0

(0)

Developer

Stas Persiianenko

Stas Persiianenko

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Categories

Share

Turn public PDF URLs and uploaded PDF files into clean, page-aware Markdown and typed layout metadata.

This pdf to markdown converter runs fully inside your Apify Actor container. It reads PDF text and layout locally, identifies likely headings, paragraphs, list items, table rows, links, and embedded-image references, and returns one integration-friendly record per document.

Use it for repeatable RAG ingestion, knowledge-base preparation, document publishing, archive migration, and PDF content analysis without sending document contents to a third-party conversion API.

What does this Actor do?

For each supplied PDF, the Actor:

  1. downloads an anonymous public PDF URL or reads a PDF from the run's default key-value store;
  2. validates its size and PDF signature;
  3. extracts page text and positioned text items;
  4. infers headings, paragraphs, list items, and simple tables;
  5. collects PDF link annotations and embedded-image references;
  6. creates combined Markdown with optional page markers;
  7. emits detailed page and block metadata to the default dataset;
  8. optionally saves a .md file in the default key-value store.

Each successful document produces one charged document result. Failed inputs produce diagnostic dataset rows but are not charged as documents. Fields such as error, warnings, links, tables, and image references are included in the document record and are not separately charged.

Who is it for?

RAG and AI engineers

Prepare PDFs for chunking, embeddings, vector databases, document Q&A, and retrieval pipelines while retaining page references.

Data and knowledge teams

Normalize document collections into consistent Markdown and JSON-compatible block metadata on scheduled Apify runs.

Publishers and technical writers

Move text-first PDFs into editable Markdown while preserving useful document structure and links.

Automation developers

Call one Actor from JavaScript, Python, cURL, Apify Tasks, schedules, webhooks, or MCP tools.

Why use this PDF to Markdown converter?

  • Local conversion: no external conversion API key is required.
  • Batch input: combine multiple public URLs and uploaded storage keys in one run.
  • Page-aware output: Markdown can include <!-- page: N --> markers.
  • Typed blocks: inspect page, type, Markdown, plain text, bounding box, list marker, heading level, table cells, and image reference.
  • Integration-ready: the full record is available in the default dataset.
  • File-ready: optional .md artifacts are saved to the default key-value store.
  • Bounded failures: one invalid PDF does not discard successful conversions in the same batch.
  • SSRF protection: URL inputs must resolve to publicly routable addresses.

What data is extracted?

FieldMeaning
statussucceeded or failed for this input
sourceTypeurl or key-value-store
sourceOriginal URL or storage key
finalUrlURL after redirects, when applicable
fileNameSanitized PDF file name
byteSizePDF size in bytes
title, authorPDF metadata when available
pageCountTotal pages in the PDF
processedPageCountPages processed under your safety limit
markdownCombined Markdown for processed pages
markdownKeyKey of the generated .md artifact
pagesPage-level text, Markdown, blocks, links, and image references
headingsDocument-wide heading index with levels and pages
linksDocument-wide external-link index
tablesInferred table rows plus Markdown
imageReferencesStable references to image paint operations
warningsNon-fatal limits or metadata issues
errorPer-document failure explanation
convertedAtISO 8601 conversion time

Nullable fields are expected when a PDF does not contain the corresponding metadata.

Getting started

  1. Open the Actor input page.
  2. Add one or more direct PDF URLs under PDF URLs.
  3. Optionally add keys for PDFs already uploaded to the default key-value store.
  4. Choose document, page, file-size, and timeout limits.
  5. Keep Include page markers enabled for page-aware RAG workflows.
  6. Keep Save Markdown files enabled when you need downloadable artifacts.
  7. Start the run.
  8. Open Results for structured records or Markdown files for .md artifacts.

A safe first input is:

{
"pdfUrls": [
{ "url": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf" }
],
"maxDocuments": 1,
"maxPagesPerDocument": 20,
"includePageMarkers": true,
"saveMarkdownFiles": true
}

Input parameters

ParameterTypeDefaultDescription
pdfUrlsarraysample PDFAnonymous public HTTP(S) PDF URLs
pdfFilesarray[]Default key-value store keys and optional file names
maxDocumentsinteger10Unique PDFs to process, from 1 to 100
maxPagesPerDocumentinteger200Page safety limit, from 1 to 1,000
maxFileSizeMbinteger25Per-PDF size limit, from 1 to 100 MB
requestTimeoutSecsinteger45Per-attempt URL timeout, from 5 to 180 seconds
includePageMarkersbooleantrueAdd page comments to combined Markdown
saveMarkdownFilesbooleantrueSave generated .md artifacts

At least one item is required across pdfUrls and pdfFiles. Duplicate URLs and duplicate storage keys are processed once.

How to convert an uploaded PDF file

Upload the binary PDF to the run's default key-value store with content type application/pdf, then reference its key:

{
"pdfFiles": [
{
"key": "quarterly-report",
"fileName": "quarterly-report.pdf"
}
],
"saveMarkdownFiles": true
}

The storage value must be the binary PDF, not a JSON wrapper or a URL string.

Output example

A real one-page sample produces the following shape (long arrays shortened):

{
"status": "succeeded",
"sourceType": "url",
"source": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
"finalUrl": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
"fileName": "dummy.pdf",
"contentType": "application/pdf",
"byteSize": 13264,
"title": null,
"author": null,
"pageCount": 1,
"processedPageCount": 1,
"markdown": "<!-- page: 1 -->\n\nDummy PDF file",
"markdownKey": "markdown-3d7d1b17f7597eed.md",
"pages": [
{
"pageNumber": 1,
"markdown": "Dummy PDF file",
"text": "Dummy PDF file",
"blocks": [
{
"type": "paragraph",
"pageNumber": 1,
"markdown": "Dummy PDF file",
"text": "Dummy PDF file"
}
],
"links": [],
"imageReferences": []
}
],
"headings": [],
"links": [],
"tables": [],
"imageReferences": [],
"warnings": [],
"error": null
}

How headings, lists, and tables are detected

The converter uses PDF text coordinates and font sizes rather than visual HTML semantics, because PDFs normally do not contain HTML-like heading tags.

  • Larger, bounded text lines become inferred Markdown headings.
  • Bullet and numbered prefixes become list items.
  • Lines with at least three positionally separated cells become simple Markdown table rows.
  • Other lines become paragraphs in page reading order.

The original text, approximate bounding box, page number, and inferred type remain available in pages[].blocks[] so downstream code can refine decisions.

PDF link annotations are exported with target URL, page number, and annotation rectangle when available.

Embedded images are represented as stable references such as page-2-image-1. The Actor does not export image bytes or perform image OCR. References let downstream pipelines preserve figure positions without claiming that image contents were understood.

RAG ingestion workflow

A practical recurring workflow is:

  1. collect or upload the latest source PDFs;
  2. run this Actor on an Apify schedule;
  3. split markdown by page markers or pages[].blocks[];
  4. attach source, pageNumber, title, and block type as chunk metadata;
  5. generate embeddings;
  6. upsert chunks into your vector database;
  7. compare source hashes or file metadata before replacing older chunks.

The Actor produces conversion output; it does not call an embedding model or vector database by itself.

Publishing workflow

For Markdown publishing:

  1. enable saveMarkdownFiles;
  2. retrieve each markdownKey from the run's key-value store;
  3. review inferred headings and complex tables;
  4. rewrite image-reference anchors for your publishing platform;
  5. commit the resulting Markdown to your content repository.

This is especially useful for text-first manuals, reports, policies, and papers.

How much does it cost to convert PDF documents to Markdown?

Pay-per-event pricing has two parts:

  • Run start: $0.005 once per run.
  • PDF converted: BRONZE tier currently $0.012737 per successful document, with lower per-document prices at higher Apify tiers.

Examples at the BRONZE event price:

Successful PDFsEstimated Actor charge
1$0.017737
10$0.13237
100$1.2787

Failed documents are included in the dataset for diagnosis but do not emit the document charge event. Platform compute usage is governed by Apify's applicable plan and the active pricing shown in Console; check the live pricing panel before large runs.

JavaScript API example

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/pdf-to-structured-markdown-converter').call({
pdfUrls: [{ url: 'https://arxiv.org/pdf/1706.03762' }],
maxDocuments: 1,
maxPagesPerDocument: 15,
includePageMarkers: true,
saveMarkdownFiles: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0].markdown);

Python API example

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/pdf-to-structured-markdown-converter').call(run_input={
'pdfUrls': [{'url': 'https://www.irs.gov/pub/irs-pdf/fw9.pdf'}],
'maxDocuments': 1,
'maxPagesPerDocument': 6,
'saveMarkdownFiles': True,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items[0]['processedPageCount'])

cURL API example

curl -X POST \
"https://api.apify.com/v2/acts/automation-lab~pdf-to-structured-markdown-converter/runs?token=$APIFY_TOKEN&waitForFinish=300" \
-H 'Content-Type: application/json' \
-d '{
"pdfUrls": [{"url":"https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"}],
"maxDocuments": 1,
"saveMarkdownFiles": true
}'

Use the returned defaultDatasetId to fetch structured output from the Dataset API.

Use with MCP and AI agents

Add the Apify MCP endpoint to Claude Code:

claude mcp add --transport http apify \
"https://mcp.apify.com?tools=automation-lab/pdf-to-structured-markdown-converter"

Claude Desktop

Add this server to Claude Desktop's MCP configuration:

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com?tools=automation-lab/pdf-to-structured-markdown-converter"
}
}
}

Cursor

Add the same apify HTTP server URL in Cursor's MCP settings.

VS Code

Add the same apify HTTP server URL to your VS Code MCP server configuration.

Example prompts:

  • “Convert this public PDF to page-aware Markdown and summarize its headings.”
  • “Extract table metadata from the IRS W-9 PDF and return the page numbers.”
  • “Prepare this technical paper as Markdown blocks for a RAG pipeline.”

Scheduling and automation

Save a tested input as an Apify Task, then attach a schedule for recurring document processing. Webhooks can trigger downstream embedding, publishing, or archival jobs after successful runs.

For changing URLs, keep the source list in the Task input. For changing uploaded files, update the default key-value store keys before starting the Task.

Limits and expected behavior

  • Only PDF inputs are supported.
  • URLs must be anonymous HTTP(S) endpoints on public network addresses.
  • Redirects are limited to five and revalidated against private addresses.
  • Transient downloads receive bounded retries; deterministic HTTP errors do not.
  • Password-protected and malformed PDFs may fail.
  • Scanned image-only PDFs require OCR and are not supported.
  • Multi-column reading order is inferred and may need review.
  • Borderless or irregular tables may remain paragraphs or separate table rows.
  • Image bytes, captions, and OCR text are not extracted.
  • maxPagesPerDocument truncation is reported in warnings.
  • One failed document does not stop other valid inputs, but a run with no successful conversion fails visibly.

Troubleshooting

“Input is not a PDF”

Confirm the URL returns a real PDF rather than an HTML login page, consent page, or download landing page. Direct responses must begin with the %PDF- signature.

“Only publicly routable PDF URLs are allowed”

Private, loopback, link-local, and credential-bearing URLs are blocked. Upload private files to the default key-value store and use pdfFiles instead.

The output contains little or no text

The source may be a scanned image-only PDF. This Actor does not perform OCR. Use a text-layer PDF or an OCR-focused Automation Lab workflow before conversion.

A table is split into paragraphs

PDFs store positioned glyphs, not semantic tables. Use pages[].blocks[].bbox and text values to add source-specific post-processing for complex layouts.

A large PDF times out

Raise requestTimeoutSecs for slow downloads, increase the run timeout when needed, or lower maxPagesPerDocument. Do not raise maxFileSizeMb beyond what your memory allocation can safely parse.

Legality and responsible use

Process only PDFs you are authorized to access and transform. Respect copyright, contractual restrictions, privacy obligations, and applicable data-protection law.

Do not use this Actor to bypass authentication, access controls, paywalls, or private networks. URL inputs intentionally reject embedded credentials and non-public destinations.

Review generated Markdown before public redistribution. Structural inference is automated and does not transfer rights in the source document.

FAQ

Does it support batch PDF conversion?

Yes. Supply up to 100 unique URL and key-value-store inputs, bounded by maxDocuments.

Does it preserve pages?

Yes. Every block has a page number, every page has separate Markdown, and combined Markdown can contain page comments.

Does it extract tables?

It conservatively infers simple rows from positioned text columns. Complex layouts are not guaranteed to reconstruct perfectly.

Does it extract images?

It emits stable embedded-image references. It does not save image bytes or interpret image content.

Does it use AI?

No external model is required. Conversion uses deterministic local PDF parsing and layout heuristics.

Can it convert PDF to JPG, Excel, DOCX, or EPUB?

No. Those are separate file-conversion jobs. This Actor specifically converts PDF content to Markdown and structured metadata.

Can I process private documents?

Yes, by uploading the binary PDF to the run's default key-value store and referencing its key. Do not expose private files through public URLs.

What happens when one PDF fails?

The Actor writes a failed dataset record with a diagnostic message, continues with remaining inputs, and does not charge the document event for that failure.

What happens when every PDF fails?

The run fails after diagnostic rows are written, so schedules and webhooks can detect that no useful conversion was produced.

Can I use the output with LangChain or LlamaIndex?

Yes. Consume markdown directly or create chunks from pages[].blocks[], then attach source and page metadata in your framework.