A wkhtmltopdf alternative: headless Chromium over an API
wkhtmltopdf's own status page and repository tell you where the project stands. This guide maps its common options to a headless Chromium PDF API and shows the same job as code you can run.
Why people are moving
- The wkhtmltopdf GitHub repository shows "archived by the owner on Jan 2, 2023" and is read-only. Source: github.com/wkhtmltopdf/wkhtmltopdf, checked 2026-10-09.
- Its status page says QtWebKit, the engine it uses, was deprecated in 2015 and removed in 2016, and tells you not to use wkhtmltopdf with untrusted HTML. Source: wkhtmltopdf.org/status.html, checked 2026-10-09.
- This service renders with headless Chromium, which supports current CSS such as flexbox and grid layout, and runs your page's JavaScript.
None of this means your existing PDFs are wrong. It is a reason to plan a move, and to test your own templates before you do.
Option mapping
wkhtmltopdf options are from its usage page (wkhtmltopdf.org/usage/wkhtmltopdf.txt, checked 2026-10-09). The API options go inside options of POST /v1/render.
| wkhtmltopdf | API option | Note |
|---|---|---|
-s, --page-size A4 | format: "A4" | Also width and height for custom sizes (wkhtmltopdf: --page-width, --page-height). |
-O Landscape | landscape: true | |
-T / -B / -L / -R | margin: { top, bottom, left, right } | wkhtmltopdf lists a default of 10 mm for left and right. Ours defaults to 10 mm on all four sides. |
--print-media-type | media: "print" | wkhtmltopdf uses screen styles unless you pass this flag. Ours prints with print styles by default, so send media: "screen" to keep the wkhtmltopdf behaviour. |
--background / --no-background | print_background: true | false | On by default in both. |
--javascript-delay <ms> | delay_ms (up to 10000) | Also wait_until and wait_for_selector. These are not exact equivalents. |
--header-html, --footer-html | display_header_footer: true, header_template, footer_template | Templates are HTML strings. Page numbers use spans with the classes pageNumber and totalPages. Leave room in the margins. |
--zoom <float> | scale | The closest equivalent. Check the result against your layout. |
--disable-smart-shrinking | no direct equivalent | Compare your output and adjust the CSS. |
Before and after
A typical wkhtmltopdf call:
wkhtmltopdf --page-size A4 --orientation Portrait \
--margin-top 20mm --margin-bottom 20mm --margin-left 15mm --margin-right 15mm \
report.html report.pdfThe same job against the API. Set AIIPWORLD_API_KEY to a key from your dashboard. The cURL version needs jq.
import { readFile, writeFile } from "node:fs/promises";
const API = "https://api.aiipworld.com";
const headers = { Authorization: `Bearer ${process.env.AIIPWORLD_API_KEY}`, "Content-Type": "application/json" };
const html = await readFile("report.html", "utf8");
const submit = await fetch(`${API}/v1/render`, {
method: "POST",
headers,
body: JSON.stringify({
html,
output: "pdf",
options: {
"format": "A4",
"landscape": false,
"media": "screen",
"margin": { "top": "20mm", "bottom": "20mm", "left": "15mm", "right": "15mm" }
},
}),
});
const { job_id } = await submit.json();
let job;
do {
await new Promise((r) => setTimeout(r, 500));
job = await (await fetch(`${API}/v1/jobs/${job_id}`, { headers })).json();
} while (job.status === "queued" || job.status === "running");
if (job.status !== "succeeded") throw new Error(`${job.status}: ${job.error}`);
const pdf = await (await fetch(job.download_url)).arrayBuffer();
await writeFile("report.pdf", Buffer.from(pdf));Headers and footers: add display_header_footer: true with header_template and footer_template, as in the PDF API example. The sample below was made that way.
Example output

Things to check when you switch
- Relative files. The API loads public http and https addresses only, not files on your disk. Inline images and CSS with data URLs, use absolute https URLs, or send
base_url. - Fonts. Fonts on your own server are not on ours. The render server has the Noto and Liberation families and colour emoji; for others use
@font-facewith a public or data URL. - Page breaks. Use
break-inside: avoidon rows or blocks you do not want split. A table header set todisplay: table-header-grouprepeats on each page; we checked that with a multi-page table. - Layout differences. A different engine means small differences. Compare real documents side by side.
- Where the HTML goes. It is sent to our servers and deleted after the retention time of your plan. Keep documents that cannot leave your network on your own infrastructure.
Prefer to run it yourself? Headless Chromium driven by Playwright or Puppeteer is the common self-hosted route. Our HTML to PDF tool lets you try the output without an account.
Questions
Is wkhtmltopdf still maintained?
Its GitHub repository was archived by the owner on 2 January 2023 and is read-only (checked 2026-10-09 on github.com/wkhtmltopdf/wkhtmltopdf). Its status page also says it uses Qt WebKit, which was deprecated in 2015 and removed in 2016, and advises against using it with untrusted HTML (wkhtmltopdf.org/status.html, checked 2026-10-09).
Will my existing HTML render the same?
Not necessarily. The rendering engine is different, so layouts can shift. Run your real templates through both and compare before you switch, and fix differences in your CSS.
How do I convert a local HTML file with relative images and CSS?
The API loads public http and https addresses only, not files on your machine. Inline images and styles with data URLs, point them at absolute https URLs, or send base_url so relative paths resolve against a public site.
Are my fonts available?
Fonts installed on your own server are not on ours. The render server has the Noto and Liberation font families and colour emoji. For other fonts, load them with @font-face from a public URL or a data URL.
Do I have to send my HTML to a third party?
With the API, yes: the HTML is sent to our servers and deleted after the retention period of your plan. If that is not acceptable for your documents, running headless Chromium yourself is the alternative.
How do page numbers work?
Pass footer_template HTML containing spans with the classes pageNumber and totalPages, set display_header_footer to true and leave room in the top and bottom margins.