How to Convert HTML Templates to PDF with an API
Convert an HTML template to PDF by rendering it in a browser or sending it to a hosted API. Learn how to handle layout, assets, async jobs, and failures.

To convert an HTML template to PDF with an API, either render the template in a browser and call its PDF method, or send the HTML or template data to a hosted conversion API. For browser rendering, load the finished HTML, wait for its fonts and other required assets, choose print or screen styles, then export with Puppeteer’s page.pdf(). For hosted conversion, use the provider’s documented raw-HTML or stored-template endpoint and handle its response as either PDF bytes or an asynchronous job.
The examples below use Puppeteer for the do-it-yourself browser route. Use a hosted API when your application should not manage browser processes, or when a stored template and provider-managed job flow fit your system better. No renderer guarantees that arbitrary HTML will look correct: check representative output, especially page breaks, long tables, fonts, images, and backgrounds.
1. Choose a rendering route
| Route | Good fit when | Plan for |
|---|---|---|
| Browser automation | You want rendering in your own application environment and direct control of the page. | Browser lifecycle, deployment resources, asset loading, PDF options, and output validation. |
| Hosted conversion API | You want a service to accept HTML or template data and produce the PDF. | Provider-specific auth and payloads, request limits, service dependency, output delivery, and current terms. |
The browser workflow is render, then export. Puppeteer and Playwright both document PDF generation through a page method; both use print CSS by default. A hosted service changes where rendering runs and how you submit and retrieve the result. It does not, by itself, establish better speed, cost, reliability, or CSS fidelity. Choose based on ownership, template reuse, layout requirements, job model, and delivery or retention needs.
2. Convert a template with Puppeteer
Install Puppeteer in a Node.js project using its current official installation instructions. The script below expects a local template file named template.html, writes output.pdf, and uses a file:// URL so relative local assets can resolve from the template directory. For production, supply HTML from your application or navigate to a controlled page. Review Puppeteer’s PDF generation guide and PDFOptions reference for the current option names and behavior.

import puppeteer from 'puppeteer';
import { pathToFileURL } from 'node:url';
import { resolve } from 'node:path';
const inputPath = resolve('template.html');
const outputPath = resolve('output.pdf');
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto(pathToFileURL(inputPath).href, {
waitUntil: 'networkidle0',
timeout: 30_000
});
// Ensure the document's web fonts are ready before export.
await page.evaluate(() => document.fonts.ready);
await page.pdf({
path: outputPath,
format: 'A4',
printBackground: true,
margin: {
top: '18mm',
right: '16mm',
bottom: '18mm',
left: '16mm'
}
});
} finally {
await browser.close();
}
Run it from the project directory with node convert.mjs. The script closes the browser in a finally block so a rendering exception does not leave the browser open. In a server, keep the browser lifecycle under a worker or request handler that also reports errors and cleans up resources.
Use the right HTML source
For a public page, use page.goto(url) and wait for the application’s actual ready condition. For HTML assembled in memory, use page.setContent(html), then wait for required resources or a template-specific selector. Remote stylesheets, images, and fonts need to be reachable from the rendering environment. A page that appears loaded before an image or client-side data is ready can produce an incomplete PDF.
networkidle0 is a useful option for pages that settle, but it is not a universal readiness signal. Analytics, polling, and persistent connections can prevent network idle; delayed scripts can also run after network activity quiets. If the template has a known completion marker, wait for that selector or an application-level ready signal, with a timeout. Keep a bounded timeout so a broken resource does not hang a job indefinitely.
3. Configure print layout and PDF options
PDF export uses print media by default. That means @media print rules can alter what appears, and screen-only layout may not carry over. If the template is intentionally designed for a screen viewport, switch media before export:
await page.emulateMediaType('screen');
await page.pdf({ path: 'screen-layout.pdf', printBackground: true });
Common layout choices include:
- Paper size: select a standard format such as A4 or Letter, or provide width and height with units when the output needs a custom page size.
- Margins: specify each edge with units. Check whether the page’s CSS and configured margins leave enough room for headers, footers, and content.
- Backgrounds: enable background printing when colored blocks or images are part of the design. Print color handling can modify colors; Puppeteer documents
-webkit-print-color-adjustfor preserving exact colors where appropriate. - CSS page size: use the engine’s CSS page-size option when
@pagerules should control paper dimensions; consult the current reference before relying on defaults. - Headers and footers: use the library’s documented templates and options. Playwright notes that scripts in header/footer templates do not execute and page styles are not visible inside those templates.
- Media mode: choose print for print styles, or emulate screen before creating the PDF when screen styles are required.
For predictable pagination, add print-specific CSS to the template and test it with real content lengths:
@media print {
.screen-only { display: none !important; }
.keep-together { break-inside: avoid; }
.new-page { break-before: page; }
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
@page {
size: A4;
margin: 18mm 16mm;
}
CSS page-break behavior depends on element structure and available space. A table row taller than a page, a large unbreakable block, or a forced break near the end of a page can create blank space or awkward splits. Test short and long versions of each template, not only the sample record.
4. Send HTML to a hosted PDF API
Hosted providers document different request schemas; do not assume their parameters or response formats are interchangeable. Keep credentials on your server, use placeholders in examples and configuration, and check current provider documentation for authentication, input limits, timeouts, retention, and output delivery.
For example, PDF.co documents a raw HTML conversion endpoint and a separate template endpoint. DocRaptor documents a JSON request that can carry document content or a URL. APITemplate.io documents raw HTML and reusable-template methods. These are examples of integration shapes, not a ranking or a claim that their output is equivalent.
Raw HTML versus stored template
A raw-HTML request sends markup with each conversion, which can suit a service generating one-off documents from application data. A stored-template request keeps markup at the provider and sends a template identifier plus data, which can suit repeated document layouts. In either model, escape or encode untrusted values before inserting them into HTML. Treat generated documents as sensitive if they contain personal or business data, and check the provider’s current handling and retention terms.
For the exact payload and response contract, follow the provider’s current API reference: PDF.co raw HTML, PDF.co template conversion, DocRaptor document creation, DocRaptor API reference, and APITemplate.io generation methods.
Handle binary and asynchronous responses
A successful request may return PDF bytes directly, a temporary download URL, or a job/status identifier. Build the client around the documented response rather than assuming every response is a PDF. For asynchronous conversion, persist the job identifier, poll or receive the documented callback, distinguish completion from failure, then retrieve the result before its documented link expires. PDF.co and APITemplate.io document async paths; DocRaptor documents async status and callback behavior. Verify current retention and limits with the provider.
Callbacks should be authenticated or verified according to the provider’s current instructions. Make completion handling idempotent: a callback or retry should not create duplicate downstream records. Record a job state, request correlation ID, completion time, and a safe error summary; avoid logging API keys or full sensitive HTML.
5. Validate output before shipping
- List representative cases. Include the shortest and longest content, optional sections, empty fields, long tables, unusual characters, and each supported locale.
- Check assets. Confirm fonts and images load from the deployed rendering environment, not just a developer laptop.
- Inspect page boundaries. Look for clipped text, orphaned headings, split rows, blank pages, and unexpected overflow.
- Check visual intent. Confirm colors, backgrounds, paper size, margins, page numbers, and header/footer placement.
- Test failure paths. Simulate a missing image, slow asset, malformed HTML, timeout, provider error, and async failure. Confirm the application reports a useful status and releases resources.
- Repeat after changes. Browser versions, fonts, CSS, and provider behavior can change. Re-render a small set of representative documents when these inputs change.
The official library and provider documentation establishes available controls, not that a particular template will render correctly. Keep sample PDFs or visual checks in your release process for important documents.

6. Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| PDF is missing styles | Stylesheet URL is inaccessible, relative paths resolve from the wrong base, or the page exported before assets loaded. | Use an absolute reachable URL or correct base URL; wait for the needed stylesheet or template-ready marker. |
| Fonts differ or text wraps unexpectedly | Font files failed, the requested face is not loaded, or export began before fonts were ready. | Check font network access and CSS declarations; wait for document.fonts.ready; compare the deployed environment. |
| Background colors are absent | Background printing is disabled or print color adjustment changes the result. | Enable the library’s background option and use print color adjustment CSS where suitable. |
| Screen design becomes a print layout | Print media is the PDF default. | Add print styles or emulate screen media before export. |
| Navigation or export hangs | Network idle never occurs, a resource stalls, or a page waits on ongoing activity. | Use a bounded timeout and a specific selector or readiness signal instead of an unbounded wait. |
| Table content clips or breaks badly | Rows or blocks exceed page space, or print CSS lacks break rules. | Test long data, adjust column widths and font sizes, and tune break behavior; do not rely on one sample. |
| Hosted call returns JSON instead of a PDF | The API accepted an async job or returned an error/status payload. | Read status and job fields, then follow the documented poll, callback, and result retrieval flow. |
| Hosted download link no longer works | The provider returned a temporary link that expired. | Retrieve and store the output promptly under your retention policy; check current link expiry settings. |
7. Performance, reliability, and cost
Browser rendering consumes application resources and adds browser lifecycle work. Measure your own template mix, concurrency, memory use, and render duration before choosing worker sizing; the documentation reviewed here does not establish a universal speed comparison. Reuse browser processes carefully if your architecture supports it, isolate jobs that can hang, and close pages or browsers on both success and error paths.
A hosted API removes browser process management from your application but adds a network call, provider-specific limits, service availability dependency, and output retrieval or retention decisions. For either route, use bounded timeouts, retry only transient failures, and make retries safe so they do not duplicate billing or downstream work. Persist job state for asynchronous requests and expose a meaningful failure to the caller.
Cost depends on the chosen provider or the infrastructure you operate, document volume, concurrency, and whether retries or stored output incur charges. The sources here do not provide a comparable current cost or performance benchmark, so estimate from your real workload and current pricing pages. Check request-size caps, maximum render time, retention, and plan limits before launch.
8. Or skip the browser setup
If your task is capturing a web page as an image or PDF rather than converting a designed HTML document template, ScreenshotNeo is a website screenshot API with a PDF option. It is a different job from a full HTML-template-to-PDF document pipeline; use it when a rendered page capture is what you need. The API base is https://api.screenshotneo.com/v1/shot. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
ScreenshotNeo can remove cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Every feature is on every plan. To capture a page as PDF, request the documented PDF output format. For a custom HTML template that must be paginated as a formal document, use a PDF renderer and validate its output.
Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.
FAQ
Can I convert a template with data without storing the HTML remotely?
Yes. Render the assembled HTML in your own browser automation process, or use a provider’s raw-HTML endpoint if its contract suits the document. Keep untrusted values escaped before putting them into markup.
Why does my PDF have different colors from the browser?
PDF export uses print media by default, and print color handling can change colors. Check the active media type, background option, print CSS, and color-adjust rules.
Should every conversion be asynchronous?
No. Use the provider’s documented synchronous response when it fits your latency and payload needs. Add an async flow when rendering duration or request handling constraints call for a background job.
How do I know which API provider renders my template best?
There is no comparative benchmark established here. Test the same representative templates and data against each candidate, then compare fidelity, limits, output handling, operational needs, and current terms.


