How to Convert Markdown to PDF with an API (2026)
Markdown is everywhere: README files, AI-generated docs, internal notes, changelogs. When you need to ship a PDF from that content, the standard options are headless Chrome (memory leaks, cold starts, ops overhead) or a three-step pipeline that converts Markdown to HTML, then HTML to PDF, across separate services. The markdown to pdf api in Rendex collapses that to one POST request.
You pass a raw Markdown string. The API converts it to a styled HTML document server-side and renders it to a PDF or PNG. No Chromium to manage, no intermediate files, no conversion library to pin.
What you need
- An API key from rendex.dev/login (free tier: 500 calls/month)
- curl, Python 3.8+, or Node.js 18+
Step 1: Make the API call
The POST /v1/screenshot endpoint accepts a markdown field alongside url and html. The three are mutually exclusive: pick one per request.
# Get your API key at rendex.dev/login
curl -X POST https://api.rendex.dev/v1/screenshot \
-H "Authorization: Bearer rdx_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Q2 Report\n\n## Revenue\n\n- APAC: $18k\n- EMEA: $14k",
"format": "pdf",
"pdfFormat": "A4",
"pdfPrintBackground": true
}' \
--output report.pdf
The markdown field accepts up to 5 MB of Markdown text. The API converts it to HTML using a GitHub-flavored stylesheet before rendering, so standard Markdown syntax (headings, tables, fenced code blocks, inline code, blockquotes) renders as expected.
The format parameter controls the output type: "pdf" returns a PDF document, "png", "jpeg", and "webp" return images of the rendered page. pdfFormat accepts A4, Letter, Legal, Tabloid, and A3.
Step 2: Capture the full document
By default, the viewport height is 800px. For long Markdown documents, add fullPage: true to capture the entire rendered height rather than just the above-the-fold view. This has no effect on PDF output, which always renders full-page, but matters for PNG and JPEG.
curl -X POST https://api.rendex.dev/v1/screenshot \
-H "Authorization: Bearer rdx_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Docs\n\n## Section 1\n\nContent here.\n\n## Section 2\n\nMore content.",
"format": "png",
"fullPage": true
}' \
--output docs.png
Step 3: Use the Python SDK
The Rendex Python SDK ships a render_markdown method that wraps the markdown to pdf api call and handles authentication, camelCase conversion, and response parsing.
# pip install rendex
from rendex import Rendex
from pathlib import Path
# Get your API key at rendex.dev/login
rendex = Rendex("rdx_live_YOUR_KEY")
md = """# Q2 Report
Generated: 2026-05-27
## Revenue
| Region | Q1 | Q2 |
|---------|------|------|
| APAC | $12k | $18k |
| EMEA | $9k | $14k |
## Notes
- Compare against **Q1 targets** before sharing.
- Export as PDF before the 3pm review.
"""
result = rendex.render_markdown(md, format="pdf", pdf_format="A4")
Path("q2-report.pdf").write_bytes(result.image)
print(f"PDF written: {result.metadata.bytes_size} bytes")
render_markdown is a convenience wrapper over screenshot(markdown=...). Both accept the same keyword arguments in snake_case, which the SDK converts to camelCase before sending the request.
Step 4: Use the JavaScript SDK
The TypeScript SDK on npm (@copperline/rendex) exposes renderMarkdown, a typed wrapper over screenshot({ markdown }).
// npm install @copperline/rendex
import { Rendex } from "@copperline/rendex";
import { writeFile } from "node:fs/promises";
// Get your API key at rendex.dev/login
const rendex = new Rendex("rdx_live_YOUR_KEY");
const markdown = `# Release Notes
## v2.1.0
- Fixed auth timeout on long requests
- Added batch capture support (up to 500 URLs per job)
- Improved PDF margin defaults
`;
const { image } = await rendex.renderMarkdown(markdown, {
format: "pdf",
pdfFormat: "A4",
pdfPrintBackground: true,
});
await writeFile("release-notes.pdf", Buffer.from(image));
console.log("release-notes.pdf written");
Step 5: Override the default styles
The default stylesheet is GitHub-flavored: 16px system font, max-width: 768px, bordered H1 and H2, code blocks with a light gray background. To match your brand, pass a css string (up to 50 KB). It is injected after the default stylesheet, so it wins the cascade.
curl -X POST https://api.rendex.dev/v1/screenshot \
-H "Authorization: Bearer rdx_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"markdown": "# Custom Report\n\nStyled with custom CSS.",
"format": "pdf",
"css": "body.rendex-markdown { max-width: 960px; font-size: 14px; } body.rendex-markdown h1 { color: #ea580c; border-bottom-color: #ea580c; }"
}' \
--output styled.pdf
Target body.rendex-markdown to override the container, or use any standard CSS selector.
What you can generate
Markdown mode gives you clean GitHub styling out of the box. Layer a css string on top, or switch to html mode, and the same render pipeline turns plain input into production documents: invoices, reports, certificates, packing slips.
The pipeline handles long-form documents just as well. Author the body in Markdown and theme it with a css string, or hand Rendex finished HTML.
Markdown mode vs HTML mode
Use the markdown parameter when your source is already Markdown and the default GitHub styling is close enough. Use the html parameter when you need precise control over layout, custom fonts loaded from a CDN, or pixel-level design fidelity. The markdown pipeline adds a conversion step; HTML mode passes your markup directly to the renderer. Either way, the css parameter can push styles on top.
Both modes support Mustache templating via the data parameter. Pass {"data": {"name": "Alice"}} alongside your markdown string and use {{name}} in the Markdown body. The server fills the template before rendering. See the Markdown-to-Image tool to try this in the browser without writing any code.
Troubleshooting
VALIDATION_ERROR: Provide exactly one of 'url', 'html', or 'markdown': the request body includes more than one source field. Remove url or html if you are using markdown.
PAYLOAD_TOO_LARGE: the Markdown string exceeds 5 MB, or the data object exceeds 256 KB serialized. Split large documents or trim the data payload.
Tables or fenced code blocks not rendering: the default parser enables HTML mode so embedded HTML tags render as authored. Tables use standard GFM syntax (pipe-separated). Fenced blocks with a language identifier render without syntax highlighting; syntax coloring requires custom CSS or a client-side library.
Font not loading in rendered output: the renderer fetches web fonts from the URL in your @import statement. If the font domain requires authentication or uses a private CDN, the renderer falls back to the system font. Use a public URL.
PDF missing page breaks between sections: add a CSS page-break rule to the css param: h2 { page-break-before: always; }. The first H2 in the document will still start on page one; subsequent H2s will open new pages.
Next steps
The markdown to pdf api is one input mode of the same rendering pipeline. The same endpoint accepts a URL or raw HTML. For URL-to-PDF patterns in Python, see URL to PDF with Python.
To try Markdown rendering in the browser without an API key, use the Markdown-to-Image tool. Paste your content, pick PNG or PDF, and download the result.
Start for free at rendex.dev/login. The free tier covers 500 calls/month. No credit card required.


Top comments (0)