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.