ScreenshotNeo

BlogHow-to

Puppeteer Screenshot with a Custom User Agent

Set a custom user agent before navigation, choose the right page readiness condition, and capture a viewport, full page, clipped region, or element with Puppeteer.

By the ScreenshotNeo team4 October 20267 min read

Set the user agent on the Puppeteer Page before navigating, then capture the page after it reaches an appropriate readiness condition. A user-agent override changes the configured user-agent value; it does not by itself set a mobile viewport or reproduce every property of a particular device.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.setUserAgent('ExampleBrowser/1.0');
  await page.goto('https://example.com', { waitUntil: 'networkidle2' });
  await page.screenshot({ path: 'screenshot.png', fullPage: true });
} finally {
  await browser.close();
}

Install Puppeteer with npm install puppeteer, save this as an ES module such as screenshot.mjs, and run node screenshot.mjs. The browser is closed in a finally block so it is also cleaned up if navigation or capture fails. For exact method signatures and supported options, see the Puppeteer Page API and the official screenshot guide.

Set the user agent before navigation

page.setUserAgent(userAgent, userAgentMetadata) configures the page’s user agent. Call it before page.goto() so the initial navigation uses the intended setting. Changing it after the page has loaded does not change the request that already happened; navigate again if you need to load the page under the new value.

await page.setUserAgent('ExampleBrowser/1.0');
await page.goto('https://example.com');

Choose a user-agent string appropriate to your legitimate testing or rendering scenario. A custom string does not guarantee how a website classifies the browser, and it does not make the browser anonymous or ensure that other browser-identifying properties match the string.

User-agent override versus device emulation

A user-agent override changes the configured user-agent value. Device emulation is the better fit when the goal is a mobile-style screenshot: Puppeteer’s page.emulate(device) applies device settings including the user agent and viewport. Apply device emulation before navigation because sites may choose their layout or resources during initial page load. See Puppeteer’s Page API for the current API.

Goal Use What it changes
Send a different user-agent value page.setUserAgent(...) The configured user agent; viewport dimensions remain as configured.
Render for a supported device preset page.emulate(device) Device emulation settings, including user agent and viewport.
Use custom dimensions with a custom user agent Set viewport and user agent explicitly before navigation The viewport and configured user-agent value you choose.

Do not infer that setting a mobile-looking user-agent string alone makes the page mobile-sized. If layout dimensions matter, configure the viewport or use a device preset as well.

Choose what to capture

Puppeteer supports viewport screenshots, full-page screenshots, clipped regions, and element screenshots. The screenshot guide and ScreenshotOptions reference document the relevant capture methods and options.

Viewport screenshot

await page.screenshot({ path: 'viewport.png' });

This captures the currently visible viewport. Set its dimensions before navigation when the target page’s initial layout should be based on a particular viewport.

Full-page screenshot

await page.screenshot({ path: 'full-page.png', fullPage: true });

Use fullPage: true to capture beyond the visible viewport. Pages that load images or other content as the reader scrolls may need extra handling to ensure that content appears before capture.

Clipped region

await page.screenshot({
  path: 'region.png',
  clip: { x: 0, y: 0, width: 800, height: 500 }
});

The clip rectangle uses page coordinates and dimensions in pixels. Ensure the desired region exists and has nonzero width and height.

Element screenshot

const card = await page.waitForSelector('.product-card');
if (!card) throw new Error('Product card was not found');
await card.screenshot({ path: 'product-card.png' });

Element capture is useful when the output should contain one component rather than the page. Waiting for the selector makes the capture fail clearly if the element never appears.

Format and background options

The screenshot options include path, fullPage, clip, type, quality, and omitBackground, among others. Use type to select a supported image format when needed. quality applies to JPEG and WebP output; it is not a PNG quality control. omitBackground: true can produce a transparent background where applicable. Consult the current options reference for supported values and constraints.

Wait for the right page state

The example uses waitUntil: 'networkidle2', a convenient choice for pages that settle after their initial requests. Readiness is site-dependent: analytics, polling, streaming, and other continuous requests can make a network-idle condition unsuitable. Select a condition that matches what the screenshot needs, and wait for a specific element or application state if that is the actual requirement.

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('main article');
await page.screenshot({ path: 'article.png', fullPage: true });

Navigation completion alone does not prove that every image, font, animation, or client-rendered component is ready. For a reliable capture, identify the visible content that matters and wait for a stable signal for that content.

Complete example with viewport configuration

This version sets both viewport and user agent before navigation, waits for the main content, and saves a full-page image. Replace the sample values with those appropriate to your capture.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1365, height: 900 });
  await page.setUserAgent('ExampleBrowser/1.0');

  await page.goto('https://example.com', {
    waitUntil: 'domcontentloaded',
    timeout: 45_000
  });
  await page.waitForSelector('main', { timeout: 15_000 });
  await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
  await browser.close();
}

The explicit navigation and selector timeouts bound how long this script waits before reporting a failure. Tune them to the target and environment rather than assuming one duration fits every site.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request can return an image or PDF, and its parameter names also work with those used by other screenshot APIs. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));

ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo’s free 1,000 screenshots a month, with no card required.

Troubleshooting

Symptom Likely cause Fix
The page appears to ignore the custom user agent The setting was applied after navigation, or the site uses other signals when choosing its response. Call setUserAgent before goto and reload. Do not assume the string controls every browser or device property.
The screenshot has a desktop layout despite a mobile user agent The user-agent override did not change the viewport. Set the viewport or use device emulation before navigating.
Navigation times out on a page that looks loaded A chosen network-idle condition may never occur because the page keeps making requests. Use a more appropriate readiness condition, then wait for the content or selector that must appear.
An element screenshot fails or is empty The selector did not match, the element was not ready, or it has no visible dimensions. Wait for the selector, check that it exists and is visible, and verify its rendered size before capture.
Full-page output is missing lazy-loaded content The page loads some content only after scrolling or interaction. Trigger the relevant page behavior and wait for the content before taking the screenshot.
Transparent output is unexpectedly opaque The page or capture options supply a background, or the output format does not meet the desired transparency needs. Review omitBackground and the selected output format in the ScreenshotOptions documentation.
The process remains running after capture The browser process was not closed on every exit path. Close it in a finally block, as in the runnable examples.

Performance, reliability, and cost

  • Wait only for what the capture needs. Waiting for a page-wide network-idle state can add time or hang on pages with ongoing traffic. A target-specific selector can provide a more useful readiness signal.
  • Close each browser. Browser processes consume resources. Ensure cleanup happens on both success and error paths; for repeated jobs, manage browser lifecycle deliberately.
  • Bound waits. Configure navigation and selector timeouts so a slow or unresponsive site does not hold a job forever. Record the failing URL and operation to make retries diagnosable.
  • Retry selectively. A transient navigation failure may merit a bounded retry, but repeated retries against a consistently failing or protected page add load and time without guaranteeing a screenshot.
  • Plan around page variability. Dynamic content, lazy loading, fonts, and animation can affect the resulting image. Wait for required content and keep the capture viewport and readiness rules consistent when comparing outputs.
  • Account for browser operations. Puppeteer requires your own browser setup and runtime resources. ScreenshotNeo is an API alternative with a free allowance of 1,000 shots per month; its published paid tiers are Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan.

FAQ

Can I use a custom user agent for a full-page screenshot?

Yes. Set it before navigation, then call page.screenshot({ fullPage: true }).

Does a custom user agent make Puppeteer look exactly like a specific phone?

No. The user-agent value alone does not set the viewport or guarantee that other device properties match. Use device emulation or configure the relevant settings explicitly.

Can I capture just one page element?

Yes. Select or wait for its element handle and call ElementHandle.screenshot().

Can I choose the screenshot output format?

Yes. Screenshot options include a format type; check the current Puppeteer reference for supported formats and option requirements.