Convert HTML to PDF with the PDFMonkey API

Last updated April 7, 2026

PDFMonkey can work as an HTML-to-PDF API. Send your complete HTML — including <head>, styles, and <body> — as a string in the document payload, and PDFMonkey renders it to PDF exactly as-is. No templating required on the PDFMonkey side.

This is useful when the HTML already lives in your own pipeline: a transactional email renderer reused for receipts, a dashboard export, or an existing HTML invoice template.

When to use this approach

Use this pattern only when the HTML is owned and maintained outside of PDFMonkey. If the document layout will always be generated by PDFMonkey, a regular Code Template with dynamic data and Liquid logic will serve you better.

How PDFMonkey converts your HTML to PDF

PDFMonkey does not expose a raw HTML endpoint. Every PDF is generated from a Template. The workaround is a minimal Code Template that acts as a transparent pass-through:

  1. The template contains a single Liquid output tag: {{htmlContent}}
  2. You send your full HTML as the htmlContent value in the document payload
  3. Liquid renders the tag without escaping, so your HTML is injected verbatim
  4. The PDF engine converts the resulting page to PDF

Step 1 — Create the pass-through template

Create a new Code Template (this does not work with Builder Templates — see below). In the HTML tab, write nothing more than:

{{htmlContent}}

Open Settings and configure the page size, margins, and engine version you want. Those settings apply at the template level regardless of what HTML you send.

Click Publish to make the template available to the API.

Send a complete HTML document

The template renders only what is in htmlContent. Your HTML must include everything: a <!DOCTYPE html> declaration, a <head> with <style> or <link> tags, and a <body>. PDFMonkey adds nothing around it.

Step 2 — POST your HTML in the document payload

Send your HTML as the htmlContent string field inside the payload object:

curl https://api.pdfmonkey.io/api/v1/documents \
  -X POST \
  -H 'Authorization: Bearer YOUR_SECRET_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "document": {
      "document_template_id": "YOUR-TEMPLATE-ID",
      "status": "pending",
      "payload": {
        "htmlContent": "<!DOCTYPE html><html><head><style>p { color: #333 }</style></head><body><p>Hello\nWorld!</p></body></html>"
      }
    }
  }'

Most HTTP libraries (Node.js fetch, Python requests, Ruby net/http) serialize strings automatically — you pass the HTML as a normal string and the library handles escaping. If you build JSON by hand, escape:

  • double quotes → \"
  • line breaks → \n
  • backslashes → \\

See Generate PDFs with the API for the full generation flow, including how to poll for status and retrieve the download URL.

Mixing your HTML with Liquid

The template still runs through Liquid, so you can layer PDFMonkey logic on top of your HTML. For example, this template appends a watermark without touching your HTML:

{{htmlContent}}

{% if watermark %}
  <div class="watermark">{{watermark}}</div>
{% endif %}

Send both htmlContent and watermark in the payload. The {% raw %} tag is available if any field in your payload contains literal {{ characters that should not be evaluated.

Assets, fonts, and encoding

A few things to get right before your HTML renders as expected.

Images and external stylesheets must use absolute URLs. The PDF engine has no concept of your file system or server. A relative path like ./img/logo.png or /assets/style.css will not resolve and the asset will be silently skipped. Use full https:// URLs instead.

External fonts load fine if accessible. A <link> to Google Fonts or any public CDN works as expected — the engine fetches it during rendering. Fonts hosted behind authentication or on localhost will not load.

Declare your character encoding. Always include <meta charset="utf-8"> in the <head>. Without it, non-ASCII characters (accented letters, CJK, symbols) may render as garbled text depending on the engine version.

Trade-offs and limitations

Trade-offDetails
No reusable layoutHeaders, footers, and shared styles must live in your own HTML pipeline
Larger payloadsEach document carries the full HTML string; large payloads count against storage limits
No live previewThe Test Data tab can hold a sample, but changes don’t preview like a normal template
No built-in Liquid logicConditions, loops, and filters must be in your renderer, not PDFMonkey’s

If you find yourself needing any of these features, switch to a Code Template and let PDFMonkey own the rendering.

Builder Templates do not support this pattern

Builder Templates render bindings through Vue, which escapes HTML by default in {{ ... }} expressions. Sending raw HTML through a Builder binding prints the markup as visible text.

To inject pre-built HTML in a Builder Template, drop an HTML block on the canvas and paste your markup directly into its code editor. Note that HTML blocks are static — they are part of the template itself, not driven by payload data.

Frequently asked questions

Can I use PDFMonkey as an HTML-to-PDF API?
Yes. Create a Code Template containing only {{htmlContent}}, then POST your full HTML as the htmlContent value inside the document payload. PDFMonkey's Liquid engine does not escape HTML, so the markup renders as-is and returns a generated file.
How do I send pre-built HTML to PDFMonkey and get a PDF back?
1. Create a Code Template with a single line: {{htmlContent}}. 2. Publish the template. 3. POST to /api/v1/documents with your HTML as a string in the payload field htmlContent. PDFMonkey renders the HTML and returns the document with a download URL once generation is complete.
Does this HTML-to-PDF approach work in a Builder Template?
No. Builder Templates use Vue, which escapes interpolated content by default. Raw HTML sent through a Builder binding renders as visible text, not markup. Use a Code Template instead, or drop an HTML block on the Builder canvas.
Why do I still need a template if I send the full HTML myself?
PDFMonkey always generates documents from a template. The template carries the page size, margins, engine version, and access controls for the generation — even when it contains nothing but a Liquid placeholder for your HTML.
What HTML escaping is needed when sending HTML in a JSON payload?
Double quotes must be escaped as \", line breaks as \n, and backslashes as \\. Most HTTP client libraries and JSON serializers handle this automatically — you only need to escape manually when building raw JSON strings by hand.