HTML to PDF in TypeScript
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 TypeScript 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 { Renderer, type RenderInput } from '@pdfcraft-dev/pdf';
const pdfcraft = new Renderer(process.env.PDFCRAFT_API_KEY!);
const input: RenderInput = { html: '<h1>Invoice 1042</h1>' };
const pdf: Uint8Array = await pdfcraft.render(input);On npm.
Render HTML to a PDF with plain HTTP
import { Renderer } from '@pdfcraft-dev/pdf';
const renderer = new Renderer(process.env.PDFCRAFT_API_KEY!);
// Buffer back
const pdf = await renderer.render({
html: '<h1>Invoice 1042</h1>',
options: { printBackground: true, margin: { top: '20mm' } },
});
// Or a signed URL, if you would rather not move the bytes
const { url, expires_at } = await renderer.renderToUrl({ url: 'https://example.com' });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 result = await renderer.extract({
file: pdfBuffer,
schema: {
account_number: 'string',
closing_balance: 'currency',
statement_date: 'date',
},
options: { rows_as_objects: true },
});
// Typed: value is string | number | boolean | null, and currency is present
// only for the fields you asked for as currency.
result.fields.closing_balance.value;
result.tables[0].header; // string[] | null
result.tables[0].header_confidence; // 0-1What bites in TypeScript
- Zero dependencies, and the option types are generated from the same contract the server validates against — so an option the server would reject does not compile.
- `header` is `string[] | null` on purpose. It is null when the evidence did not reach the bar, and a type that pretended otherwise would push the check into your runtime.
- Install with `npm i @pdfcraft-dev/pdf`. ESM and CJS builds both ship.
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 Node.js
- 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
Or try it with no code at all in the playground. A free key is 100 renders a month.