pdfmd.dev API v1
Send a text PDF, get Markdown and page metadata back as JSON. For English scanned PDFs, use the OCR endpoint. Both use the same page allowance, at one page credit per page. Free tier: 25 pages a day per IP without a key, up to 25 pages per file; 100 pages a day, up to 50 per file, with a free key for your email. Resident · Starter is $8.90 a month for 10,000 pages; see pricing.
Convert a file
curl -X POST https://pdfmd.dev/api/v1/convert \
-F "file=@paper.pdf"Convert from a URL
curl -X POST https://pdfmd.dev/api/v1/convert \
-H "content-type: application/json" \
-d '{"url": "https://arxiv.org/pdf/1706.03762"}'Response
{
"id": "sha256 prefix of the file",
"parserVersion": "version of the parser that read the file",
"title": "Attention Is All You Need",
"pages": 15,
"scannedPages": 0,
"tables": 4,
"chars": 41616,
"ms": 5700,
"pageBreaks": [0, 3120, ...], // character offsets where each page starts
"warnings": [], // parser 0.1.2 and later: values to check, see below
"markdown": "# Attention Is All You Need\n\n..."
}From parser 0.1.2, a small raised footnote or estimate mark is kept apart from its number when the page shows which value it belongs to: 7,000,000<sup>5</sup>. When the page alone cannot settle a value, it is left as extracted and listed in warnings. Each entry has page, kind and message. Two kinds exist: raised_mark_unresolved (a mark pdfmd could not place) and table_value_not_in_source (a table number the page does not print, for example a lost decimal point). The same messages close their page in the Markdown as HTML comments, so a copied or saved file keeps them. A response from an earlier parser has no warnings field and did not run these checks; parserVersion says which parser read your file.
Limits
An uploaded file can be up to 4 MB (4,000,000 bytes); a PDF fetched from a URL up to 50 MB. A result's JSON can be up to 4 MB: a larger one returns 422 result_too_large and uses none of your pages. Without a key, 25 pages per file; with a free key, 50; with a plan, 400. This Markdown endpoint counts scanned pages in scannedPages without reading their image text; use POST /api/v1/ocr for OCR. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, counted in pages (X-RateLimit-Unit: pages). A file longer than your per-file cap returns 413; running out of pages returns 429. Both include a pricing link, and without a key a free_key link: a free key for your email keeps 100 pages a day. Send a key as x-api-key; a plan from pricing gives a monthly allowance.
English OCR for scanned PDFs
POST /api/v1/ocr accepts an uploaded PDF or a public PDF URL. Set output to searchable_pdf or markdown; it defaults to markdown. English printed text only at launch. Existing text layers are kept, and image-only pages are read with OCR. Names, numbers, tables and unusual layouts need checking against the source.
curl -X POST https://pdfmd.dev/api/v1/ocr \
-F "file=@scan.pdf" \
-F "output=searchable_pdf" \
--output scan.searchable.pdf
curl -X POST https://pdfmd.dev/api/v1/ocr \
-H "content-type: application/json" \
-d '{"url":"https://example.org/scan.pdf","output":"markdown"}'For searchable_pdf, a successful response is PDF bytes with Content-Type: application/pdf, X-Pdfmd-Pages and X-Pdfmd-Ocr-Pages. Error responses are JSON. Check the HTTP status before saving a PDF.
For markdown, a successful response is the usual document JSON, plus operation: "ocr", output: "markdown", language: "eng" and ocrPages, the count of image-only pages processed by OCR. Both outputs carry X-Pdfmd-Rx: ocr.
OCR shares the existing API page allowance and API keys at one page credit per page, including pages that already have text. The same upload, public-link and per-file page caps apply. On this endpoint, each result must fit 4 MB; a larger result is refused. OCR requests have up to five minutes, so allow time in your client. A timeout or lost connection does not prove processing stopped or that allowance was unused; check GET /api/v1/me before retrying. An OCR request that fails after it was sent to the converter is charged its pages for a caller without a key and for a free key, because a failed OCR job still uses the converter's time. Paid plans get the pages back. A request the converter refuses before it starts OCR is not charged: a link that is private, malformed or cannot be fetched, a body or PDF over the size limit, a file that is not a PDF, or a body that arrives too slowly. The error message says which one happened.
When enabled, POST /api/v1/ocr/session accepts JSON with output and returns url, token, maxPages, maxBytes and expiresAt. Within two minutes, POST multipart field file to that URL with X-Ocr-Session: token. Keep the token private. Send your API key only to the session endpoint. The upload response is the result; there is no separate result URL. For long jobs, send Accept: text/event-stream. Ignore keepalive comments. Read the meta event as JSON with status, headers and bytes. Decode each chunk event from base64, in order. Require the done event and the exact byte count before use. Job errors carry their status and JSON body inside these frames. Without this header, the response is PDF bytes or document JSON, but long jobs can exceed the hosting response deadline. This path accepts uploads and PDF results up to 52,428,800 bytes. Markdown keeps its 4 MB cap. Existing page allowances apply. Unused page holds expire within 15 minutes. If this path is unavailable, the session endpoint returns 503.
The free browser OCR tool shares the web converter's daily file allowance. OCR creates private temporary worker files that our code removes on success, failure and worker timeout. Results have no saved download URL. Privacy and processing · Measured OCR agreement and its limits.
Use PDFMD from an AI assistant
Add https://pdfmd.dev/mcp as a remote MCP server. The free tier needs no key. One tool, convert_pdf_url, converts a public PDF link using the same API allowance and safety checks.
Add to ClaudeAdd to CursorAdd to VS Code
- Claude Code
claude mcp add --transport http pdfmd https://pdfmd.dev/mcp - Codex
codex mcp add pdfmd --url https://pdfmd.dev/mcp
Claude opens its Add custom connector dialog with the name and URL filled in: check both, continue, and choose no sign-in. ChatGPT has no install link: turn on developer mode (Settings, Apps, Advanced settings; available on some paid plans), then create an app with https://pdfmd.dev/mcp and no authentication.
Any other client that supports remote MCP: add the same URL as a Streamable HTTP server with no authentication. These connection settings do not mean PDFMD has been accepted into an app directory.
Replies contain up to 20,000 Markdown characters. Longer results are clearly marked as excerpts; the whole conversion uses your page allowance. For complete Markdown, use this website or the REST API, which starts another conversion and uses allowance again. There is no saved result link. This connector identifies scanned pages without OCR; for English scans, use the OCR REST endpoint. Local file paths or assistant attachments cannot be used as public links.
Assistant providers can share network addresses, so the anonymous allowance is not necessarily per person. Existing API keys work in clients that support request headers. A timeout does not prove conversion stopped; check your allowance before retrying. See connector privacy.
Your own standing
GET /api/v1/me
Returns whether you are calling anonymously or with a key, your page allowance, how much is left, and when it resets: daily at 00:00 UTC without a key, each billing period with one. Reading this does not use any of your allowance.
{
"product": "pdfmd",
"caller": "anon",
"limitKind": "pages_day",
"unit": "pages",
"limit": 25,
"remaining": 10,
"used": 15,
"maxPagesPerFile": 25,
"resetIso": "2026-09-21T00:00:00.000Z",
"persistent": true
}Privacy
A file goes to the conversion worker we run on Modal and the result comes back in the response. Ordinary text-to-Markdown conversion holds the file and result in memory only. OCR uses private temporary worker files that our code removes on success, failure and worker timeout. Neither gives a saved results URL. How long our hosting providers keep request logs we have not measured; privacy and processing has the detail. You keep every right to your documents.