ScreenshotNeo

BlogComparisons

Screenshot API vs AWS Lambda with Puppeteer for Page Captures

Compare a managed screenshot API with Puppeteer on AWS Lambda: capture controls, security, operations, cost, and how to choose for your workload.

By the ScreenshotNeo team4 October 20268 min read

A screenshot API is usually the simpler choice when you want to submit a URL and receive an image without packaging or operating a browser. AWS Lambda with Puppeteer is a better fit when you need to own the rendering pipeline, integrate it closely with AWS, or control browser behavior and can maintain Chromium, deployments, monitoring, and cost modeling.

There is no universal cost or performance winner. Compare the options using your capture settings, monthly volume, render duration, concurrency, latency target, storage needs, and engineering time. The comparison below is an architectural guide, not a benchmark.

1. What each option means

Managed screenshot API

Your application sends a capture request to a hosted service. The provider operates the browser and returns an image or another supported output. This reduces browser infrastructure work, but makes you dependent on the provider’s supported features, limits, security and data handling terms, reliability, and pricing. Check these directly before choosing a provider.

For a first hosted option, consider ScreenshotNeo: it offers clean captures, bills only clean shots, and has a $5 paid plan for 3,000 screenshots. Its free plan includes 1,000 screenshots per month without a card.

Lambda with Puppeteer

Your function starts a browser, navigates to the requested page, captures it, then returns or stores the result. Puppeteer supports page screenshots with Page.screenshot() and element screenshots with ElementHandle.screenshot(). You operate the browser deployment and the surrounding service.

2. Choose by requirements

Question Managed screenshot API Lambda with Puppeteer
Who operates Chromium? The provider Your team
How much browser control do you need? Limited to the provider’s documented options Control your capture code and browser workflow
How quickly can you add capture? Usually a request and response integration Build, package, deploy, secure, and operate a capture service
What should you model for cost? Provider pricing, usage rules, and any add-ons Requests, duration, configured memory, gateway, storage, transfer, logs, and concurrency choices
What is the main operational dependency? Provider features, terms, limits, and service behavior Browser compatibility, runtime packaging, AWS configuration, and your monitoring

Prefer a managed API if avoiding browser operations matters more than owning the renderer. Prefer Lambda if the control and integration justify maintaining the browser stack. If you cannot decide from requirements alone, prototype both with representative target pages and measure the same workload.

3. Build a minimal Lambda capture with Puppeteer

The following example uses puppeteer-core and @sparticuz/chromium, a deployment pattern described in vendor implementation guidance. Verify current package compatibility, runtime support, and AWS deployment limits before adopting it. Keep the Chromium package and Puppeteer versions compatible.

import chromium from '@sparticuz/chromium';
import puppeteer from 'puppeteer-core';

export const handler = async (event) => {
  const params = event.queryStringParameters ?? {};
  const target = params.url;

  if (!target) {
    return {
      statusCode: 400,
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ error: 'Missing url query parameter' }),
    };
  }

  let parsed;
  try {
    parsed = new URL(target);
  } catch {
    return {
      statusCode: 400,
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ error: 'url must be an absolute URL' }),
    };
  }
  if (!['http:', 'https:'].includes(parsed.protocol)) {
    return {
      statusCode: 400,
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ error: 'Only http and https URLs are supported' }),
    };
  }

  let browser;
  try {
    browser = await puppeteer.launch({
      args: chromium.args,
      defaultViewport: { width: 1440, height: 900, deviceScaleFactor: 1 },
      executablePath: await chromium.executablePath(),
      headless: true,
    });
    const page = await browser.newPage();
    page.setDefaultNavigationTimeout(25000);
    await page.goto(target, { waitUntil: 'networkidle2' });
    const png = await page.screenshot({
      type: 'png',
      fullPage: true,
      captureBeyondViewport: true,
    });
    return {
      statusCode: 200,
      headers: {
        'content-type': 'image/png',
        'cache-control': 'no-store',
      },
      isBase64Encoded: true,
      body: Buffer.from(png).toString('base64'),
    };
  } catch (error) {
    console.error('Capture failed', error);
    return {
      statusCode: 502,
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ error: 'Could not capture page' }),
    };
  } finally {
    if (browser) await browser.close();
  }
};

This is a minimal illustration, not a production-ready public endpoint. In production, add authentication, request limits, structured logs, and URL protections. A user-controlled URL can make your function fetch internal or otherwise unintended network locations. Use an allowlist where possible, reject private and link-local destinations, and validate redirects and resolved addresses as part of your security design.

Capture options to decide explicitly

Puppeteer’s documented screenshot options include full-page capture, clipping, image type, quality for supported formats, and omission of the default background for transparency. PNG is the default. Check the current ScreenshotOptions reference for exact version behavior.

  • Viewport or full page: Set fullPage when the entire document is required. Long pages can produce large images and use more memory.
  • Specific region: Use a clip rectangle for known coordinates, or ElementHandle.screenshot() for a selected element.
  • Format: PNG is suitable when lossless output matters. JPEG or WebP may reduce size where supported; quality applies only to formats that support it.
  • Transparency: Use the documented background omission option when transparent output is required; verify the output format and target page behavior.
  • Viewport and device scale: Set the viewport and scale deliberately so output dimensions and responsive layout are predictable.
  • Readiness: Choose a navigation condition that suits the site. Network-idle waits can stall on pages with ongoing requests; a selector or application-specific readiness check may be more reliable.

4. Expose the capture safely

A Lambda handler needs an invocation path. AWS describes Function URLs as a direct HTTP invocation option and API Gateway as the option with more advanced authentication, validation and transformation, monitoring, and traffic management. Select the path based on what your endpoint needs, and consult the current AWS Function URL and API Gateway guidance.

  1. Require authentication unless the endpoint is intentionally public.
  2. Validate the URL scheme and destination. Consider an allowlist for known capture domains.
  3. Set maximum navigation and overall execution times with headroom for browser startup.
  4. Limit request rates and concurrent work to protect the function and downstream sites.
  5. Choose how results are delivered: return bytes for small synchronous captures, or store results and return a reference for larger or asynchronous workloads.
  6. Record capture status, duration, target host, and failure category without logging credentials or sensitive page content.

Function URLs scale with Lambda concurrency and return HTTP 429 when the concurrency limit is reached. AWS documents default concurrency limits that depend on account and Region context; check the current limits for your deployment rather than relying on a fixed default.

5. Compare cost, latency, and reliability

Cost model

AWS says standard Lambda charges are based on request count and execution duration, with compute cost depending on configured memory. Your actual capture service may also incur API Gateway, storage, data transfer, logging, and provisioned concurrency costs. Use the current AWS prices for the deployment Region and measured execution behavior.

For either approach, estimate with these inputs:

  • Monthly successful captures and peak request rate
  • Average and high-percentile browser startup and navigation duration
  • Configured memory and concurrency requirements
  • Cache hit rate and cache retention
  • Typical image dimensions, format, size, and retention period
  • Data transfer, gateway, storage, and log volume
  • Time spent updating browser dependencies and diagnosing failures

A vendor-authored comparison may illustrate assumptions, but its crossover point is not a general rule. Do not infer a universal break-even volume from another workload. Compare current provider prices with your own measurements and include engineering operations in the decision.

Latency and reliability

Lambda introduces browser startup and page navigation into each invocation; cold starts and target-site behavior can affect response time. A managed API removes your browser deployment work but leaves you dependent on that provider’s behavior and limits. The research available for this article does not establish an independent latency or reliability winner. Test representative pages, including heavy client-side applications, slow pages, and pages that never become network-idle.

For reliability, define timeouts, bounded retries, and idempotent job handling. Retry transient infrastructure failures selectively; repeated navigation to a slow or failing destination can increase latency and cost. Track outcomes by failure class so browser launch errors, navigation timeouts, blocked requests, and output delivery problems can be distinguished.

6. Common problems and fixes

Symptom Likely cause What to do
Chromium fails to launch Browser binary is missing, incompatible, or not executable in the deployed runtime Use a browser package built for the target environment; verify puppeteer-core and Chromium compatibility and inspect the deployed artifact.
Function times out during navigation The target is slow, has persistent network activity, or the wait condition is too strict Set a navigation timeout, use an appropriate readiness condition, and separate navigation time from screenshot time in logs.
Screenshot is blank or incomplete Capture started before client rendering, fonts, or images were ready Wait for a page-specific selector or readiness signal; test lazy-loaded content and full-page behavior on the actual target.
Response is truncated or rejected Image payload exceeds the synchronous response path’s practical size limits Reduce dimensions or use object storage and return a reference. Check the selected gateway’s current payload limits.
HTTP 429 responses Lambda concurrency limit or configured throttling is reached Control bursts with a queue or rate limit, review concurrency settings and current account limits, and return a retryable response where appropriate.
Different output after deployment Viewport, browser build, fonts, locale, or page content differs between environments or runs Pin compatible dependencies, set viewport and locale deliberately, and avoid assuming third-party page content is static.
Unexpected network access Caller supplied an unrestricted URL or a redirect reached a disallowed destination Apply destination validation, restrict network egress where feasible, and re-check redirect destinations.

7. When ScreenshotNeo is the better fit

If your goal is to request page captures without packaging Chromium or maintaining a Lambda browser runtime, ScreenshotNeo is the hosted alternative to try first. The service accepts a URL and can return PNG, JPEG, WebP, or PDF. It can accept cookie and consent banners before capture and remove 60+ known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, with the result identified in response headers.

It also provides an MCP server for Claude, Cursor, and other MCP clients, with take_screenshot, get_page_info, and capture_pdf tools. The free plan includes 1,000 screenshots monthly without a card; paid plans start at $5 for 3,000. Every feature is on every plan. See the ScreenshotNeo documentation for request options and setup.

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

Use the API key from your account. The request returns the capture file; use the response headers to identify the page verdict and billing status.

8. FAQ

Can Puppeteer capture a single element instead of the whole page?

Yes. Puppeteer documents ElementHandle.screenshot() for capturing a particular element. Make sure the element is present and visible before calling it.

Should I use network idle for every page?

No. Pages with polling, analytics, or streaming requests may never become idle. Use a selector or application-specific readiness condition when it better represents completed rendering.

Is Lambda automatically cheaper at high volume?

No general crossover is established here. Compare current prices and your measured durations, memory, concurrency, storage, transfer, cache behavior, and maintenance effort.

What should I test before choosing?

Test pages representative of your traffic: responsive layouts, lazy images, large documents, slow pages, redirects, and pages with persistent network requests. Measure output correctness, latency distribution, failure rate, and full monthly cost assumptions.