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);