How to Capture a Website Screenshot with Puppeteer at Android Viewport Dimensions
Set a Puppeteer device profile or explicit mobile viewport before navigation, then capture the visible page. Learn sizing, options, troubleshooting, and a no-browser-setup alternative.
To capture a website screenshot at Android viewport dimensions with Puppeteer, configure a mobile viewport or emulate a named device before navigating, wait for the page state you need, and call page.screenshot(). There is no single universal Android viewport: use a specific Puppeteer device profile when you want its supplied metrics and user agent, or set explicit CSS viewport dimensions that match your target.
Choose a device profile or explicit viewport
A named profile is convenient when you want Puppeteer’s device metrics and user agent together. An explicit viewport is useful when you already know the CSS width and height your capture should use. The example dimensions below are illustrative; they are not a universal Android standard.
| Approach | What it configures | Use it when |
|---|---|---|
page.emulate(KnownDevices['Pixel 7']) |
The selected profile’s device metrics and user agent | You want a named Puppeteer profile and do not need to specify each value yourself |
page.setViewport(...) |
The CSS viewport and mobile-related flags you provide | You need a particular width and height or want the configuration to be explicit |
Emulation represents device metrics and user-agent settings in the browser. It does not prove how the page renders on physical Android hardware; validate on a real device separately when hardware-specific behavior matters. Configure the profile or viewport before navigation because resizing can affect a site, and changing mobile or touch settings may trigger a reload in some cases.
Capture a viewport screenshot with Puppeteer
Install Puppeteer in a Node.js project, save the following as screenshot.mjs, and run it with node screenshot.mjs. The first version uses a named device profile. Replace the URL with the page you need to capture.
import puppeteer, { KnownDevices } from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
// Apply device metrics and user agent before navigating.
await page.emulate(KnownDevices['Pixel 7']);
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'android-viewport.png' });
} finally {
await browser.close();
}
The screenshot is the visible viewport because fullPage defaults to false. The finally block closes the browser even if navigation or capture fails.
Use explicit viewport dimensions
If you need a chosen CSS viewport instead of a named profile, replace the page.emulate() line with page.setViewport(). Pick dimensions and flags that correspond to your target. These values are only an example configuration.
await page.setViewport({
width: 412,
height: 915,
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true,
});
Set deviceScaleFactor to the scale you need for the output. A named profile applies its own metrics; do not then assume the screenshot’s pixel dimensions equal the CSS viewport dimensions. Inspect the saved image if exact output dimensions matter.
Decide what page state to capture
Choosing viewport dimensions is only one part of a useful screenshot. Select a navigation wait condition and, when necessary, wait for the specific content that should appear.
waitUntil: 'load'waits for the load event. It may be suitable for simpler pages, but does not guarantee that client-rendered content or late images are ready.waitUntil: 'domcontentloaded'proceeds after the initial document has been parsed. Use an additional wait when the target content loads afterward.waitUntil: 'networkidle2'waits for a period with no more than two network connections. This is the example used above; pages with ongoing requests may not become idle.- Wait for a known selector when the screenshot depends on a specific element. For example:
await page.waitForSelector('[data-ready="true"]'). Use a selector that the target page actually exposes. - For a page with a known, short animation or delayed render, a deliberate delay can help, but it adds time and is less precise than waiting for a meaningful selector.
For example, add this after page.goto() and before page.screenshot() when the page signals readiness with a selector:
await page.waitForSelector('[data-ready="true"]', { timeout: 15000 });
Do not combine every wait condition by default. Pick the condition that reflects how the page works, and set timeouts appropriate to the environment.
Screenshot options and capture extent
Use the default viewport capture when the goal is an Android-sized screenshot. Set fullPage: true only when you want the full document, which can be much taller than the viewport.
await page.screenshot({
path: 'android-full-page.png',
fullPage: true,
type: 'png',
});
Useful screenshot options include:
path: save the screenshot to a file.fullPage: capture the full document instead of only the viewport.type: choose an image format such as PNG, JPEG, or WebP where supported by the installed Puppeteer version.quality: set image quality for JPEG or WebP where supported; it does not apply to PNG.clip: capture a specified rectangular region instead of the usual viewport or full document.omitBackground: omit the default background in formats that support transparency.
Consult the Puppeteer screenshot options reference for the exact options supported by your installed version. A clipped screenshot is a region of the rendered page, not a way to define a different device profile.
Common errors and fixes
| Symptom | Likely cause | What to do |
|---|---|---|
| The page looks like a desktop layout | The viewport or device profile was applied after navigation, or mobile emulation was not enabled. | Set page.emulate() or page.setViewport() before page.goto(). For an explicit mobile viewport, check isMobile and hasTouch as needed. |
| The screenshot has unexpected dimensions | The selected profile’s scale or the output image pixel size differs from the CSS viewport size. | Review the profile or explicit deviceScaleFactor, and inspect the saved image’s pixel dimensions. |
| Content or images are missing | The screenshot ran before client-side rendering or lazy-loaded content completed. | Wait for a meaningful selector, or use a suitable navigation wait condition. For below-the-fold lazy content, a viewport screenshot may not trigger loading; use full-page capture when that is the intended output and verify the page’s behavior. |
| Navigation times out | The site keeps network connections open, is slow, or cannot be reached from the capture environment. | Try a less restrictive navigation condition such as domcontentloaded, then wait for the required selector. Set an explicit timeout and confirm the URL is reachable. |
KnownDevices profile is undefined |
The installed Puppeteer version or import style does not expose the expected profile. | Check the installed package and its documentation. Alternatively, configure an explicit viewport with setViewport(). |
| Capture fails because the browser will not launch | The browser binary is missing or the runtime lacks required system dependencies. | Follow the installation instructions for the Puppeteer package and the operating system or container where the script runs. |
Performance, reliability, and cost considerations
Browser startup, page navigation, page scripts, and image decoding all add work to a capture. Reuse a browser process for multiple pages when running a batch, while creating a separate page for each capture and closing pages when done. Avoid waiting for network idle on sites that continuously poll or stream; wait for the content that matters instead.
For more reliable output, use a stable target URL, explicit viewport configuration, a selector-based readiness condition when possible, and a bounded timeout. Handle navigation and screenshot errors in your application, and close the browser in a finally block. A viewport screenshot is generally smaller and quicker to process than a full-page capture, although actual time and file size depend on page content and environment.
Puppeteer is an open-source browser automation library; the code shown has no per-screenshot API charge. Your operational costs may include compute, browser hosting, and maintenance of the capture environment. No fixed runtime or cost estimate applies across different pages and machines.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns an image or PDF. Its capture options include viewport configuration, device presets, full-page capture, and output format. See the ScreenshotNeo API documentation for request parameters and examples.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
With ScreenshotNeo, cookie banners, 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. Learn about ScreenshotNeo, then sign up free for 1,000 screenshots a month with no card.
FAQ
Does Puppeteer emulate a real Android phone?
It applies browser device metrics and a user agent for emulation. That is useful for responsive page captures, but it is not physical-device validation.
Should I use a device profile or set dimensions myself?
Use a profile when its supplied metrics and user agent match your target. Set dimensions explicitly when your required CSS viewport is known and you want to state those values directly.
How do I capture only the visible screen?
Call page.screenshot() without fullPage: true; viewport capture is the default.
Why does my page differ from a phone screenshot?
Responsive layout can depend on more than width and height, including user agent, touch behavior, loaded content, and browser differences. Confirm the emulated settings and validate on the target device if exact hardware behavior matters.
References: Puppeteer page.emulate(), Puppeteer page.setViewport(), Puppeteer screenshot guide, and screenshot options.


