Taking Screenshots in Serverless Environments

Bypass the limitations of running Headless Chrome on AWS Lambda, Vercel, and Netlify.

📋 Table of Contents

The Problem with Headless Browsers in Serverless

Running Puppeteer or Playwright in a traditional Node.js server is straightforward. However, when migrating to a serverless architecture (like AWS Lambda, Vercel, or Netlify Functions), developers quickly run into a wall. Headless browsers are massive.

The Chromium binary alone is often over 130MB, which exceeds the deployment limits of many serverless platforms. Furthermore, spinning up a browser instance requires significant memory (RAM) and CPU resources, causing slow cold starts and frequent timeout errors.

AWS Lambda & Vercel Limitations

If you have ever tried to deploy a simple Vercel API route that takes a screenshot using puppeteer-core and @sparticuz/chromium, you likely encountered the following constraints:

The Solution: Screenshot APIs

Instead of wrestling with custom Chromium binaries, layer limitations, and font loading issues, the modern approach is to offload the heavy lifting to a specialized Screenshot API.

A Screenshot API runs fleets of headless browsers on optimized infrastructure. Your serverless function simply makes a lightweight HTTP request, reducing execution time from 10+ seconds to milliseconds.

Implementation Example

Here is how you can use the SnapAPI in your Vercel Next.js API Route or AWS Lambda function to generate a screenshot instantly:

// Vercel Next.js API Route Example
export default async function handler(req, res) {
  const targetUrl = req.query.url;
  
  if (!targetUrl) {
    return res.status(400).json({ error: 'URL is required' });
  }

  // Use SnapAPI instead of launching a local browser
  const snapApiUrl = new URL('https://snapapi.alterra-solutions.com/v1/screenshot');
  snapApiUrl.searchParams.append('url', targetUrl);
  snapApiUrl.searchParams.append('api_key', process.env.SNAPAPI_KEY);
  
  try {
    const response = await fetch(snapApiUrl.toString());
    const imageBuffer = await response.arrayBuffer();
    
    // Return image directly
    res.setHeader('Content-Type', 'image/png');
    res.setHeader('Cache-Control', 'public, max-age=86400');
    res.status(200).send(Buffer.from(imageBuffer));
    
  } catch (error) {
    res.status(500).json({ error: 'Failed to capture screenshot' });
  }
}

As you can see, this approach is extremely reliable, requires no heavy dependencies (just native fetch), and avoids all the pitfalls of serverless headless browsers.

Ready to simplify your Serverless Architecture?

Get 100 free screenshots per month. Perfect for open source projects and prototypes.

Get Started Free →