Puppeteer screenshot with a specific user agent string
Set a custom user agent in Puppeteer before navigation, capture a screenshot, and learn when viewport emulation is needed.
Set the user agent before navigating, then capture the page with page.screenshot(). This changes the user-agent value Puppeteer sends; it does not by itself emulate a phone or guarantee that a site will serve the same content it would to a real device.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setUserAgent({
userAgent: 'YOUR USER AGENT STRING',
});
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'screenshot.png' });
} finally {
await browser.close();
}
The example uses the current options-object form of Page.setUserAgent(). For a device-like rendering, configure viewport and device metrics too, or use a Puppeteer device profile. See the setUserAgent API, mobile emulation guide, and screenshot guide.
1. Install Puppeteer and prepare the script
In a new project, install Puppeteer:
npm install puppeteer
Save the example as screenshot.mjs and run it with node screenshot.mjs. Puppeteer launches a browser, creates a page, sets its user agent, navigates to the target, writes a PNG file, and closes the browser even if navigation or capture fails.
Replace YOUR USER AGENT STRING with the exact value you want to test. Use a user-agent string appropriate to the target site and test environment. The documentation specifies how to configure it, but cannot guarantee how a particular site responds.
2. Set the user agent before navigation
Call await page.setUserAgent({ userAgent: '…' }) before page.goto() when you want the initial document request to use that configuration. Await the call so the setting completes before navigation starts.
await page.setUserAgent({ userAgent: 'YOUR USER AGENT STRING' });
await page.goto('https://example.com');
The current options object accepts userAgent and optional userAgentMetadata and platform. The positional setUserAgent(userAgent, userAgentMetadata?) form is marked obsolete in the current API reference. Check the documentation for the Puppeteer version installed in your project if the method signature differs.
3. Choose the right wait condition
The screenshot should be taken after the content you need has rendered. Puppeteer’s screenshot guide demonstrates networkidle2, but no single navigation wait condition fits every site.
loadwaits for the load event. Use it when the page’s needed content is available by then.domcontentloadedwaits for HTML parsing and deferred scripts, but does not wait for all images or later network requests.networkidle2waits for a period with no more than two active network connections. Analytics, polling, streaming, and other persistent requests can affect whether an idle condition is reached.
For a page that renders content after navigation, wait for the specific selector that signals readiness, then capture:
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('[data-ready="true"]');
await page.screenshot({ path: 'screenshot.png' });
Replace the selector with one that exists only when the content you need is ready. A fixed delay can be used for known animation or rendering delays, but it is less reliable than waiting for a meaningful page condition.
4. User agent versus device emulation
A user-agent override changes the user-agent setting. It does not automatically set a phone-sized viewport, touch behavior, or all other device characteristics. If your goal is to inspect a mobile layout, use a device profile or configure the page’s viewport as well.
Use a built-in device profile
import puppeteer, { KnownDevices } from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
const device = KnownDevices['iPhone 13'];
await page.emulate(device);
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'mobile.png' });
} finally {
await browser.close();
}
page.emulate(device) combines user-agent and viewport configuration. Emulate before navigation because viewport changes can affect how a site renders. Device names available in KnownDevices depend on the Puppeteer version; consult the device emulation guide.
Use a custom user agent and viewport
await page.setViewport({ width: 390, height: 844, deviceScaleFactor: 1 });
await page.setUserAgent({ userAgent: 'YOUR USER AGENT STRING' });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'custom-device.png' });
Choose dimensions and device scale that match the rendering you are trying to inspect. This sets a viewport and user-agent string; it is not a promise that every device signal or site behavior matches a physical phone.
5. Capture a full page or a selected element
By default, page.screenshot() captures the current viewport. For a page-length image, use fullPage: true:
await page.screenshot({ path: 'full-page.png', fullPage: true });
To capture a single element, locate it and use the element handle’s screenshot method:
const element = await page.waitForSelector('main article');
if (!element) throw new Error('Target element was not found');
await element.screenshot({ path: 'article.png' });
Puppeteer attempts to scroll an element into view for an element screenshot. If the selector matches multiple elements, choose the intended one explicitly. Full-page captures can be large, and content that loads only while scrolling may need to be triggered before capture.
6. Screenshot output options
For a typical image artifact, pass a file path. Puppeteer infers the format from the extension; the screenshot guide documents PNG, JPEG, and WebP. You can also supply screenshot options such as image quality for JPEG or WebP and omit the path to receive image bytes.
const bytes = await page.screenshot({ type: 'jpeg', quality: 80 });
await import('node:fs/promises').then(({ writeFile }) => writeFile('screenshot.jpg', bytes));
When an API specifically needs a base64 string, use the documented encoding option:
const base64 = await page.screenshot({ encoding: 'base64' });
Check the ScreenshotOptions API for the options supported by your installed Puppeteer version, including full-page capture and format-specific settings.
7. Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| The page still looks like desktop | Only the user-agent changed; the viewport stayed desktop-sized. | Set a mobile viewport or call page.emulate(device) before navigation. |
| The first request uses the old user agent | The page was navigated before the setting was applied, or the call was not awaited. | Await page.setUserAgent() before page.goto(). Reload or create a fresh page after changing the value. |
| The screenshot is blank or missing content | Capture happened before client-side rendering or a required element appeared. | Wait for a page-specific selector or meaningful readiness condition before taking the screenshot. |
| Navigation hangs or times out | The chosen idle condition may not occur because of long-lived or continuously active requests. | Choose a different waitUntil condition, then wait for the specific content needed. Set an explicit navigation timeout if appropriate. |
| Device profile import or lookup fails | The installed Puppeteer release may not expose the profile under that name. | Check the installed version and available KnownDevices; use a custom viewport and user agent if needed. |
| The site serves unexpected content or blocks the browser | Site behavior can depend on more than the configured user-agent string. | Validate against the target site and its access rules. The API configuration does not guarantee a specific response. |
8. Performance, reliability, and cost
Browser automation has setup and runtime costs: the script launches a browser, loads the page and its assets, and waits for the chosen readiness condition. Reuse a browser process for multiple captures when building a worker, while creating an appropriately isolated page or context for each job. Always close pages and browsers when work is complete.
Use the shortest wait that still captures the intended content. Waiting for all network activity can be slow or fail to settle on sites with persistent traffic; waiting only for DOM parsing can capture before images or client-rendered content appear. A selector tied to the content you need is often a better signal.
For repeatable results, keep the Puppeteer version, viewport, user agent, wait condition, and target state consistent. Pages can still vary because of site changes, personalized content, geolocation, cookies, or bot checks. No benchmark or universal capture time is implied here; measure the pages and environment you actually run.
Self-hosted Puppeteer has no per-screenshot service fee, but browser compute, memory, storage, and maintenance have costs. Keep concurrency within the limits of your host, and avoid launching an unbounded number of browsers at once.
Or skip the browser setup
ScreenshotNeo is a website screenshot API: one GET request returns an image or PDF. Cookie banners, newsletter popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000.
For an image capture, make the request with cURL:
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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(async ({ writeFile }) => {
await writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
});
See the ScreenshotNeo API documentation for request options. Create a free account at ScreenshotNeo sign-up to get 1,000 screenshots a month with no card.
FAQ
Can I change the user agent after navigation?
You can set it for subsequent page activity, but to ensure the initial document request uses the value, set it before navigating. Reload when you need the page fetched again with the changed setting.
Does a custom user agent make Puppeteer undetectable?
No. A user-agent string is one browser setting; the reviewed Puppeteer documentation makes no guarantee about detection or site access.
Can I screenshot just one component?
Yes. Use an element handle’s screenshot() method after selecting the element you want.
Can Puppeteer return screenshot bytes instead of writing a file?
Yes. Omit the path to receive image data, or request base64 encoding when a string is required.


