Convert Any URL to PDF with a Single API Call
Need to save a web page as a PDF? Whether you're archiving content, generating reports from dashboards, or creating offline-friendly documents, converting a URL to PDF is one of the most common developer tasks. With PDFLoom, it takes a single API call — no headless browsers to manage, no Puppeteer configuration, no Docker issues.
#Why convert URLs to PDF?
Before diving into code, here are some common reasons developers need URL-to-PDF conversion:
- Web archiving: Save a snapshot of a page at a specific point in time for legal or compliance purposes
- Report generation: Convert dashboards, analytics pages, or admin panels into shareable PDF reports
- Offline sharing: Turn styled web content into documents that work without an internet connection
- Compliance snapshots: Capture terms of service, privacy policies, or regulatory pages as dated records
- Content distribution: Package blog posts, documentation, or marketing pages as downloadable PDFs
All of these use cases boil down to the same operation: give the API a URL, get back a PDF. Let's see how.
#The quick version
Here's the simplest possible call to the PDFLoom API. Pass a URL, get a PDF.
cURL:
curl -X POST https://api.pdfloom.com/v1/convert/url \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'
Node.js:
const response = await fetch('https://api.pdfloom.com/v1/convert/url', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com',
}),
});
const data = await response.json();
console.log(data.response); // Pre-signed S3 URL to download the PDF
The response looks like this:
{
"success": true,
"response": "https://pdfloom-processed.s3.amazonaws.com/generated/pdf/71f1a...pdf?X-Amz-Expires=1800",
"fileSize": 6139
}
The response field contains a pre-signed S3 URL. Download the PDF from that URL — it expires after 30 minutes.
Get your API key
Sign up at pdfloom.com/register and create an API key from the dashboard. You get 50 free credits on email verification.
#Customizing your PDF output
The default conversion uses A4 paper with no margins. You can control page size, orientation, margins, headers, footers, and more through the options object.
#Paper size and orientation
const response = await fetch('https://api.pdfloom.com/v1/convert/url', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com/report',
options: {
format: 'Letter',
landscape: true,
},
}),
});
Supported formats include A4, Letter, Legal, Tabloid, and A3.
#Margins
Margins accept CSS units — mm, cm, in, or px:
{
url: 'https://example.com',
options: {
format: 'A4',
margin: {
top: '20mm',
right: '15mm',
bottom: '20mm',
left: '15mm',
},
},
}
#Headers and footers
Enable displayHeaderFooter and provide HTML templates. The templates support special CSS classes for dynamic values like page numbers:
{
url: 'https://example.com/report',
options: {
format: 'A4',
margin: { top: '25mm', bottom: '25mm' },
displayHeaderFooter: true,
headerTemplate: '<div style="font-size:10px; width:100%; text-align:center;">Confidential Report</div>',
footerTemplate: '<div style="font-size:10px; width:100%; text-align:center;">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>',
},
}
Margin space for headers/footers
Headers and footers render inside the margin area. If your margins are too small, the header and footer content will overlap the page content or get clipped. Use at least 20-25mm top/bottom margins when using headers or footers.
#Page ranges and backgrounds
{
url: 'https://example.com/long-document',
options: {
format: 'A4',
printBackground: true,
pageRanges: '1-3,8',
scale: 0.9,
},
}
printBackground: Include CSS background colors and images (off by default)pageRanges: Only include specific pages in the output (e.g.,"1-3,8")scale: Zoom factor between 0.1 and 2
#Capturing authenticated pages
Many useful pages sit behind a login. PDFLoom supports several authentication methods so the renderer can access protected content.
#Bearer token auth
If your target page accepts a bearer token, pass it in the auth object:
curl -X POST https://api.pdfloom.com/v1/convert/url \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.example.com/reports",
"auth": {
"type": "bearer",
"token": "YOUR_SESSION_TOKEN",
"wait": { "selector": "#reports-table", "waitUntil": "networkidle2" }
},
"options": { "format": "A4" }
}'
#Cookie auth
For session-based authentication, inject cookies directly:
{
url: 'https://app.example.com/dashboard',
auth: {
type: 'cookie',
cookies: [
{
name: 'session_id',
value: 'abc123',
domain: 'app.example.com',
path: '/',
secure: true,
httpOnly: true,
},
],
},
options: { format: 'A4', printBackground: true },
}
Keep tokens short-lived
Never pass long-lived production credentials. Generate a short-lived token or session specifically for PDF capture, and revoke it afterwards.
#Taking screenshots instead
Sometimes you need an image instead of a PDF — for social media previews, thumbnails, or embedding in other documents. The /v1/screenshot/url endpoint works the same way:
const response = await fetch('https://api.pdfloom.com/v1/screenshot/url', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com',
options: {
type: 'png',
fullPage: true,
},
}),
});
const data = await response.json();
console.log(data.response); // Pre-signed URL to the screenshot
The screenshot endpoint supports png, jpeg, and webp output formats, full-page capture, viewport clipping, and JPEG quality settings. See the screenshot documentation for all available options.
#Handling the response
Every successful API call returns a JSON object with three fields:
{
"success": true,
"response": "https://pdfloom-processed.s3.amazonaws.com/generated/pdf/...?X-Amz-Expires=1800",
"fileSize": 48210
}
#Downloading the PDF
The response URL is a pre-signed S3 link valid for 30 minutes. Download it immediately and store it yourself if you need permanent access:
const fs = require('fs');
const data = await response.json();
if (data.success) {
const pdfResponse = await fetch(data.response);
const buffer = Buffer.from(await pdfResponse.arrayBuffer());
fs.writeFileSync('output.pdf', buffer);
console.log(`Saved PDF (${data.fileSize} bytes)`);
}
#Error handling
When something goes wrong, the API returns success: false with a message and HTTP status code:
{
"success": false,
"message": "The url field must be a valid URL.",
"status": 422,
"details": { "url": ["The url format is invalid."] }
}
Common errors:
- 402: Insufficient credits
- 422: Validation error (bad URL, invalid options)
- 409: Duplicate idempotency key
- 503: Generator service temporarily unavailable
Use idempotency keys
Add an Idempotency-Key header to prevent duplicate PDF generation if your request is retried. The API will return the same result for the same key within a window.
#Full working example
Here's a complete Express.js endpoint that accepts a URL from the client and returns the generated PDF as a download:
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const PDFLOOM_API_KEY = process.env.PDFLOOM_API_KEY;
app.post('/api/generate-pdf', async (req, res) => {
const { url } = req.body;
if (!url) {
return res.status(400).json({ error: 'URL is required' });
}
try {
// Call PDFLoom API
const apiResponse = await fetch('https://api.pdfloom.com/v1/convert/url', {
method: 'POST',
headers: {
'Authorization': `Bearer ${PDFLOOM_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
url,
options: {
format: 'A4',
printBackground: true,
margin: { top: '10mm', right: '10mm', bottom: '10mm', left: '10mm' },
},
}),
});
const data = await apiResponse.json();
if (!data.success) {
return res.status(apiResponse.status).json({
error: data.message || 'PDF generation failed',
});
}
// Download the PDF from the pre-signed URL
const pdfResponse = await fetch(data.response);
const pdfBuffer = Buffer.from(await pdfResponse.arrayBuffer());
// Send the PDF to the client
const filename = `page-${Date.now()}.pdf`;
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
res.setHeader('Content-Length', pdfBuffer.length);
res.send(pdfBuffer);
} catch (error) {
console.error('PDF generation error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Test it with cURL:
curl -X POST http://localhost:3000/api/generate-pdf \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}' \
--output page.pdf
#Next steps
You now have everything you need to convert URLs to PDFs programmatically. Here's where to go from here:
- API documentation: Full reference for all convert and screenshot endpoints
- Create a free account: Get 50 credits on email verification to start testing
- Generate PDFs from HTML: If you need to convert raw HTML instead of URLs
- Generate Invoice PDFs: A practical use case with a complete HTML invoice template
Related posts
PDF Generation in Serverless Environments
Learn how to generate PDFs in serverless environments like Vercel, AWS Lambda, and Netlify. Includes working code examples and a comparison of approaches.
Puppeteer PDF: Common Problems and How to Fix Them
Troubleshoot Puppeteer PDF issues: timeouts, memory leaks, Docker problems, and font rendering. Working code examples and solutions.
Generate Invoice PDFs from HTML Templates
Learn how to create professional invoice PDFs using HTML templates and a PDF API. Includes a complete invoice template and Node.js code examples.