API documentation
Markdown to PDF over HTTP, powered by pandoc and WeasyPrint. Mermaid diagrams render through headless Chromium and math renders through KaTeX — both automatically, with no flags to set.
Base URL
https://md2pdf-api-service-git-501785607868.europe-west1.run.appEvery request needs an x-api-key header. Keys are free and issued instantly from an email address on the landing page.
POST /convert
Convert Markdown to PDF.
| Method and path | POST /convert |
|---|---|
| Auth | x-api-key: YOUR_KEY header, required |
| Request body | Raw UTF-8 Markdown — not JSON, not multipart |
| Content-Type | Any; the body is read as raw bytes |
| Success | 200, Content-Type: application/pdf |
| Rate limit | 5 requests/minute per key (sliding window) |
| Max input size | 10 MB |
| Render timeout | 60 s per request |
Query parameters
mermaid_max_height — optional. Caps Mermaid diagram height. A bare number means millimetres (180 becomes 180mm); a CSS unit such as 60vh passes through unchanged. Defaults to 70vh.
Error responses
Failures return a JSON body of the form {"detail": "..."}.
| Status | When |
|---|---|
| 401 | Missing or unknown x-api-key |
| 429 | Rate limit exceeded — the Retry-After header gives the seconds until the window frees up |
| 400 | Empty body, or the body is not valid UTF-8 |
| 413 | Markdown exceeds the 10 MB size limit |
| 422 | pandoc/render failed, or the render timed out |
GET /health
Liveness check. Returns 200 with {"status": "ok"}.
Examples
curl
curl -X POST "https://md2pdf-api-service-git-501785607868.europe-west1.run.app/convert" \
-H "x-api-key: YOUR_API_KEY" \
--data-binary @doc.md -o doc.pdfPython
import requests
with open("doc.md", "rb") as f:
md = f.read()
r = requests.post("https://md2pdf-api-service-git-501785607868.europe-west1.run.app/convert",
headers={"x-api-key": "YOUR_API_KEY"},
data=md, timeout=120)
open("doc.pdf", "wb").write(r.content)TypeScript
const md: string = await (await fetch("/doc.md")).text();
const res = await fetch("https://md2pdf-api-service-git-501785607868.europe-west1.run.app/convert", {
method: "POST",
headers: { "x-api-key": "YOUR_API_KEY" },
body: md,
});
const pdf: Blob = await res.blob(); // application/pdfJavaScript
const md = await (await fetch("/doc.md")).text();
const res = await fetch("https://md2pdf-api-service-git-501785607868.europe-west1.run.app/convert", {
method: "POST",
headers: { "x-api-key": "YOUR_API_KEY" },
body: md,
});
const pdf = await res.blob(); // application/pdfNotes and gotchas
- Send raw Markdown, not JSON or multipart. The server reads the request body verbatim as UTF-8 text. Use
--data-binary @filerather than-d, which mangles newlines. - Mermaid diagrams render automatically whenever a
```mermaidfenced block is present. That is the only case that pays the slower headless-Chromium cost. - Math —
$inline$and$$display$$— renders via KaTeX automatically, with no flag. It is a pure-JS layout pass rather than a browser, so it costs almost nothing per request. - Emoji do not render, by design: the image ships no emoji font, because including one broke number rendering under WeasyPrint. Text, code, CJK and Mermaid all render correctly.
- Fonts — body text uses Inter, code uses Liberation Mono. Install Inter locally for a pixel-close preview.
- Cold starts can add a few seconds to the first request after an idle period. Set a generous client timeout; 120 s is safe.