API Documentation

One endpoint, two free tiers. Everything you need to convert PDFs to markdown.

Quickstart

Convert a PDF in one command. No signup.

$ curl -X POST https://pdftomarkdown.dev/v1/convert \
  -H "Authorization: Bearer demo_public_key" \
  -H "Content-Type: application/json" \
  -d '{"input":{"pdf_url":"https://pdftomarkdown.dev/samples/invoice.pdf"}}'

The demo key demo_public_key works instantly. Multi-page PDFs are accepted, but the Hacker tier only processes page 1 and is limited to 3 req/min.

Have Node installed? The CLI converts local files, URLs, or stdin — no install, no signup:

$ npx pdftomarkdown document.pdf > document.md

Set PDFTOMARKDOWN_API_KEY to use your own key. Run npx pdftomarkdown --help for all options, or read the CLI guide.

Using Claude Code? Install the official plugin with /plugin marketplace add ThiloReintjes/pdftomarkdown-skill and Claude reads PDFs on its own.

Processing time: OCR runs on GPUs and takes roughly 10–30 seconds per page (a little longer on the first request after idle, while a worker spins up). For multi-page documents, set your HTTP client timeout to at least 10 minutes — many clients default to 30–120 seconds and give up while the conversion is still running.

Review privacy, security, and data retention before sending sensitive documents.

Language guides

Use plain HTTP from any stack. These focused guides are easier to share with implementation teams.

Tiers

Both are free. Pick the one that fits.

HackerDeveloper
AuthPublic keyGitHub login
PagesPage 1 only100/month
Rate limit3/min per IPNone
WatermarkYesNo

Tier 1 — Hacker

Public demo key, rate-limited to 3 req/min per IP. If you send a multi-page PDF, only page 1 is processed.

curl

$ curl -X POST https://pdftomarkdown.dev/v1/convert \
  -H "Authorization: Bearer demo_public_key" \
  -H "Content-Type: application/json" \
  -d '{"input":{"pdf_url":"https://pdftomarkdown.dev/samples/invoice.pdf"}}'
{
  "complete": true,
  "markdown": "# CONTOSO LTD.\n\n# INVOICE\n\nINVOICE: INV-100\nDATE: 11/15/2019\nDUE DATE: 12/15/2019\n\n| SALESPERSON | P.O. NUMBER | REQUISITIONER | SHIPPED VIA | F.O.B. POINT | TERMS |\n| --- | --- | --- | --- | --- | --- |\n|  | PO-3333 |  |  |  |  |\n\n| QUANTITY | DESCRIPTION | UNIT PRICE | TOTAL |\n| --- | --- | --- | --- |\n| 1 | Test for 23 fields | $100.00 | $100.00 |\n\nTOTAL DUE: $610.00\n\n> Processed by pdfToMarkdown.dev",
  "pages": 1,
  "request_id": "req_example_invoice"
}

Tier 1 responses append a watermark: > Processed by pdfToMarkdown.dev

Successful Hacker-tier responses also include X-PdfToMarkdown-Page-Cap: 1 so you can detect the enforced page cap.

Python

import requests

# Public demo key — no signup
r = requests.post(
    "https://pdftomarkdown.dev/v1/convert",
    headers={"Authorization": "Bearer demo_public_key"},
    json={"input": {"pdf_url": "https://pdftomarkdown.dev/samples/invoice.pdf"}},
)
print(r.json()["markdown"])

Tier 2 — Developer

Sign in with GitHub for a personal key. 100 pages/month, no watermark, multi-page PDFs.

curl

$ curl -X POST https://pdftomarkdown.dev/v1/convert \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input":{"pdf_url":"https://pdftomarkdown.dev/samples/invoice.pdf"}}'
{
  "complete": true,
  "markdown": "# CONTOSO LTD.\n\n# INVOICE\n\nINVOICE: INV-100\nDATE: 11/15/2019\nDUE DATE: 12/15/2019\n\n| SALESPERSON | P.O. NUMBER | REQUISITIONER | SHIPPED VIA | F.O.B. POINT | TERMS |\n| --- | --- | --- | --- | --- | --- |\n|  | PO-3333 |  |  |  |  |\n\n| QUANTITY | DESCRIPTION | UNIT PRICE | TOTAL |\n| --- | --- | --- | --- |\n| 1 | Test for 23 fields | $100.00 | $100.00 |\n\nTOTAL DUE: $610.00",
  "pages": 1,
  "request_id": "req_example_invoice"
}

Replace YOUR_API_KEY with the key from GitHub login.

Python

import requests

r = requests.post(
    "https://pdftomarkdown.dev/v1/convert",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={"input": {"pdf_url": "https://pdftomarkdown.dev/samples/invoice.pdf"}},
)
result = r.json()
print(result["markdown"])
print(f"Processed {result['pages']} pages")

API Reference

POST /v1/convert

Request body · application/json

FieldTypeRequiredDescription
input.pdf_urlstringYes*Public URL of a PDF to fetch and convert
input.pdf_base64stringYes*Base64-encoded PDF bytes
input.include_rawbooleanNoAdd one extra raw field for debugging without changing the standard success fields
input.max_pagesintegerNoOptional page cap. Hacker tier always overrides this to 1, so only page 1 is processed.

* Provide exactly one of input.pdf_url or input.pdf_base64.

Headers

AuthorizationBearer <api_key>
Content-Typeapplication/json

Response · application/json

FieldTypeDescription
completebooleanAlways true on a successful, complete response
markdownstringConverted markdown text
pagesintegerPages processed
request_idstringUnique ID for debugging

Successful responses always include complete: true, markdown, pages, and request_id. Canonical Markdown is never silently truncated. Set input.include_raw to true only if you also want an extra raw field for debugging.

Simple rectangular tables use escaped GFM pipe-table syntax. Complex tables with row spans, column spans, nested tables, or nested block content remain sanitized raw HTML inside the Markdown so their content and structure are not destroyed.

Hacker-tier successes also return X-PdfToMarkdown-Page-Cap: 1; if the source PDF has multiple pages, only page 1 is converted and pages returns 1.

Errors

Error responses always include error, message, and request_id. Any 429 also includes retry_after_seconds and the same value in the Retry-After header.

413 Complete response too large

{
  "error": "response_too_large",
  "message": "The complete OCR result exceeds the response size limit. Set input.max_pages or split the PDF into smaller documents.",
  "docs": "https://pdftomarkdown.dev/docs/",
  "request_id": "req_large123",
  "pages": 25,
  "max_response_bytes": 3500000,
  "response_bytes": 3900000
}

422 Bad input

{
  "error": "unprocessable_document",
  "message": "The file could not be parsed as a PDF. Only PDF documents are supported.",
  "request_id": "req_pdf321"
}

401 Unauthorized

{
  "error": "missing_api_key",
  "message": "Send an Authorization: Bearer <api_key> header. Get a free key in 30 seconds at https://pdftomarkdown.dev/auth/github.",
  "request_id": "req_auth789"
}

429 Rate limited

{
  "error": "rate_limited",
  "message": "Free Hacker-tier limit reached (3 requests per minute per IP). Retry shortly, or get a free API key with higher limits at https://pdftomarkdown.dev/auth/github.",
  "request_id": "req_rate123",
  "retry_after_seconds": 42
}

429 Quota exceeded

{
  "error": "quota_exceeded",
  "message": "Your monthly page quota is exhausted; it resets at the start of next month (see reset_at).",
  "request_id": "req_quota456",
  "retry_after_seconds": 2678400,
  "reset_at": "2026-04-01T00:00:00.000Z"
}

Quota exhaustion also returns reset_at so you can surface the exact monthly reset time.

CodeMeaningWhen
200SuccessPDF converted
400Bad RequestRequest payload rejected upstream
401UnauthorizedMissing or invalid API key
422Unprocessable EntityInvalid source URL, TLS failure, unreachable source PDF, or unreadable PDF
413Content Too Largeresponse_too_large; lower max_pages or split the PDF
429Too Many Requestsrate_limited or quota_exceeded; both include Retry-After
502Bad GatewayUpstream worker unreachable, invalid, or failed while processing
504Gateway TimeoutUpstream worker timed out
500Server ErrorInternal — include request_id when reporting

Ready to start?

Try the demo key or sign in with GitHub for 100 pages/month.