Markdown to PDF API
Post Markdown, get a printed PDF back. No HTML in between, no headless browser in your container, no pandoc and no LaTeX toolchain. This page is one real report — GFM table, ordered list, blockquote, fenced code, task list — rendered against the live service on 30 August 2026.
Why Markdown at all #
Because a lot of text is already Markdown before anyone thinks about a PDF: release notes, an LLM’s answer, a README, a runbook, a weekly report assembled from a database. Converting it to HTML yourself means picking a Markdown library, then a print stylesheet, then discovering that tables and code blocks need work before they look like a document.
The alternatives each cost something real. pandoc plus LaTeX gives beautiful output and a container image measured in gigabytes, plus a font and template problem. A Markdown library plus your own Chromium is the flexible route and is roughly a day of work plus a print stylesheet you will maintain forever. md-to-pdf and friends pull Puppeteer into your dependency tree, which is fine on a laptop and awkward in a Lambda.
One call #
curl -X POST https://pdf.mintapis.com/v1/pdf \
-H "Authorization: Bearer $PDFMINT_KEY" -H "content-type: application/json" \
-d '{
"markdown": "# Q3 Freight Review\n\n| Lane | Loads | On time |\n| --- | ---: | ---: |\n| Rotterdam → Passau | 412 | 94.2 % |\n",
"format": "A4",
"margin": "20mm",
"pageNumbers": true
}' --output report.pdf
Binary PDF in the response body by default. "output": "base64" puts the bytes in the
JSON, and "output": "url" returns a temporary link — that one matters in n8n, Make
and Zapier, where a base64 field is easier to hand to the next step than a binary body.
What the renderer supports #
GitHub-flavoured Markdown, and the print detail is where the work is. From the report rendered for this page, checked in the PDF’s own text layer:
- Tables, including column alignment.
| ---: |right-aligns; the numeric columns came out right-aligned on the paper. - Ordered and unordered lists, nested.
- Blockquotes and fenced code, monospaced, with the leading spaces preserved.
- Task lists.
- [x]is drawn as a real tick box. This is deliberate: a browser’s native checkbox prints as a flat grey square because print has no widget rendering, so the stylesheet suppresses the bullet and draws the box and the tick itself. - Headings, emphasis, links, images, horizontal rules.
Unicode goes through untouched — →, €, ß, decimal commas. What
is not here: footnotes, definition lists, math, Mermaid diagrams and admonition blocks are
not part of GFM and are not rendered. Raw HTML inside Markdown does pass through, which is the
escape hatch when you need one thing the syntax cannot express.
Page size, margins, page numbers #
The options are the same for Markdown as for HTML, because it is the same renderer.
format (A4, Letter, A5, Legal…), or explicit width and
height; margin as one value or per side; landscape;
scale; pageRanges.
For page numbers, "pageNumbers": true is the whole thing — it printed
Page 1 of 1 centred in the footer of the report on this page. When you need your own
wording, footerHtml takes markup with Chromium’s pageNumber and
totalPages spans in it. There is no separate flag to switch the footer on: supplying
one is the switch.
Your own CSS and fonts #
The default stylesheet is meant to look like a document rather than a web page, and you can
replace or extend any of it with "css". "googleFonts" loads a web font by
name so a corporate typeface does not need a base64 blob in every request.
{ "markdown": "…", "googleFonts": "Source Serif 4", "css": "body{font-family:'Source Serif 4',serif} h1{border-bottom:2px solid #111}" }
One caveat worth knowing before you debug it: a font is fetched at render time, so the render
waits on Google’s CDN. If that matters, inline the font as a data URI in css and
nothing external is fetched.
Placeholders in Markdown #
{{markers}} work in Markdown exactly as they do in HTML, including
{{#rows}}…{{/rows}} repeat blocks — which is how a table with an unknown
number of rows gets built without string-concatenating Markdown in your own code:
{
"markdown": "# {{title}}\n\n| Lane | Loads |\n| --- | ---: |\n{{#lanes}}| {{name}} | {{loads}} |\n{{/lanes}}",
"data": {"title": "Q3", "lanes": [{"name": "Rotterdam → Passau", "loads": "412"}]},
"strict": true
}
"strict": true turns a marker your data does not fill into an HTTP 400 that names
the field, instead of a silent blank in the middle of a report. That rejection costs no credit.
What it cost and how long it took #
The report described above — heading, subtitle, a three-row aligned table, an ordered list, a blockquote, a fenced code block and a two-item task list — on a free account, from Germany, on 30 August 2026:
| Measure | Value |
|---|---|
| Server-side render | 130 ms |
| Round trip including TLS | 237 ms |
| Output | 1 page A4, 96,129 bytes |
| Credits | 1 |
The file is larger than an equivalent HTML render of the same length — 96 KB against 48 KB for the invoice on the invoice page — because the Markdown stylesheet embeds a font subset for the monospaced code block. Worth knowing if you are storing millions of them.
When not to use us #
- You need typesetting, not printing. Real footnotes, an index, cross-references, proper widow and orphan control, a bibliography: that is pandoc with LaTeX or Typst, and the result is better than anything a browser prints. Do not fight this one.
- Math. No KaTeX or MathJax pass. A formula stays the source text you wrote.
- Mermaid or PlantUML diagrams. Not rendered. Turn them into an image first and reference it; images are fetched.
- The content must never leave your network. Then it must not be POSTed anywhere, including here. Run a Markdown library plus Chromium yourself.
- You convert a handful of files a month, at a desk. Your editor already exports PDF, and it is free.
What we cannot claim #
No outside customer has paid for PDFMint yet. The free tier is 10 documents a month. One region, one service, no SLA, no SOC 2, no PDF/A. The timings above are single measurements on one day from one machine, not a benchmark — the status page carries the running record. We have not compared output quality against pandoc, and we would not expect to win that comparison on typesetting.
Try it #
Send the longest Markdown file you actually have — the one with the wide table and the code block — rather than a paragraph of lorem ipsum. Wide tables and long code lines are where a Markdown-to-PDF converter is decided.