PDFPipe

Guide

HTML to PDF in AWS Lambda

The standard advice for PDF generation in Lambda is to bundle a Chromium binary using @sparticuz/chromium or a wkhtmltopdf layer. Both add 50–150 MB to your deployment package, push cold-start times past 5 seconds, and require glibc-compatible binaries that break on newer Amazon Linux runtimes. The simpler path is an HTTP call to a hosted render API. Your Lambda stays small, cold-start stays fast, and the rendering happens outside your function entirely.

Prerequisites

  • A PDFPipe API key (free tier: 500 documents/month, no card required)
  • The key stored as a Lambda environment variable: PDFPIPE_API_KEY
  • Node.js 18+ runtime (or any runtime with fetch)

Returning the PDF directly from API Gateway

When your Lambda is fronted by API Gateway with binary media types enabled, you can stream the PDF bytes as a base64-encoded body directly to the caller.

handler.ts (Node.js runtime)
import type { APIGatewayProxyHandlerV2 } from "aws-lambda";

export const handler: APIGatewayProxyHandlerV2 = async (event) => {
  const { html, filename = "document.pdf" } = JSON.parse(event.body ?? "{}");

  const res = await fetch("https://api.pdfpipe.xyz/v1/pdf", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.PDFPIPE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ html, options: { format: "A4" } }),
  });

  if (!res.ok) {
    const { detail } = await res.json() as { detail: string };
    return { statusCode: 502, body: detail };
  }

  const bytes = new Uint8Array(await res.arrayBuffer());
  const b64 = Buffer.from(bytes).toString("base64");

  return {
    statusCode: 200,
    isBase64Encoded: true,
    headers: {
      "Content-Type": "application/pdf",
      "Content-Disposition": `attachment; filename="${filename}"`,
    },
    body: b64,
  };
};

Enable binary media types in API Gateway: open your API, go to Settings, and add application/pdf to the Binary Media Types list. Without this, API Gateway base64-decodes the body automatically and returns the PDF bytes to the client.

Saving the PDF to S3

For async workloads (order confirmations, monthly statements), render the PDF and write it to S3 so other services can consume it.

handler.ts: S3 variant
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({});

export const handler = async (event: { orderId: string; html: string }) => {
  const res = await fetch("https://api.pdfpipe.xyz/v1/pdf", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.PDFPIPE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ html: event.html, options: { format: "A4" } }),
  });

  if (!res.ok) {
    const { detail } = await res.json() as { detail: string };
    throw new Error(`Render failed: ${detail}`);
  }

  const bytes = Buffer.from(await res.arrayBuffer());

  await s3.send(new PutObjectCommand({
    Bucket: process.env.BUCKET_NAME!,
    Key: `invoices/${event.orderId}.pdf`,
    Body: bytes,
    ContentType: "application/pdf",
  }));

  return { key: `invoices/${event.orderId}.pdf` };
};

Using the PDFPipe document store

If your use case is generating documents and sharing download links, you can skip the S3 step entirely. Add store: true to the request body and PDFPipe stores the PDF on its CDN. You get back a document ID and a pre-signed URL valid for your plan's retention window.

handler.ts: stored document variant
const res = await fetch("https://api.pdfpipe.xyz/v1/pdf", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PDFPIPE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    html: event.html,
    options: { format: "A4" },
    store: true,
    filename: `invoice-${event.orderId}.pdf`,
  }),
});

const { id, url } = await res.json() as { id: string; url: string };
// Send url to the customer, store id for re-download

Deployment size comparison

ApproachPackage sizeCold start
@sparticuz/chromium (Puppeteer)~50 MB compressed3–8 s
wkhtmltopdf Lambda layer~50 MB layer2–5 s
PDFPipe API call~0 MB (no binary)~100 ms

Get started free

500 documents a month, no card required. Your API key is issued immediately after signup.

Get your API key →