Back to Blog
Guide February 24, 2026 · 3 min read

How to Take Screenshots in AWS Lambda (Without Chromium Layers)

Getting Chromium to run in AWS Lambda is a rite of passage nobody enjoys. There's a simpler path: don't run Chromium in Lambda. Make an API call instead.

The standard approach: add @sparticuz/chromium or chrome-aws-lambda, configure a Lambda layer, stay under the 250MB deployment limit, fight cold start times, then discover Lambda's /tmp storage is too small for your screenshots.

The Lambda function

// handler.js
export const handler = async (event) => {
  const { url, filename } = event;

  const res = await fetch('https://api.pagebolt.dev/v1/screenshot', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.PAGEBOLT_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      url,
      fullPage: true,
      blockBanners: true
    })
  });

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

  // Save to S3, return base64, or pass downstream
  return {
    statusCode: 200,
    body: buffer.toString('base64'),
    isBase64Encoded: true,
    headers: { 'Content-Type': 'image/png' }
  };
};

Zero dependencies beyond Node's built-in fetch (available in Node 18+). Deployment package is kilobytes, not hundreds of megabytes. Cold starts are instant.

Save to S3

For screenshot pipelines that store results:

import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

const s3 = new S3Client({});

export const handler = async (event) => {
  const { url, key } = event;

  const res = await fetch('https://api.pagebolt.dev/v1/screenshot', {
    method: 'POST',
    headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ url, fullPage: true, blockBanners: true })
  });

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

  await s3.send(new PutObjectCommand({
    Bucket: process.env.S3_BUCKET,
    Key: key || `screenshots/${Date.now()}.png`,
    Body: buffer,
    ContentType: 'image/png'
  }));

  return { statusCode: 200, key };
};

Generate a PDF

Swap the endpoint:

body: JSON.stringify({
  url,
  format: 'A4',
  printBackground: true
})
// and POST to /v1/pdf instead of /v1/screenshot

PDF generation via Chromium in Lambda is even harder than screenshots — Lambda's sandboxed environment often breaks Page.printToPDF. With an API call it's identical to the screenshot pattern.

Trigger on a schedule (EventBridge)

{
  "source": "aws.events",
  "detail-type": "Scheduled Event",
  "resources": ["arn:aws:events:us-east-1:123:rule/screenshot-cron"],
  "detail": {
    "url": "https://yoursite.com/dashboard",
    "key": "monitoring/daily.png"
  }
}

Pair with an EventBridge cron rule to capture daily screenshots of any URL — competitor monitoring, status page archives, visual regression baselines.

Environment variable setup

PAGEBOLT_API_KEY=your_key_here
S3_BUCKET=your-bucket-name

Set both as Lambda environment variables in the console or via Terraform/CDK. No layers, no extensions, no binary compatibility issues.

What you skip

The @sparticuz/chromium approach requires:

  • A Lambda layer (~40MB compressed)
  • Setting executablePath to the layer binary
  • Configuring --single-process, --no-zygote, --disable-dev-shm-usage
  • Bumping Lambda memory to 1–2GB for Chromium to launch
  • Accepting 5–10s cold starts when the container initializes

With an API call, your Lambda stays at 128MB, deploys in seconds, and cold starts in milliseconds. The browser runs on PageBolt's infrastructure.

Get Started Free

100 requests/month, no credit card

Screenshots and PDFs from Lambda with a single fetch call — no Chromium layers, no cold start penalty.

Get Your Free API Key →