ScreenshotNeo

BlogHow-to

How to Capture Playwright Screenshots in an AWS Lambda Function

Bundle a compatible Chromium build, launch it from Lambda, and capture a page, element, or full document with Playwright. See deployment patterns, output handling, and troubleshooting.

By the ScreenshotNeo team4 October 20268 min read

Direct answer: In an AWS Lambda function, launch Chromium that is packaged for your Lambda runtime and architecture, navigate with Playwright, then call page.screenshot(). Use fullPage: true for the full scrollable document or a locator screenshot for one element. A screenshot path saves a file; omitting the path returns image bytes. Lambda does not make those bytes persistent: upload them to S3 or another destination if they must survive the invocation.

The key deployment decision is the browser binary. Playwright’s screenshot API is documented independently of Lambda, so choose and pin a compatible Playwright package, Chromium build, runtime, and architecture as one deployment unit. The package examples below illustrate the launch pattern, but their historical compatibility claims need checking against your chosen versions and current AWS Lambda options before deployment.

1. Choose and package Chromium

Lambda needs a Chromium executable that runs in its environment. Two packages in the available documentation describe Lambda-oriented approaches:

Approach What its documentation says What to verify
playwright-aws-lambda with playwright-core The npm documentation shows launchChromium(), then creating a context and page. It lists Node.js 10.x through 20.x as working out of the box and says it supports Chromium only. Those runtime claims may be stale. Check the package’s maintenance, its supported runtime and architecture, and compatibility with your pinned Playwright version.
chrome-aws-lambda with playwright-core The repository documents pairing its binary and launch arguments with playwright-core. It recommends at least 512 MB of RAM and 1600 MB or more. Treat these as that repository’s recommendations, not AWS minimums or a sizing benchmark. Verify current package compatibility and test your workload.

The research does not establish a current winner or a maintained compatibility matrix. Compare package activity, runtime and architecture support, how the executable path is supplied, required launch arguments, deployment artifact size, and memory use. Pin package versions and record the matching browser build so a dependency update cannot silently change rendering.

References: playwright-aws-lambda on npm, chrome-aws-lambda repository, and Playwright screenshot documentation.

2. Capture a screenshot in the Lambda handler

This example follows the playwright-aws-lambda API documented by that package. It accepts a URL from the event, opens Chromium, waits for navigation, captures a viewport screenshot as bytes, and closes the browser even if navigation or capture fails. Confirm that the package and runtime versions you deploy still match its documented API.

const playwright = require('playwright-aws-lambda');

exports.handler = async (event) => {
  const url = event?.url;
  if (typeof url !== 'string' || url.length === 0) {
    return { statusCode: 400, body: 'Provide a url string in the event.' };
  }

  let browser;
  try {
    browser = await playwright.launchChromium();
    const context = await browser.newContext({ viewport: { width: 1280, height: 800 } });
    const page = await context.newPage();

    await page.goto(url, { waitUntil: 'load', timeout: 30000 });
    const image = await page.screenshot({ type: 'png' });

    // Return bytes only if your invocation path can handle a binary response.
    return {
      statusCode: 200,
      isBase64Encoded: true,
      headers: { 'content-type': 'image/png' },
      body: image.toString('base64')
    };
  } catch (error) {
    console.error('Screenshot capture failed', error);
    return { statusCode: 502, body: 'Could not capture the requested page.' };
  } finally {
    if (browser) await browser.close();
  }
};

The exact event response integration depends on how the function is invoked. For durable output, upload the screenshot bytes to object storage using the storage SDK configured in your function, then return a key or URL according to your application’s access policy. Capturing bytes does not itself upload to S3. AWS’s serverless image processing architecture illustrates Lambda and S3 in a processing pipeline, but is not a Playwright implementation recipe.

Readiness and navigation

waitUntil: 'load' waits for the page load event, but many applications continue rendering afterward. Prefer an application-specific readiness condition when one is known:

await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.locator('[data-page-ready="true"]').waitFor({ state: 'visible', timeout: 10000 });
const image = await page.screenshot({ type: 'png' });

Other Playwright navigation wait states include load, domcontentloaded, and networkidle. Choose based on the page: network activity can continue indefinitely on sites with polling or analytics, and a fixed delay is not a universal readiness signal.

3. Pick the screenshot target and output

Viewport screenshot

The default screenshot covers the current viewport. Set its size when creating the context to make captures more consistent:

const context = await browser.newContext({ viewport: { width: 1440, height: 900 } });
const page = await context.newPage();
await page.goto(url, { waitUntil: 'load' });
const bytes = await page.screenshot({ type: 'png' });

Full scrollable page

Use fullPage: true to capture the full page rather than only the viewport:

const bytes = await page.screenshot({ type: 'png', fullPage: true });

Very long or image-heavy pages can require more memory and time. Lazy-loaded content may not be present until it scrolls into view; if completeness matters, make the page load the relevant content before capturing and validate the result.

One element

Use a locator screenshot when the desired artifact is a component rather than the whole page:

const card = page.locator('.product-card').first();
await card.waitFor({ state: 'visible' });
const bytes = await card.screenshot({ type: 'png' });

The locator must resolve to a visible element. A missing selector, hidden element, or element that changes while being captured can cause a failure; wait for the right state and select the intended match.

File path or in-memory bytes

page.screenshot({ path: '/tmp/screenshot.png' }) writes to a path and returns the image data as well. In Lambda, use the writable temporary area for transient files; the file is not durable after the environment is discarded. Omitting path gives you bytes to return, transform, or upload directly.

Playwright supports screenshot options including type (png, jpeg), quality for JPEG, fullPage, clip for a viewport region, omitBackground, animations, caret, scale, and mask. Quality applies to JPEG. Check the API documentation for option details and availability for the Playwright version you pin.

4. Return or persist the artifact

Choose an output path based on the invocation:

  • Synchronous HTTP response: Return base64-encoded bytes with the correct content type if your gateway or caller supports the response size.
  • Object storage: Upload the returned buffer and return an object key or an access-controlled link. Set the content type to match the screenshot format.
  • Asynchronous processing: Store the result and send a job identifier or completion event to the caller.

Large full-page images can exceed response limits in surrounding infrastructure even when the Lambda handler itself can produce them. For larger outputs, object storage avoids carrying the whole image in an API response. Apply your normal access controls and retention policy to captured pages, which may contain private or personal information.

5. Deployment and reliability checklist

  1. Choose the Lambda runtime and CPU architecture, then select a browser package and Playwright version compatible with both.
  2. Pin versions and include the expected Chromium binary and launch configuration in the deployment artifact.
  3. Run a capture against representative pages in the deployed environment; local success does not prove the binary works in Lambda.
  4. Set a navigation timeout and a function timeout that leaves room for screenshot processing and output upload.
  5. Use an application readiness condition where possible instead of relying on a fixed sleep.
  6. Close the browser in a finally block so errors do not skip cleanup.
  7. Decide whether output is returned, written temporarily, or persisted to object storage.
  8. Validate image dimensions, format, and content in the receiving system.

Rendering can differ with operating system, browser version, settings, hardware, power source, and headless mode. For visual comparisons, capture both the baseline and the new image in the same environment when possible. See Playwright’s visual comparisons guidance.

6. Security when the URL comes from a caller

A caller-supplied URL is untrusted input. The research sources do not establish a safe URL policy for this pattern. Before exposing a capture endpoint, apply your own URL validation and network egress controls so the function cannot be used to request unintended internal or protected destinations. Avoid returning detailed browser errors to untrusted callers; log diagnostic context securely and return a generic failure response.

7. Troubleshooting

Symptom Likely cause Fix
Chromium fails to launch Binary, runtime, architecture, or launch arguments do not match. Verify the deployed executable and package compatibility for the chosen Lambda environment. Recheck the exact versions and arguments.
Works locally but fails in Lambda The local browser environment differs from the deployed one, or the deployment package lacks the expected binary. Test the actual deployed artifact and architecture. Keep browser and Playwright versions pinned together.
Navigation timeout The page is slow, unreachable, or waits on continuing network activity. Check network access and the target URL. Set a suitable timeout and wait for a specific readiness signal rather than requiring network idle for every site.
Screenshot is blank or incomplete The app had not rendered its content, or deferred content had not loaded. Wait for a visible application marker; trigger or wait for lazy content if required; inspect the page before capture.
Element screenshot reports no match or visibility problem The selector is wrong, matches no element, or the element is hidden. Correct the selector and wait for the intended element to become visible.
Function runs out of memory or times out The page or full-page image is resource intensive, or the function has insufficient resources for the workload. Measure representative pages, reduce unnecessary work, consider viewport capture, and tune function resources based on observed workload. The 512 MB and 1600 MB figures are recommendations from the chrome-aws-lambda repository, not universal sizing rules.
Screenshot disappears after the invocation The image was written only to temporary storage. Upload the bytes to durable storage before returning.
Image response is corrupted Binary bytes were treated as text or base64 response metadata is missing. Preserve the byte buffer, use base64 encoding only at the HTTP boundary, and set the matching content type.
Local and Lambda screenshots differ Browser, OS, headless mode, fonts, settings, or hardware differ. Use the same browser and environment for baseline and generated captures, and avoid comparing across uncontrolled rendering environments.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request captures a URL as PNG, JPEG, WebP, or PDF. Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed, and response headers tell you the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf.

For a quick Lambda alternative, call the API and handle the returned image bytes as the response body or upload them to your storage destination:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for request options. It includes full-page and element capture, device and viewport settings, PDF output, custom CSS and JavaScript, request controls, caching, signed links, async jobs, bulk capture, and a usage API. One thousand screenshots a month are free with no card; paid plans start at $5 for 3,000, and every feature is on every plan. Learn about ScreenshotNeo or sign up free for 1,000 screenshots a month, with no card.

FAQ

Does Playwright itself provide a Lambda Chromium binary?

The screenshot API documentation does not define Lambda packaging. Your deployment must provide a browser executable compatible with the selected runtime and architecture.

Can I use this for visual regression?

Yes, but compare captures made with the same browser and environment where possible, because rendering can vary across environments.

Does returning a screenshot buffer save it permanently?

No. A buffer is in-memory output. Persist it explicitly to storage if it must remain available after the invocation.