JavaScript Examples
Example code for using PDF API with JavaScript and Node.js.
JavaScript Examples
Use PDF API with Node.js, browsers, and popular frameworks.
Node.js with fetch
import { writeFile } from 'fs/promises';
async function generatePDF() {
const response = await fetch('https://api.renderpdf.dev/v1/pdf/markdown', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
markdown: `# Monthly Report
## Summary
- Total users: 1,234
- Revenue: $45,678
## Details
| Metric | Value |
|--------|-------|
| New signups | 156 |
| Churn rate | 2.3% |`,
theme: 'corporate',
options: {
format: 'A4',
margin: { top: '25mm', bottom: '25mm' }
}
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error.message);
}
const buffer = await response.arrayBuffer();
await writeFile('report.pdf', Buffer.from(buffer));
console.log('PDF saved to report.pdf');
}
generatePDF();
Browser Download
async function downloadPDF() {
const response = await fetch('https://api.renderpdf.dev/v1/pdf/markdown', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_xxx', // Use backend proxy in production!
'Content-Type': 'application/json',
},
body: JSON.stringify({
markdown: '# Hello World\n\nThis is my PDF!',
theme: 'default',
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error.message);
}
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'document.pdf';
a.click();
URL.revokeObjectURL(url);
}
Never expose API keys in client-side code. Use a backend proxy to make API calls.
Express.js Server
import express from 'express';
const app = express();
app.use(express.json());
app.post('/api/generate-pdf', async (req, res) => {
const { markdown, theme = 'default' } = req.body;
try {
const response = await fetch('https://api.renderpdf.dev/v1/pdf/markdown', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ markdown, theme }),
});
if (!response.ok) {
const error = await response.json();
return res.status(response.status).json(error);
}
const buffer = await response.arrayBuffer();
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'attachment; filename=document.pdf');
res.send(Buffer.from(buffer));
} catch (error) {
res.status(500).json({ error: { message: 'PDF generation failed' } });
}
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
Next.js API Route
// app/api/pdf/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const { markdown, theme } = await request.json();
const response = await fetch('https://api.renderpdf.dev/v1/pdf/markdown', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ markdown, theme }),
});
if (!response.ok) {
const error = await response.json();
return NextResponse.json(error, { status: response.status });
}
const buffer = await response.arrayBuffer();
return new NextResponse(buffer, {
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename=document.pdf',
},
});
}
TypeScript Types
interface PDFOptions {
format?: 'A4' | 'A3' | 'A5' | 'Letter' | 'Legal' | 'Tabloid';
landscape?: boolean;
margin?: {
top?: string;
bottom?: string;
left?: string;
right?: string;
};
print_background?: boolean;
scale?: number;
header?: string;
footer?: string;
}
interface MarkdownToPDFRequest {
markdown: string;
theme?: string;
options?: PDFOptions;
}
interface APIError {
error: {
code: string;
message: string;
details?: Record<string, unknown>;
};
}
async function generatePDF(request: MarkdownToPDFRequest): Promise<ArrayBuffer> {
const response = await fetch('https://api.renderpdf.dev/v1/pdf/markdown', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(request),
});
if (!response.ok) {
const error: APIError = await response.json();
throw new Error(error.error.message);
}
return response.arrayBuffer();
}
Error Handling with Retry
async function generatePDFWithRetry(markdown, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch('https://api.renderpdf.dev/v1/pdf/markdown', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ markdown }),
});
// Handle rate limiting
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
console.log(`Rate limited. Waiting ${retryAfter}s...`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
if (!response.ok) {
const error = await response.json();
throw new Error(error.error.message);
}
return await response.arrayBuffer();
} catch (error) {
if (attempt === maxRetries) throw error;
// Exponential backoff
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
}
}
}
Batch Processing
async function batchGeneratePDFs(documents) {
const results = [];
for (const doc of documents) {
try {
// Check rate limit before each request
const usage = await fetch('https://api.renderpdf.dev/v1/usage', {
headers: { 'Authorization': `Bearer ${process.env.PDFAPI_KEY}` },
}).then(r => r.json());
if (usage.data.rate_limit.current_usage >= usage.data.rate_limit.requests_per_minute - 1) {
console.log('Approaching rate limit, waiting...');
await new Promise(r => setTimeout(r, 60000));
}
const buffer = await generatePDF(doc);
results.push({ success: true, buffer });
} catch (error) {
results.push({ success: false, error: error.message });
}
// Small delay between requests
await new Promise(r => setTimeout(r, 100));
}
return results;
}
React Hook
import { useState, useCallback } from 'react';
interface UsePDFResult {
generatePDF: (markdown: string, theme?: string) => Promise<void>;
loading: boolean;
error: string | null;
}
export function usePDF(): UsePDFResult {
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const generatePDF = useCallback(async (markdown: string, theme = 'default') => {
setLoading(true);
setError(null);
try {
const response = await fetch('/api/pdf', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ markdown, theme }),
});
if (!response.ok) {
const data = await response.json();
throw new Error(data.error.message);
}
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'document.pdf';
a.click();
URL.revokeObjectURL(url);
} catch (err) {
setError(err instanceof Error ? err.message : 'Generation failed');
} finally {
setLoading(false);
}
}, []);
return { generatePDF, loading, error };
}
Merge PDFs
import fs from 'fs';
async function mergePDFs(pdfPaths) {
// Read and encode PDFs as base64
const pdfs = pdfPaths.map(path => ({
data: fs.readFileSync(path).toString('base64')
}));
const response = await fetch('https://api.pdfapi.dev/v1/pdf/merge', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
pdfs,
storage: {
enabled: true,
filename: 'merged-document.pdf'
}
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error.message);
}
const fileId = response.headers.get('X-File-ID');
const buffer = await response.arrayBuffer();
return { buffer, fileId };
}
// Usage
const { buffer, fileId } = await mergePDFs([
'cover.pdf',
'content.pdf',
'appendix.pdf'
]);
fs.writeFileSync('merged.pdf', Buffer.from(buffer));
console.log('File ID:', fileId);
PDF with Compression
async function generateCompressedPDF(markdown) {
const response = await fetch('https://api.pdfapi.dev/v1/pdf/markdown', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
markdown,
options: {
format: 'A4',
compression: {
enabled: true,
level: 'medium' // 'none', 'low', 'medium', 'high'
}
}
}),
});
return response.arrayBuffer();
}
Set PDF Metadata
import fs from 'fs';
async function setPDFMetadata(pdfPath, metadata) {
const pdfBase64 = fs.readFileSync(pdfPath).toString('base64');
const response = await fetch('https://api.pdfapi.dev/v1/pdf/metadata', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
pdf: pdfBase64,
metadata: {
title: metadata.title,
author: metadata.author,
subject: metadata.subject,
keywords: metadata.keywords
}
}),
});
return response.arrayBuffer();
}
// Usage
const buffer = await setPDFMetadata('document.pdf', {
title: 'Q4 2025 Report',
author: 'Finance Team',
subject: 'Quarterly Financial Summary',
keywords: 'finance, quarterly, 2025'
});
fs.writeFileSync('document-with-metadata.pdf', Buffer.from(buffer));
File Storage Operations
// List stored files
async function listFiles(page = 1, limit = 20) {
const response = await fetch(
`https://api.pdfapi.dev/v1/files?page=${page}&limit=${limit}`,
{
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
},
}
);
return response.json();
}
// Get file details
async function getFile(fileId) {
const response = await fetch(
`https://api.pdfapi.dev/v1/files/${fileId}`,
{
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
},
}
);
return response.json();
}
// Download file
async function downloadFile(fileId) {
const response = await fetch(
`https://api.pdfapi.dev/v1/files/${fileId}/download`,
{
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
},
}
);
return response.arrayBuffer();
}
// Delete file
async function deleteFile(fileId) {
const response = await fetch(
`https://api.pdfapi.dev/v1/files/${fileId}`,
{
method: 'DELETE',
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
},
}
);
return response.status === 204;
}
Webhook Configuration
// Configure webhook
async function configureWebhook(url, events, secret) {
const response = await fetch('https://api.pdfapi.dev/v1/webhooks/config', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url,
events, // ['pdf.generated', 'pdf.failed']
secret
}),
});
return response.json();
}
// Get webhook configuration
async function getWebhookConfig() {
const response = await fetch('https://api.pdfapi.dev/v1/webhooks/config', {
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
},
});
return response.json();
}
// Delete webhook configuration
async function deleteWebhookConfig() {
const response = await fetch('https://api.pdfapi.dev/v1/webhooks/config', {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${process.env.PDFAPI_KEY}`,
},
});
return response.status === 204;
}
Webhook Handler (Express.js)
import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.json());
function verifyWebhookSignature(payload, signature, timestamp, secret) {
const signedPayload = `${timestamp}.${JSON.stringify(payload)}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
app.post('/webhooks/pdfapi', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
if (!verifyWebhookSignature(req.body, signature, timestamp, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Respond immediately
res.status(200).send('OK');
// Process asynchronously
const { type, data } = req.body;
switch (type) {
case 'pdf.generated':
console.log('PDF generated:', data.file_id);
// Handle successful generation
break;
case 'pdf.failed':
console.log('PDF failed:', data.error);
// Handle failure
break;
}
});
app.listen(3000);