HTML to PDF in n8n

This is one of the most-asked questions in the n8n community, and the first answer people find is wrong. There are three real ways to turn HTML into a PDF inside a workflow. This page describes all three, including the two that do not involve us, and says what each does with the binary afterwards — which is the part that actually costs you an evening.

1. A built-in node — it does not exist #

The Convert to File node handles CSV, JSON, XML, spreadsheets, text and base64. It does not render HTML, and it does not produce PDFs. Neither does Extract from File, which reads PDFs rather than writing them. There is no core n8n node that renders a web page.

The reason is not an oversight: rendering HTML faithfully means shipping a browser. n8n would have to bundle Chromium into every installation to do what a one-line HTTP call can do instead. So every real answer is one of the next two.

2. An HTTP Request node against any PDF API #

This works today, on Cloud and self-hosted, with no installs and no community-node policy to think about. It is the right answer if you already pay for a PDF API, or if your organisation does not allow community nodes.

The one thing to get right is the response format. By default n8n will try to parse the response as JSON and you will get mojibake instead of a file. Set Response → Format → File and name the output binary property.

HTTP Request node, configured by hand
Method:        POST
URL:           https://pdf.mintapis.com/v1/pdf
Authentication: Generic Credential Type → Header Auth
  Name:        Authorization
  Value:       Bearer pm_live_...
Body:          JSON
  {"html": "{{ $json.html }}", "format": "A4"}
Response:
  Format:      File
  Output field: data

What you give up doing it this way: the response headers. PDFMint returns X-PDFMint-Pages, X-PDFMint-Duration-Ms, X-PDFMint-Credits-Remaining and X-PDFMint-Warning, and an HTTP Request node in File mode drops them. If you never branch on page count or warnings, you will not miss them.

3. A community node #

A dedicated node exists so that the fiddly parts — binary handling, the headers above, per-item execution, and a credential that tests itself — are configured rather than rebuilt. n8n-nodes-pdfmint is a thin client over the same HTTP API, with zero runtime dependencies, built and tested against n8n 2.35 on Node.js 20 and 22, and marked usableAsTool so an AI Agent node can call it directly.

It is verified by n8n: the package is in n8n's community-node registry with isPublished: true, which is the list n8n Cloud installs from. You can confirm that without an account — the entry is in the response of https://api.n8n.io/api/community-nodes. We also publish the package: PDFMint and this node are built and run by the same people, so the node and the API it calls stay in step.

One thing to check after installing. n8n Cloud serves 0.4.5; npm's current release is 0.4.6. Both default to https://pdf.mintapis.com and include the error-output fix. The npm release also adds the account-free demo to its package documentation. If an older workflow still shows 0.1.0, update it rather than assuming it updated itself. The registry and npm evidence is linked on the docs page.

OperationCallsNotes
Generate PDFPOST /v1/pdfSource: HTML, Markdown, a URL or a saved template. Runs once per input item.
Generate ImagePOST /v1/imagePNG or JPEG screenshot of the same markup.
Merge PDFsPOST /v1/mergeRuns once for the whole branch, not once per item.
Get UsageGET /v1/mePlan, quota and documents remaining, on json.

Install and credential #

Cloud or self-hosted: Settings → Community Nodes → Install, then enter n8n-nodes-pdfmint. On self-hosted you can also install it from the CLI:

Self-hosted install
cd ~/.n8n/nodes
npm install n8n-nodes-pdfmint
# then restart n8n

The package carries an npm provenance attestation, so npm audit signatures can verify it was built by the GitHub Actions workflow in the repository, from a specific commit. That is worth doing for any community node before you let it hold a credential.

Then add a PDFMint API credential and paste a key from the dashboard — it starts with pm_live_. The credential tests itself with GET /v1/me, so n8n tells you immediately whether the key works instead of failing on the first real run.

Where the file ends up — the part that decides everything downstream #

With the default File (Binary) output, the PDF is attached to the output item's binary field (data, unless you rename it), so Gmail, Google Drive, S3, Slack, Nextcloud and HTTP Request nodes can consume it directly. There is no download step and no base64 dance.

Alongside it, on json:

FieldWhat it tells you
fileName, mimeType, sizethe usual, for the next node
pagespage count — useful for “this invoice should be one page”
durationMshow long the render took
creditsRemainingso a workflow can alert you before it runs out
warningpresent when the render succeeded but was probably not what you meant

Choose Hosted URL or Base64 in JSON instead and the item carries the API's JSON response with no binary attached — the right choice when the next step is a webhook or a database row rather than an attachment.

Three workflows people actually build #

Invoice on a new orderWebhook → Set (build the HTML, or name a stored template) → PDFMint → Gmail. The binary attaches straight to the mail.
Weekly reportSchedule → Postgres or Sheets → Code (render rows into a table) → PDFMint → Slack or Drive. One credit, any page count.
Statement per customer, mergedLoop the customers through Generate PDF, then a single Merge PDFs node at the end — it runs once for the whole branch, not per item, so one archive comes out.

Two of those three are published n8n templates you can import instead of building. Generate PDF invoices from webhook data is the first shape: a webhook takes the customer and the line items, the arithmetic happens in one Code node, and a broken payload gets a 422 with the reason rather than an invoice that says undefined. Send branded PDF receipts for Stripe payments carries a checkout.session.completed event through to a receipt in Gmail and Drive. Both are ours, both are free, and both need self-hosted n8n, because they use a community node.

When the data arrives as an email rather than a webhook, the missing step is in front of PDFMint, not after it. MailMint is ours and does that half: an inbound address or an IMAP mailbox goes in, structured JSON comes out, with the message’s own SPF, DKIM and DMARC verdict attached — so the invoice mail becomes the input of a Set node instead of a regex over the body. Same caveat as everything else on this page: community node, self-hosted n8n, and n8n’s verification of it is still pending.

For the template case, store the layout once with PUT /v1/templates/<name> and then send only data from the workflow. That keeps a 300-line invoice out of a Set node, where it is unreadable and undiffable.

When it goes wrong #

Four failures cover almost everything, and each has a named error rather than a 500:

SymptomWhat it means
unresolved_placeholdersA template placeholder had no value in data. The message names the field. Set strict: false to render anyway.
A warning on json, PDF looks emptyThe page rendered before its content existed — a JavaScript chart, a late image. Use waitFor with a selector.
Fonts look wrongA web font failed to load and the browser substituted silently. Reference it by absolute URL, or inline it.
Node output is text, not a fileYou are on the HTTP Request path and the response format is not set to File.

Failed renders refund the credit before the error reaches the workflow, so a retry loop on a broken template does not quietly spend your quota.

What it costs #

One credit per successful document, whatever the page count and whatever the file size. The free tier is 10 documents a month with no card — enough to wire the node up and see a real PDF, not enough to run a daily workflow, which needs Starter at $9 for 5,000 documents. Every feature is on every plan including free: password protection, headers and footers, page numbers, custom page sizes, watermarks, merging, images and hosted links.

How that compares with the other HTML-to-PDF APIs, with every figure read off the vendor's own page: the comparison page.

What we cannot claim #

PDFMint has paying customers, but no customer logo, testimonial or case study that we have permission to publish. The 1,002 weekly figure is npm downloads — machines running npm install, including CI and scanners — not users. n8n Cloud serves 0.4.5, while npm serves 0.4.6; the node code is unchanged between them. PDFMint runs as a single service in one region with no SLA; its status page is generated from its own request log and its uptime figure resets on every deploy, which it says on the page.

Try it #

Install the node, paste a key, drop it after any node that produces HTML, and run once. If it does what you need, the rest of this page was unnecessary; if it does not, the API reference has every option and every error code.

Get an API key   The node, in the reference   Live latency and success rate

Comparisons #

HTML to PDF APIWhat the job really involves, and what six products charge.
vs PDFShiftSame $9, ten times the documents — and their credit is 5 MB.
vs CraftMyPDFA drag-and-drop designer we do not have — and passwords from $99.
vs PDFMonkey€5 for 300 documents kept one day, against $9 for 5,000.