Free tools Windows power users keep installed
One-click scans. No signup required.
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Use a Lambda container image that packages a tested Chromium binary alongside Pyppeteer, then call one top-level async function from a normal synchronous Lambda handler with asyncio.run(). Do not count on the first invocation to download Chromium, assume any installed Chrome version will work, or treat this as an AWS-supported Pyppeteer recipe: AWS documents Lambda container mechanics and a Puppeteer/Chrome example, while Pyppeteer documents its own browser and event-loop behavior. The Pyppeteer project also says it is unmaintained, so consider whether a maintained alternative is a better fit before committing to it.
What to build—and what AWS does not promise
For a Python Lambda that must run Pyppeteer, the most controllable path is to build a container image containing your application, its Python dependencies, and a Chromium executable whose compatibility you have tested. Set Pyppeteer’s executablePath to that binary. A synchronous Lambda entry point can call an async worker using asyncio.run(), which lets the worker await navigation and browser operations.
This is an implementation pattern, not an AWS guarantee for Pyppeteer. AWS documents Python Lambda container images and has published a browser-automation architecture using Puppeteer and Chrome. That precedent supports the packaging approach, but does not establish that AWS supports or has validated Pyppeteer specifically. Pyppeteer’s own guidance says it works best with its bundled Chromium and that compatibility with other Chrome versions is not guaranteed.
These distinctions matter operationally: the image has to include a browser that runs on the target Lambda architecture and has the system libraries it needs; successful Python installation alone is not proof that Chromium can launch.
#1 Best Overall
Choose a packaging and browser strategy
Bundle Chromium in a Lambda container image
A container gives you a place to package the Python application, Pyppeteer, a chosen browser binary, and its runtime dependencies together. AWS Python base images include the Lambda runtime interface client. If you choose an OS-only or other non-AWS base image, add the Python runtime interface client as AWS requires for that packaging approach.
AWS’s current Python image documentation distinguishes AL2023-based images for Python 3.12 and later from AL2-based images for Python 3.11 and earlier in the image table described there. Runtime availability and deprecation schedules change, so check AWS’s live Lambda runtime documentation when choosing a base image; do not treat that version split as a promise of future support.
Do not make first-use download part of production startup
Pyppeteer documents that, if Chromium is not already installed, its first run downloads a browser; its pyppeteer-install utility can fetch the browser ahead of use. For Lambda, arrange and test the intended browser during the build or deployment process instead. This recommendation follows from Pyppeteer’s download behavior and Lambda’s packaged deployment model; it is not a Pyppeteer-specific recipe published by AWS.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Bundling a browser is not the same as proving it will launch. Use a binary built or obtained for the target architecture, include its required shared libraries, and test inside the image. Pin the Pyppeteer package and browser artifact together in your own build inputs so a rebuild does not silently substitute a different combination.
Consider a remote browser only when its trade-offs fit
A hosted browser moves browser installation and some lifecycle management outside Lambda, but adds a network dependency and makes latency, data handling, isolation, observability, scaling, and provider cost part of the design. Browserless documents how to connect Pyppeteer to a remote browser; that establishes an integration path, not a performance or cost advantage. Compare current provider terms against the operational work you would otherwise own.
Build the async handler
The following handler accepts a URL in the Lambda event and returns a full-page PNG as base64, suitable for an API Gateway-style JSON response. It expects a Chromium executable at /opt/chromium/chrome. Your image must contain that binary and its runtime libraries; set the environment variable CHROMIUM_PATH if it is elsewhere. The code is intentionally explicit about cleanup so a failed navigation or screenshot does not skip browser closure.
import asyncio
import base64
import os
from urllib.parse import urlparse
from pyppeteer import launch
CHROMIUM_PATH = os.environ.get("CHROMIUM_PATH", "/opt/chromium/chrome")
NAVIGATION_TIMEOUT_MS = int(os.environ.get("NAVIGATION_TIMEOUT_MS", "30000"))
async def capture(url: str) -> bytes:
browser = None
page = None
try:
browser = await launch(
headless=True,
executablePath=CHROMIUM_PATH,
args=["--no-sandbox", "--disable-setuid-sandbox"],
)
page = await browser.newPage()
await page.setViewport({"width": 1365, "height": 900})
await page.goto(
url,
{"waitUntil": "networkidle2", "timeout": NAVIGATION_TIMEOUT_MS},
)
return await page.screenshot({"type": "png", "fullPage": True})
finally:
if page is not None:
await page.close()
if browser is not None:
await browser.close()
def handler(event, context):
url = (event.get("queryStringParameters") or {}).get("url")
if not url:
return {"statusCode": 400, "body": "Missing url query parameter"}
parsed = urlparse(url)
if parsed.scheme not in ("http", "https") or not parsed.netloc:
return {"statusCode": 400, "body": "url must be an absolute HTTP or HTTPS URL"}
image = asyncio.run(capture(url))
return {
"statusCode": 200,
"headers": {"Content-Type": "image/png"},
"isBase64Encoded": True,
"body": base64.b64encode(image).decode("ascii"),
}
Pyppeteer’s API uses asynchronous browser operations; each is awaited inside capture(). asyncio.run() creates and runs the event loop for that invocation. Do not call it from a context where an event loop is already running. If you are adapting this code for a framework that owns an event loop, use that framework’s async integration rather than nesting asyncio.run().
The sample validates URL syntax, not whether a destination is safe to fetch. If callers can supply arbitrary URLs, enforce an allowlist or other network egress controls appropriate to your service. A browser can reach more than the public page a user intended, so input validation should not be mistaken for an SSRF defense.
Build and test the Lambda container
Use an AWS Python Lambda base image compatible with your chosen runtime. AWS’s Python base images include the runtime interface client. The outline below assumes you have prepared a chromium/ directory containing the executable and all runtime libraries for your target architecture; it deliberately does not imply that an arbitrary system Chrome download is compatible.
FROM public.ecr.aws/lambda/python:3.12
WORKDIR ${LAMBDA_TASK_ROOT}
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py ${LAMBDA_TASK_ROOT}/app.py
COPY chromium/ /opt/chromium/
RUN chmod +x /opt/chromium/chrome
ENV CHROMIUM_PATH=/opt/chromium/chrome
CMD ["app.handler"]
Put the exact Pyppeteer release you have validated in the build’s dependency lock rather than allowing a production rebuild to float to a new release. The browser directory must also be built or selected for the same architecture as the Lambda image. Follow AWS’s container-image build guidance to select the target architecture; the Docker platform option and the Lambda function architecture need to agree.
Rank #3
- Build the image: use the AWS Python Lambda base image for the runtime you intend to deploy, and specify the target platform for the function architecture.
- Check the browser inside the image: invoke Chromium’s version command and run a smoke test that launches it through Pyppeteer. A version command alone does not prove the browser can render a page.
- Test with Lambda’s local workflow: use AWS’s documented container testing workflow, such as the runtime interface emulator or SAM/Docker flow, to exercise the handler in the image.
- Deploy and run an integration test: verify the function in its actual AWS runtime and architecture, using representative pages and the real configured timeout and memory.
A local image check catches packaging mistakes, but does not replace a deployed integration test. Network access, function configuration, and the actual runtime environment still matter.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Set lifecycle, timeout, memory, and concurrency deliberately
AWS describes Lambda execution environments in initialization, invocation, and shutdown phases. Treat browser cleanup as part of the invocation’s correctness: the sample closes both the page and browser in a finally block. If cleanup itself raises an error in your application, decide how to log and handle it without obscuring the original capture failure.
You can investigate keeping a browser process across invocations to avoid relaunch work, but treat reuse as a deployment choice to load-test, not a guaranteed optimization. The available guidance does not validate a Pyppeteer-specific reuse recipe. Reuse can make state management and concurrent requests more complex; test isolation, page cleanup, failures, and resource behavior before relying on it.
- Timeout: set the Lambda timeout to cover browser startup, navigation, rendering, screenshot generation, and cleanup for your workload. The sample’s navigation timeout only limits
page.goto(); it does not set the Lambda’s overall timeout. - Memory: measure the pages you actually capture, including large or script-heavy pages. Do not copy a universal memory value from an unrelated example.
- Concurrency: test realistic parallel invocations and destination-site behavior. A browser workload can consume substantially different resources from a small Python-only function.
- Waiting strategy:
networkidle2waits for network activity to settle, which may be unsuitable for pages that keep connections open or load background resources continuously. Use an explicit selector or a bounded delay when that better matches the page’s readiness condition.
No universal timeout, memory allocation, cold-start time, image size, or cost figure is established for this workload. Derive configuration and cost from measurements of your pages, selected architecture, invocation volume, and AWS pricing for the region and date you deploy.
Or skip the browser setup
If the job is to return a website screenshot or PDF rather than to control a custom Pyppeteer browser session, ScreenshotNeo provides a screenshot API and MCP server. One GET request returns an image or PDF; its documentation is at ScreenshotNeo API docs.
For example, this cURL request saves a WebP screenshot:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie/consent banners, newsletter popups, and chat widgets are removed before capture; each cleanup step can be turned off.
- Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Responses include
X-Page-VerdictandX-Billedheaders indicating the outcome. - An MCP server exposes
take_screenshot,get_page_info, andcapture_pdffor Claude, Cursor, and other MCP clients. - The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. The available capture options include full-page screenshots, selector capture, custom viewport/device settings, PDF output, custom CSS and JavaScript, and asynchronous jobs.
ScreenshotNeo is a different execution model from packaging Pyppeteer: use it when a screenshot or PDF result is enough, not when your application needs arbitrary in-browser automation. Sign up for 1,000 free screenshots a month with no card.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Troubleshooting common failures
Chromium is missing or Pyppeteer tries to download it
Cause: the browser was not included in the image at the configured path, or Pyppeteer cannot find its expected default download. Fix: package the intended binary during the build, set CHROMIUM_PATH and executablePath consistently, and confirm the file exists inside the deployed image. Do not make a cold-start download the recovery plan.
Chromium exits immediately or reports missing libraries
Cause: the binary or one of its shared-library dependencies does not match the Lambda image or target architecture. Fix: inspect the image’s architecture and browser dependencies, package the needed libraries, and run a launch smoke test inside the exact image. Do not assume a Chrome binary from another operating system image will work unchanged.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchNavigation times out on a page that appears loaded
Cause: a page may keep network requests active, so a network-idle condition is never reached. Fix: choose a readiness condition that reflects the page you need, such as waiting for a known selector, and retain a finite navigation timeout. Diagnose failed destinations separately from a wait condition that is too strict.
The handler fails with an event-loop error
Cause: asyncio.run() is being invoked while another event loop is already running. Fix: retain the synchronous Lambda entry-point pattern shown above, or have the framework that owns the event loop await the coroutine directly; do not nest a second loop.
Captures work locally but not after deployment
Cause: local and deployed images, architectures, environment variables, network paths, or Lambda settings differ. Fix: compare the actual deployed image and function architecture with the tested build, then run a deployed integration test and inspect logs for browser launch, navigation, and cleanup failures.
Pyppeteer maintenance and whether to keep it
The Pyppeteer repository describes its own status this way: “This repo is unmaintained and has been outside of minor changes for a long time. Please consider playwright-python as an alternative.” That is the project’s maintenance notice, not an independent audit. If Pyppeteer is mandatory, assign ownership for package and browser updates and keep a validated pair available for rebuilds. If you are starting fresh, assess Playwright Python against the browser behavior and API your application needs before building new infrastructure around an unmaintained dependency.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesMigration is not automatically free: compare the behavior your code depends on, the amount of API adaptation, browser compatibility needs, and the ability of your team to maintain the resulting deployment. AWS’s Puppeteer-and-Chrome Lambda example is useful architectural precedent for containerized browser automation, but it should not be read as evidence of Pyppeteer-specific AWS support.
Frequently Asked Questions
Can I use this handler as an unrestricted public URL screenshot endpoint?
Not safely without additional controls. Validate allowed destinations and apply network egress restrictions appropriate to your application; the sample’s URL syntax check alone does not prevent server-side request forgery.
Does a successful local container test prove the Lambda deployment is production-ready?
No. It confirms behavior in the local test setup, but the deployed function still needs an integration test in its actual runtime, architecture, network configuration, and resource limits.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

