HTML to PDF in Node.js
One POST, a PDF back. No headless browser to install, no Chromium to keep patched, no fonts to install on the box — the render happens on a warm browser that is already running.
With the official client
There is a published Node.js client, so the shortest version is two lines. It has no dependencies of its own, retries 429s and 5xxs for you, and turns the API’s error envelope into a typed error you can branch on. The raw HTTP below still works and is still supported — plenty of people would rather send a POST than take a dependency.
npm install @pdfcraft-dev/pdfimport { writeFile } from 'node:fs/promises';
import { Renderer } from '@pdfcraft-dev/pdf';
const pdfcraft = new Renderer(process.env.PDFCRAFT_API_KEY);
await writeFile('invoice.pdf', await pdfcraft.render({ html: '<h1>Invoice 1042</h1>' }));On npm.
Render HTML to a PDF with plain HTTP
import { writeFile } from 'node:fs/promises';
const response = await fetch('https://api.pdfcraft.dev/v1/render', {
method: 'POST',
headers: {
authorization: `Bearer ${process.env.PDFCRAFT_API_KEY}`,
'content-type': 'application/json',
},
body: JSON.stringify({
html: '<h1>Invoice 1042</h1>',
options: { printBackground: true, margin: { top: '20mm', bottom: '20mm' } },
}),
});
if (!response.ok) {
const { error } = await response.json();
throw new Error(`${error.code}: ${error.message}`);
}
await writeFile('invoice.pdf', Buffer.from(await response.arrayBuffer()));Read a PDF back as JSON
The same key works for extraction. Send a PDF, name the fields you want by the label printed on the page, and get them back with the raw text, a coerced value, a confidence and a bounding box.
const pdf = await readFile('statement.pdf');
const response = await fetch('https://api.pdfcraft.dev/v1/extract', {
method: 'POST',
headers: {
authorization: `Bearer ${process.env.PDFCRAFT_API_KEY}`,
'content-type': 'application/json',
},
body: JSON.stringify({
file: pdf.toString('base64'),
schema: { account_number: 'string', closing_balance: 'currency' },
options: { rows_as_objects: true },
}),
});
const { fields, tables } = await response.json();
console.log(fields.closing_balance.value, fields.closing_balance.currency);
for (const row of tables[0].rows_as_objects) console.log(row);What bites in Node.js
- There is a first-party SDK — `npm i @pdfcraft-dev/pdf` — with zero dependencies and typed options. The fetch version above is here because plenty of people would rather not add a package for one endpoint.
- `response.arrayBuffer()` then `Buffer.from`, not `response.text()`. Reading a PDF as text corrupts it silently: you get a file of the right length that no reader will open.
- Node 18+ has fetch built in. On 16 you need undici or node-fetch.
Errors are one shape
Every failure is {"error":{"code","message","docs_url"}} with a stable code, so you can switch on error.code rather than parsing prose. The two worth handling explicitly are rate_limited — honour Retry-After — and render_failed, which means your HTML broke rather than ours did.
The same thing in another language
- HTML to PDF in Python
- HTML to PDF in Go
- HTML to PDF in PHP
- HTML to PDF in Ruby
- HTML to PDF in Java
- HTML to PDF in C#
- HTML to PDF in Rust
- HTML to PDF in curl
- HTML to PDF in Deno
- HTML to PDF in Bun
- HTML to PDF in TypeScript
Or try it with no code at all in the playground. A free key is 100 renders a month.