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.

Hacker Developer
Auth Public key GitHub login
Pages Page 1 only 100/month
Rate limit 3/min per IP None
Watermark Yes No

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"}}'
{
  "markdown": "# Invoice\n\nDate: 2024-01-15\n...",
  "pages": 1,
  "request_id": "req_abc123"
}

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"}}'
{
  "markdown": "# Invoice\n\nDate: 2024-01-15\nInvoice #: INV-2024-0042\n\n| Item | Qty | Price |\n|---|---|---|\n| API Pro Plan | 1 | $49.00 |\n\n**Total: $49.00**",
  "pages": 3,
  "request_id": "req_def456"
}

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

Field Type Required Description
input.pdf_url string Yes* Public URL of a PDF to fetch and convert
input.pdf_base64 string Yes* Base64-encoded PDF bytes
input.include_raw boolean No Add one extra raw field for debugging without changing the standard success fields
input.max_pages integer No Optional 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

Authorization Bearer <api_key>
Content-Type application/json

Response · application/json

Field Type Description
markdown string Converted markdown text
pages integer Pages processed
request_id string Unique ID for debugging

Successful responses always include markdown, pages, and request_id. Set input.include_raw to true only if you also want a fourth raw field for debugging.

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.

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.

Code Meaning When
200 Success PDF converted
400 Bad Request Request payload rejected upstream
401 Unauthorized Missing or invalid API key
422 Unprocessable Entity Invalid source URL, TLS failure, unreachable source PDF, or unreadable PDF
429 Too Many Requests rate_limited or quota_exceeded; both include Retry-After
502 Bad Gateway Upstream worker unreachable, invalid, or failed while processing
504 Gateway Timeout Upstream worker timed out
500 Server Error Internal — include request_id when reporting

Ready to start?

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