Need to generate PDFs from web pages or HTML templates in Node.js? Whether it's invoices, reports, or documentation snapshots, this tutorial shows you how to do it in minutes using the URLSnap API — no Puppeteer, no wkhtmltopdf binary, no headless Chromium to manage.
No extra packages needed if you're on Node 18+. We'll use the built-in fetch API. For older Node versions, you can use axios or node-fetch:
# Optional — only needed for Node < 18
npm install axios
const res = await fetch('https://urlsnap.dev/api/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'you@example.com' }),
});
const data = await res.json();
console.log(data);
// { key: 'us_abc123...', message: 'Free tier: 20 requests/day...' }
import fs from 'fs/promises';
const API_KEY = 'us_your_key_here';
async function urlToPdf(url, outputPath, options = {}) {
const params = new URLSearchParams({ url, ...options });
const response = await fetch(
`https://urlsnap.dev/api/pdf?${params}`,
{ headers: { 'x-api-key': API_KEY } }
);
if (!response.ok) {
const err = await response.json();
throw new Error(err.error || `HTTP ${response.status}`);
}
const buffer = Buffer.from(await response.arrayBuffer());
await fs.writeFile(outputPath, buffer);
console.log(`Saved PDF to ${outputPath} (${(buffer.length / 1024).toFixed(1)} KB)`);
}
// Basic usage
await urlToPdf('https://example.com', 'page.pdf');
// Letter-size landscape
await urlToPdf(
'https://en.wikipedia.org/wiki/Node.js',
'nodejs-wiki.pdf',
{ format: 'Letter', landscape: 'true', margin: '15px' }
);
Post an HTML string directly — great for rendering Handlebars/EJS invoice templates:
import fs from 'fs/promises';
const API_KEY = 'us_your_key_here';
async function htmlToPdf(html, outputPath) {
const response = await fetch('https://urlsnap.dev/api/pdf', {
method: 'POST',
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ html, format: 'A4' }),
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.error || `HTTP ${response.status}`);
}
const buffer = Buffer.from(await response.arrayBuffer());
await fs.writeFile(outputPath, buffer);
}
// Example: render a simple invoice
const invoiceHtml = `
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: sans-serif; padding: 40px; color: #111; }
h1 { color: #6366f1; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { padding: 10px; border-bottom: 1px solid #eee; text-align: left; }
.total { font-weight: bold; }
</style>
</head>
<body>
<h1>Invoice #INV-2026-001</h1>
<p>Bill to: Acme Corp</p>
<table>
<tr><th>Item</th><th>Amount</th></tr>
<tr><td>API Starter Plan</td><td>$9.00</td></tr>
<tr class="total"><td>Total</td><td>$9.00</td></tr>
</table>
</body>
</html>
`;
await htmlToPdf(invoiceHtml, 'invoice.pdf');
console.log('Invoice saved!');
Serve PDFs directly to users in an Express app:
import express from 'express';
const app = express();
const API_KEY = 'us_your_key_here';
app.get('/download-pdf', async (req, res) => {
const { url } = req.query;
if (!url) return res.status(400).json({ error: 'url required' });
try {
const upstream = await fetch(
`https://urlsnap.dev/api/pdf?url=${encodeURIComponent(url)}&format=A4`,
{ headers: { 'x-api-key': API_KEY } }
);
if (!upstream.ok) {
const err = await upstream.json();
return res.status(upstream.status).json(err);
}
res.set('Content-Type', 'application/pdf');
res.set('Content-Disposition', 'attachment; filename="page.pdf"');
// Stream the response directly to the client
const reader = upstream.body.getReader();
const stream = new ReadableStream({
start(controller) {
function push() {
reader.read().then(({ done, value }) => {
if (done) { controller.close(); return; }
controller.enqueue(value);
push();
});
}
push();
}
});
const nodeStream = require('stream').Readable.fromWeb(stream);
nodeStream.pipe(res);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.listen(3000, () => console.log('Server running on port 3000'));
Generate multiple PDFs concurrently with Promise.allSettled:
import fs from 'fs/promises';
const API_KEY = 'us_your_key_here';
async function urlToPdf(url, outputPath) {
const params = new URLSearchParams({ url, format: 'A4' });
const response = await fetch(
`https://urlsnap.dev/api/pdf?${params}`,
{ headers: { 'x-api-key': API_KEY } }
);
if (!response.ok) throw new Error(`Failed for ${url}: HTTP ${response.status}`);
const buffer = Buffer.from(await response.arrayBuffer());
await fs.writeFile(outputPath, buffer);
return outputPath;
}
const pages = [
{ url: 'https://example.com', out: 'example.pdf' },
{ url: 'https://httpbin.org/html', out: 'httpbin.pdf' },
{ url: 'https://urlsnap.dev', out: 'urlsnap.pdf' },
];
const results = await Promise.allSettled(
pages.map(({ url, out }) => urlToPdf(url, out))
);
results.forEach((r, i) => {
if (r.status === 'fulfilled') console.log(`✓ ${pages[i].out}`);
else console.error(`✗ ${pages[i].url}:`, r.reason.message);
});
const info = await fetch('https://urlsnap.dev/api/me', {
headers: { 'x-api-key': API_KEY },
}).then(r => r.json());
console.log(`Plan: ${info.plan}`);
console.log(`Used today: ${info.requests_today}/${info.daily_limit}`);
console.log(`Total all-time: ${info.requests_total}`);
async function safePdf(url, outputPath, apiKey) {
try {
const resp = await fetch(
`https://urlsnap.dev/api/pdf?url=${encodeURIComponent(url)}`,
{ headers: { 'x-api-key': apiKey }, signal: AbortSignal.timeout(60_000) }
);
if (resp.status === 429) {
console.warn('Daily limit reached. Upgrade at urlsnap.dev/#pricing');
return false;
}
if (resp.status === 401) {
console.error('Invalid API key.');
return false;
}
if (!resp.ok) {
const e = await resp.json().catch(() => ({}));
throw new Error(e.error || `HTTP ${resp.status}`);
}
const buf = Buffer.from(await resp.arrayBuffer());
await fs.writeFile(outputPath, buf);
return true;
} catch (err) {
if (err.name === 'TimeoutError') console.error('Request timed out');
else console.error('PDF error:', err.message);
return false;
}
}
Free tier: 20 PDFs/day. No credit card, no install, no config.
Get your free API key →