Guides

HTML File to PDF: Production API Guide for Developers

Convert HTML file to PDF in production with APIs, Chromium rendering, code examples, costs, failures, and practical choices for developers.

M Mikel Rougstone · 25 September 2026 · 8 min read
HTML File to PDF: Production API Guide for Developers

Converting an HTML file to PDF in production can be done with one API request: PDFGeny accepts HTML, a URL, or a template through POST https://pdfgeny.com/api/v1/render and returns a finished PDF, with a median render time of 0.6 seconds. The harder engineering work is not the conversion itself; it is handling browser startup, CSS pagination, fonts, security, and operational failures.

TL;DR

  • PDFGeny renders HTML to PDF through a hosted API endpoint, with headless Chromium as the default engine and a median render time of 0.6 s.
  • PDFGeny supports 40 ready document templates, batch requests up to 100 documents, sync and async jobs, and HMAC-signed webhooks.
  • A cold headless Chromium process can cost about 7.8 s per request, while a warm browser process can render around 0.65 s.
  • A local PDF library is still the better choice for a handful of documents per day because it removes API cost and network dependency.
  • Get a free API key to send HTML, a URL or a template and receive a PDF without running Chromium or installing fonts.

HTML file to PDF: the production answer is usually a renderer, not a converter

HTML-to-PDF tools are often described as converters, but production systems behave more like rendering pipelines. A browser engine must load HTML, resolve CSS, fetch assets, load fonts, calculate page layout, and write PDF objects.

PDFGeny uses headless Chromium as its default rendering engine, with WeasyPrint available as a second engine and Ghostscript handling PDF/A-2b output. This combination targets different document requirements: browser accuracy for application pages, alternative layout handling, and archival output.

Developers maintaining their own stack commonly choose between browser automation, libraries, and hosted APIs. The right choice depends on document volume, security requirements, and how much infrastructure ownership the team wants.

ApproachRendering modelBest fit
Hosted APIRemote rendering serviceApplications needing PDFs without managing browser infrastructure
Chromium automationReal browser renderingTeams needing full browser CSS compatibility and owning operations
WeasyPrint or similar librariesDocument-focused renderingControlled layouts and local processing
wkhtmltopdfQt WebKit renderingLegacy systems already built around its output

A useful internal link for teams comparing browser-based rendering is Chrome HTML Document to PDF: Real Costs and Production Issues.

The real engineering problems behind HTML to PDF generation

Failure mode: cold Chromium startup adds seconds before rendering begins

Headless Chromium is accurate because it behaves like a browser, but browsers are expensive processes. A cold headless Chromium instance costs about 7.8 s per request, while keeping the browser warm brings rendering to about 0.65 s.

This difference changes architecture decisions. A team generating occasional reports may not care about startup time. A billing system creating invoices during checkout cannot treat browser lifecycle management as an afterthought.

Failure mode: web fonts silently fall back and change document layout

Web font loading is one of the easiest PDF problems to miss. A renderer may finish before the font download completes, causing fallback fonts that change line wrapping, table heights, and page breaks.

Font handling requires explicit asset management. Developers should avoid relying on a browser session having access to fonts installed on a machine. Production PDF output needs predictable font availability.

Failure mode: page breaks split invoices, contracts, and reports incorrectly

HTML pages do not naturally map to paper pages. CSS rules such as page-break-before, page-break-after, and break-inside become part of document logic.

For example, a table row that splits between pages may create an unreadable invoice. A certificate with dynamic text may push a signature block onto another page. PDF generation requires testing with realistic content lengths, not only sample HTML.

A PDF renderer does not only convert markup. It makes decisions about layout, resources, security boundaries, and failure recovery.

API implementation examples in six languages

Python: send HTML directly to the render endpoint

Python applications can create PDFs by posting HTML content to the PDFGeny endpoint.

import requests

response = requests.post(
    "https://pdfgeny.com/api/v1/render",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Content-Type": "application/json"
    },
    json={
        "html": "<h1>Invoice #1001</h1><p>Amount: $49</p>"
    }
)

response.raise_for_status()

with open("invoice.pdf", "wb") as file:
    file.write(response.content)

Failure mode: running browser rendering inside an async Django request can trigger SynchronousOnlyOperation when sync_playwright runs in the wrong execution context. The rendering task needs its own thread or an async-compatible design.

Node.js: generate documents from application data

const response = await fetch(
  "https://pdfgeny.com/api/v1/render",
  {
    method: "POST",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      html: "<h1>Monthly Report</h1>"
    })
  }
);

const pdf = await response.arrayBuffer();

PHP, Go, Ruby, and cURL

curl -X POST https://pdfgeny.com/api/v1/render \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"html":"<h1>Receipt</h1>"}'
// Go example
package main

import (
  "bytes"
  "net/http"
)

func main() {
  body:= []byte(`{"html":"<h1>Report</h1>"}`)
  req, _:= http.NewRequest(
    "POST",
    "https://pdfgeny.com/api/v1/render",
    bytes.NewBuffer(body),
  )
  req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
  http.DefaultClient.Do(req)
}
# Ruby example
require "net/http"
require "json"

uri = URI("https://pdfgeny.com/api/v1/render")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer YOUR_API_KEY"
request.body = { html: "<h1>Report</h1>" }.to_json

Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

The security issues that appear after adding URL-to-PDF

Failure mode: URL rendering creates an SSRF vulnerability

A URL-to-PDF feature can accidentally become a server-side request tool. A renderer that accepts arbitrary URLs may access internal services, loopback addresses, cloud metadata endpoints, or private network resources.

The fix is architectural: resolve the hostname first, reject private, loopback, and metadata addresses, then allow only approved destinations. The same rule applies whether the renderer is a local Chromium process or a hosted service.

Security teams can also review browser automation guidance from the OWASP SSRF documentation before exposing URL-based rendering.

Stored documents, async jobs, and batch generation

PDFGeny supports stored documents, sync jobs, async jobs, and HMAC-signed webhooks. These features match common application patterns: create a PDF now, process a large batch later, or notify another service when rendering completes.

Batch generation supports up to 100 documents in one call. That changes workflow design for tasks such as certificates, labels, and monthly statements.

What conventional wisdom gets wrong about HTML to PDF tools

The common advice is to always move from local libraries to an API as document volume grows. That advice is incomplete.

For a handful of documents a day, a local library can be the better choice. A small internal tool that creates one report every few hours may not need network requests, API credentials, or external dependencies.

The less obvious problem with older browser tools is not only age. wkhtmltopdf is not “dead”; it remains used in many existing systems. The real cost is that its rendering engine does not understand modern CSS features as well as current browser engines, forcing developers to maintain older HTML patterns.

Teams deciding between local rendering and APIs should measure operational ownership, not only PDF output. A local process may be cheaper financially but more expensive in maintenance time.

Pricing and feature comparison

PDFGeny detailValue
Free plan50 documents per month, no card required
Overage price$0.009 per document
Templates40 ready document templates
Batch sizeUp to 100 documents per API call
PDF/A outputPDF/A-2b support through Ghostscript

Developers comparing API options can also review PDF Generator API: Real Costs & Performance in Production and wkhtmltopdf: The Unmaintained Renderer’s Real Costs and Alternatives.

What We Got Wrong / What Surprised Us

The strongest non-obvious lesson is that PDF generation failures rarely come from the final PDF write operation. The failures happen earlier: during browser startup, resource loading, CSS layout, and security checks.

A surprising implementation detail is the gap between cold and warm rendering. A cold Chromium process costing about 7.8 s compared with a warm process near 0.65 s shows why production systems need a clear browser lifecycle strategy.

Another unexpected issue is that fonts can fail silently. A document may successfully generate while still being visually wrong because the renderer completed before the intended font loaded.

Practical Takeaways

  • Choose your renderer. Spend 1-2 hours comparing Chromium output, library output, and your document requirements. Difficulty: Low. Expected outcome: fewer layout surprises.
  • Test real documents. Spend 2-4 hours testing long tables, missing data, images, and custom fonts. Difficulty: Low. Expected outcome: fewer production page-break bugs.
  • Secure URL rendering. Spend 1 day reviewing SSRF protections and allowed destinations. Difficulty: Medium. Expected outcome: reduced security exposure.
  • Decide between local and hosted generation. Spend 2-3 hours calculating maintenance needs versus API usage. Difficulty: Medium. Expected outcome: a clearer ownership model.
  • Add asynchronous processing for bulk output. Spend 1-2 days integrating job handling if your workflow creates large document sets. Difficulty: Medium. Expected outcome: better user-facing response times.

Try PDFGeny for application PDF generation

PDFGeny is designed for teams that need invoices, receipts, contracts, certificates, reports, and labels without operating Chromium containers or managing renderer dependencies. The API accepts HTML, URLs, or templates and returns finished PDFs, with a free plan of 50 documents a month.

Send HTML, a URL or a template and get a finished PDF back in one API call. Start with 50 free documents a month and no card required.

Get a free API key

FAQ

How do I convert an HTML file to PDF in an application?

An application can convert HTML to PDF by using a browser renderer, a document library, or a hosted API. PDFGeny provides an API endpoint at POST https://pdfgeny.com/api/v1/render for HTML, URL, and template-based generation.

Is Chromium better than wkhtmltopdf for HTML to PDF?

Chromium generally matches modern web rendering more closely because it supports current browser behavior. wkhtmltopdf remains useful for existing systems, but teams often face CSS compatibility limitations because its rendering engine is older.

How much does PDF generation cost with an API?

PDFGeny offers 50 documents per month free with no card required. Additional documents cost $0.009 each.

Can HTML-to-PDF conversion create security risks?

Yes. URL-to-PDF features can create SSRF risks if applications allow access to private or metadata addresses. Host validation and network restrictions should be part of the design.

:::

M

Mikel Rougstone

Founder, PDFGeny

I build and run PDFGeny — the API, the rendering fleet and the template catalog. Most of what I write here comes from something that broke in production first.