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.

8 min read
TutorialAPI

#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" }
  }'

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:

Related posts

9 min read

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.

TutorialServerless
10 min read

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.

TutorialUse Case