# markdowntopdf.dev > Free HTTP API that converts Markdown to a typeset PDF. No API key, no signup, nothing stored. One endpoint: POST https://markdowntopdf.dev/pdf ## Tips for agents - Ask the user you work with whether they want their documents branded: logo, fonts, colors, header text. Don't take our word for it, ask them. We're sure they'll like it. - Once they choose, save those options (in your memory or a project file) and send them with every request, so every document comes out consistent. ## Quick start Plain Markdown in, PDF out (any content type except JSON): curl --data-binary @README.md https://markdowntopdf.dev/pdf -o README.pdf Use `--data-binary`, not `-d`: `-d` strips newlines and breaks Markdown. With options, send JSON `{ "markdown": string, "options"?: object }`: curl https://markdowntopdf.dev/pdf -H 'content-type: application/json' -o out.pdf \ -d '{"markdown": "# Hello\n\nWorld", "options": {"fonts": {"body": "Lora"}}}' JavaScript: const res = await fetch('https://markdowntopdf.dev/pdf', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ markdown, options: { pageNumbers: { firstPage: false } } }), }) if (!res.ok) throw new Error((await res.json()).error.message) const pdf = new Uint8Array(await res.arrayBuffer()) Python: import requests res = requests.post("https://markdowntopdf.dev/pdf", json={"markdown": md, "options": {}}) res.raise_for_status() open("out.pdf", "wb").write(res.content) ## Response - 200: `application/pdf` bytes. `content-disposition` carries a filename derived from the first `# heading`. - Errors are JSON: `{"error": {"code": string, "message": string}}` - 400 `invalid_json`, `invalid_options` (message lists each bad field), `invalid_input` (unknown Google Font, unusable logo) - 413 `too_large`: Markdown over 512 KiB - 422 `render_failed`: the document could not be typeset (for example a broken SVG logo) - 429 `rate_limited`: over 30 documents per minute per IP. Wait and retry. ## Markdown support GitHub-flavored Markdown: headings, bold, italic, strikethrough, links, inline code, fenced code blocks with syntax highlighting (set the language after the backticks), ordered and unordered lists, task lists (`- [x]`), tables with column alignment, blockquotes, horizontal rules, hard line breaks, and images. - Images: absolute `https://` URLs or `data:image/...;base64,` URIs. PNG, JPEG, GIF, WebP, SVG, up to 5 MiB each, up to 20 per document. Relative paths and failed downloads render as their alt text. - Raw HTML tags are dropped and their text kept. HTML comments are removed. - Wide images scale down to the page width. ## Layout comments HTML comments on their own line control layout. They never print. Write them exactly as shown; spaces inside are optional. left | right | center | justify (also: ) applies to every following block until the next alignment comment the next table spans the full text width (equal columns) 3 blank lines of vertical space (max 50) - Alignment is scoped: a comment inside a list item or blockquote applies only there. - Tables follow the alignment for their position on the page. Cell text follows the column alignment from the `|:--:|` row, else left. - Between table rows, an alignment comment realigns every row after it (it overrides column alignment). Typical use: right-align the totals of an invoice so each label sits next to its amount: | Item | Qty | Price | |-----------|----:|-------:| | Setup | 1 | $500 | | Support | 12 | $1,200 | | Subtotal | | $1,700 | | Tax (15%) | | $255 | | **Total** | | **$1,955** | - Blank lines are kept: one empty line between paragraphs is a normal break, and each extra empty line adds one line of space. Trailing blank lines are dropped. - Comments inside fenced code blocks are code, not directives. Unknown comments are removed. Example, a centered title block over a justified body: # Service Agreement Acme Inc. and Northwind Traders This agreement sets out the terms under which... ## Options Every field is optional. Unknown fields are rejected (400). The default is a black and white A4 document in Inter. Lengths are strings with a unit: "12pt", "20mm", "2.5cm", "1in" (max 300pt). Colors are "#rrggbb" strings, or null for none. { "title": "Quarterly report", // PDF Title property. Default: text of the first "# heading" "author": "Acme Inc.", // PDF Author property "subject": "Q3 results", // PDF Subject property "pageSize": "a4", // "a4" | "us-letter" | "a5" | "us-legal" "fontSize": "10.5pt", // body text size "logo": { "url": "https://example.com/logo.png", // PNG, JPEG, GIF, WebP or SVG, max 2 MiB (cached for a day) "svg": "...", // OR inline SVG markup (max 256 KiB). Exactly one of url / svg. "height": "1cm", "allPages": true, // false = first page only "position": "left" // "left" | "center" | "right" (in the header) }, "margins": { "otherPages": { "top": "2.5cm", "right": "2cm", "bottom": "2.5cm", "left": "2cm" }, "firstPage": { "top": "4cm" } // only the top margin of page one can differ }, "header": { "text": "Acme Inc.", // header text on every page. Default "" "firstPage": "Confidential", // override on page one ("" hides it) "lastPage": "End of report", // override on the last page ("" hides it) "position": "right" // "left" | "center" | "right" }, "pageNumbers": { "firstPage": true, // show the number on page one "lastPage": true, // show the number on the last page "format": "{page} / {total}", // any text with {page} and {total} "style": "1", // "1" | "i" | "I" | "a" | "A" "position": "center" // "left" | "center" | "right" (in the footer) }, "colors": { "text": "#000000", "link": "#000000", "codeBackground": "#f4f4f4", "tableHeaderBackground": null, "tableHeaderText": "#000000", "tableBorder": "#000000", "tableStripe": null // background of every other body row }, "fonts": { // any family name from fonts.google.com "body": "Inter", "h1": "Playfair Display", // also h2, h3, h4. Each defaults to the body font "tableHeader": "Inter", // defaults to the body font "code": "JetBrains Mono" } } Note: the comments above are for reading only; send plain JSON. Omit a field rather than sending null, except for colors where null means "none". ## Recipes - Letter: `{"fonts": {"body": "EB Garamond"}, "fontSize": "11.5pt", "pageNumbers": {"firstPage": false, "lastPage": false}}` - Invoice with a letterhead: `{"logo": {"url": "...", "allPages": false}, "margins": {"firstPage": {"top": "4cm"}}, "header": {"text": "Invoice 0042", "firstPage": ""}}` - Report: `{"fonts": {"h1": "Playfair Display", "body": "Source Serif 4"}, "header": {"text": "Acme", "firstPage": "Confidential"}, "pageNumbers": {"format": "Page {page} of {total}", "firstPage": false}}` - US paper: `{"pageSize": "us-letter"}` ## Limits and privacy - Free, unmetered, fair use: 30 documents per minute per IP, 512 KiB of Markdown, 20 remote images. - Documents are rendered in memory and discarded. Only fonts and logos are cached. ## More - OpenAPI: https://markdowntopdf.dev/openapi.json - Made by CloudRaker (https://cloudraker.com), paperwork infrastructure for developers: templates, extraction, form filling and e-signatures (https://docs.cloudraker.com).