Markdown to PDF

Send GitHub Flavored Markdown, get an accessible, archival-grade PDF back. No AI call, no template — pure computation, typically under 200ms. This page covers what's supported, how images are handled, and the gotchas worth knowing before you wonder why something didn't come out the way you expected.

Calling it

Markdown rendering is available through the POST /api/v1/md endpoint, the makespdf CLI (makespdf md report.md -o report.pdf), and the VS Code and Obsidian plugins. The endpoint returns the PDF binary by default, or JSON metadata when you send Accept: application/json.

curl -X POST https://makespdf.com/api/v1/md \ -H "Authorization: Bearer mpdf_your_api_key" \ -H "Content-Type: application/json" \ -d '{"markdown": "# Hello\n\nSome **bold** text."}' \ -o output.pdf

See the API Reference for the full request/response contract, authentication, and rate limits. Markdown input is capped at 200KB (roughly 50,000 words).

Supported Markdown

The renderer follows the GitHub Flavored Markdown spec. The following all render as you'd expect:

Text & structure
  • Headings (# through ######), paragraphs, horizontal rules
  • Bold, italic, strikethrough, inline code, links
  • Ordered, unordered and nested lists; task lists (- [x] / - [ ])
  • Blockquotes and GFM alerts (> [!NOTE], > [!TIP], > [!IMPORTANT], > [!WARNING], > [!CAUTION])
  • Footnotes ([^1] references with end-of-document definitions)
  • A safe subset of inline HTML: <em> <strong> <b> <i> <del> <s> <img>
Code
  • Fenced code blocks with syntax highlighting for 26 languages (monospace font, GitHub-inspired colour theme)
Tables
  • GFM tables with per-column alignment
  • Per-table HTML-comment directives placed on the line before the table: <!-- borderless -->, <!-- full-width -->, <!-- columns: stretch fit --> — combinable as <!-- full-width borderless columns: stretch fit -->
Diagrams & graphics
  • Mermaid diagrams (```mermaid blocks) rendered as native PDF vector graphics — flowchart, sequence, class, state, gantt, pie, ER and gitgraph
  • SVG images drawn as native vectors (shapes, paths, text, transforms, opacity); raster images embedded directly

Images

Both Markdown image syntax (![alt](url)) and HTML <img> tags are supported. This is the part most worth understanding up front, because a misreferenced image fails quietly.

Image URLs must be absolute and publicly reachable. Rendering happens on our servers — there is no repository, working directory, or local filesystem in scope. An image source has to be something the server can fetch on its own: https:// (or http://) URLs, or inline data: URIs.
What works
  • Absolute URLs — https://example.com/logo.png
  • Inline data URIs — data:image/png;base64,… (including data:image/svg+xml,…)
  • Formats: PNG, JPEG, GIF, WebP, and SVG
What does not work
  • Relative paths — ./images/logo.png, ../assets/logo.png, /static/logo.png. These resolve against a local checkout that the server doesn't have. This is the single most common surprise when rendering a README or doc straight from a repository.
  • file:// URLs and any other non-HTTP scheme
  • URLs pointing at private or internal hosts (localhost, link-local, RFC 1918 ranges) — blocked for security
When an image can't be fetched, it is silently omitted. The renderer collapses it to zero size — no placeholder box, no error, no warning in the response. The surrounding text simply closes up around where the image would have been. If an image you expected is missing from the PDF, an unreachable URL is the first thing to check.
Limits
LimitValue
Images per document20 (extras are skipped)
Size per image5 MB
Fetch timeout per image5 seconds

An image that exceeds the size limit, times out, returns a non-image content type, or is beyond the count cap is omitted the same way an unreachable one is. SVGs render as native vectors but don't yet support gradients or filters — those elements are dropped.

Rendering a repo README

To render a document that uses relative image paths, either rewrite the paths to absolute URLs (for a GitHub repo, the https://raw.githubusercontent.com/<owner>/<repo>/<branch>/<path> form works well), or inline the images as data: URIs before sending the Markdown.

Options

Options can be set in the request body under options, or as YAML frontmatter at the top of the Markdown itself. When both are present, an explicit body option wins; otherwise frontmatter wins; otherwise the runtime default applies.

OptionValues
pageSizeA3, A4 (default), A5, Letter, Legal
fontFamilyInter (default), NotoSans
fontSizeBase size in points, 6–24 (default 10)
margins[top, right, bottom, left] in points (default [40, 40, 40, 40])
themegithub (default) — GitHub styling with h1/h2 underlines and zebra tables; default — heavier headings, no underlines
titlePDF document title (max 200 chars)

Other things to know

Page breaks keep related content together

The layout engine treats headings, code blocks, and GFM alerts as units that shouldn't be split across a page boundary. A heading is pushed to the next page rather than stranded at the bottom of one; a code block that doesn't fit moves wholesale to the next page; and a single-line label ending in a colon stays with the code block it introduces. As a result you may occasionally see white space at the foot of a page — that's the renderer keeping a block intact, not a bug.

Accessibility is built in

Every PDF is PDF/A-2A + PDF/UA-1 dual-compliant: a tagged structure tree, embedded fonts, and Unicode text extraction, with no configuration needed. Image alt text from ![alt](…) is preserved on the figure structure element. You can check a document's heading hierarchy and alt-text coverage ahead of time with POST /api/v1/md/validate.

Script support

Latin scripts render out of the box. Non-Latin European scripts (Cyrillic, Greek, Vietnamese, Georgian, Armenian) currently need a custom font supplied through the template APIs. CJK and complex-script shaping (Arabic, Hebrew, Indic, Thai, Khmer) are not yet supported and won't render correctly.

Related

  • API Reference — full /api/v1/md contract, authentication, rate limits
  • Getting Started — sign-up, API keys, your first render
  • Generate — VS Code, Obsidian, AI chat, library templates