How to Take Full-Page Screenshots with Puppeteer, Playwright, and Selenium
Capture an entire scrollable page with Puppeteer, Playwright, or Selenium. See runnable examples, readiness tips, troubleshooting, and an API option.
For a full-page screenshot, use fullPage: true with Puppeteer or Playwright. With Selenium, use the full-document screenshot method documented for your specific browser and language binding; Selenium’s Firefox Python binding provides one. The exact page state matters: wait for the content you need before capturing it.
1. Playwright: capture the full scrollable page
Install Playwright and its browser, then save a full-page PNG. The fullPage option captures the full scrollable page instead of only the viewport. The example uses an explicit selector as a readiness condition; replace it with a selector that reliably appears when your target page is ready.
npm install playwright
npx playwright install chromium
// screenshot-playwright.mjs
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('body').waitFor({ state: 'visible' });
await page.screenshot({ path: 'full-page.png', fullPage: true });
} finally {
await browser.close();
}
Run it with node screenshot-playwright.mjs. To process the image in memory instead of saving it directly, omit path; Playwright returns a buffer from page.screenshot(). You can select type: 'jpeg' or type: 'webp', set quality for those formats, or use scale: 'css' to produce one image pixel per CSS pixel. The documented default for fullPage is false. See the Playwright Page screenshot API.
2. Puppeteer: capture the full page
Puppeteer also accepts fullPage: true; its documented default is false. This complete Node.js example waits for navigation, writes a PNG and closes the browser even if navigation or capture fails.
npm install puppeteer
// screenshot-puppeteer.mjs
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900 });
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('body');
await page.screenshot({ path: 'full-page.png', fullPage: true });
} finally {
await browser.close();
}
Run it with node screenshot-puppeteer.mjs. If the site’s content depends on network requests settling, Puppeteer’s guide demonstrates waitUntil: 'networkidle2'. Use that only when it matches the site: analytics, polling, streaming, or long-lived connections can prevent network-idle conditions from being useful. A page screenshot returns image data when you omit a file path; consult the Puppeteer screenshot API for the current return type and options.
3. Selenium: use a browser-specific full-document method
Selenium’s ordinary screenshot command should not be assumed to capture the entire document across every browser and binding. For Firefox with Selenium’s Python binding, use get_full_page_screenshot_as_file(). Install Selenium and ensure Firefox and its WebDriver setup are available in your environment.
python -m pip install selenium
# screenshot_selenium.py
from selenium import webdriver
driver = webdriver.Firefox()
try:
driver.get("https://example.com")
saved = driver.get_full_page_screenshot_as_file("full-page.png")
if not saved:
raise RuntimeError("Firefox did not save the screenshot")
finally:
driver.quit()
Remove the accidental leading space before driver = if copying this code literally; the runnable version is:
from selenium import webdriver
driver = webdriver.Firefox()
try:
driver.get("https://example.com")
saved = driver.get_full_page_screenshot_as_file("full-page.png")
if not saved:
raise RuntimeError("Firefox did not save the screenshot")
finally:
driver.quit()
The Firefox Python API also provides full-document screenshot methods that return PNG bytes or base64 data. Those are useful when an image must be passed to another function rather than saved to a file. Verify the API for your exact browser and language: full-page behavior is not uniform. See the Selenium Firefox Python WebDriver API.
4. Choose the right capture scope and output
| Need | Approach |
|---|---|
| Entire scrollable document | Playwright or Puppeteer fullPage: true; Selenium’s browser-specific full-document API where supported. |
| One component or region | Use the framework’s element or locator screenshot API rather than capturing the whole page and cropping afterward. |
| Image saved to disk | Pass a screenshot path where supported, then check that the file exists and is non-empty. |
| Image sent to another function | Use returned buffer/bytes APIs where available and avoid an unnecessary file write. |
Playwright and Puppeteer document separate element screenshot APIs, so choose a page-level capture only when the whole scrollable document is the desired scope. A full-page image can become very tall; if the consumer expects a viewport image, a PDF, or a set of tiles, choose that output intentionally.
5. Make the capture representative
Wait for the content you need
Navigation completion does not guarantee that application data, fonts, images, or client-rendered sections are ready. Wait for a meaningful page-specific signal: a heading, a completed loading state, a known number of cards, or an application event. Avoid arbitrary sleeps as the only readiness check; they can be both too short on a slow run and wasteful on a fast one.
await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('[data-page-ready="true"]');
await page.screenshot({ path: 'full-page.png', fullPage: true });
For lazy-loaded content, the site may load sections only as they enter the viewport. A full-page option describes capture scope; it does not promise that every application has already fetched every below-the-fold resource. If lower sections are missing, scroll through the page before capturing, wait for images or content to appear, then take the screenshot. Implement this only when the target site needs it; scrolling can trigger sticky-header changes, infinite loading, or other page behavior.
Control the environment when repeatability matters
Set a consistent viewport, use the same browser version and device scale, and keep authentication and locale consistent across runs. Close the browser in a finally block so errors do not leave browser processes behind. For visual checks, use the same capture settings on baseline and comparison runs.
6. Or skip the browser setup
ScreenshotNeo takes a screenshot through one HTTP request. It accepts a URL and can return PNG, JPEG, WebP, or PDF. The example saves a WebP response; see the ScreenshotNeo API documentation for request options, including full-page capture and output configuration.
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,
)
r.raise_for_status()
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 Node.js versions that do not provide Bun’s Bun.write, save the response using Node’s filesystem API:
import { writeFile } from 'node:fs/promises';
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 writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo removes known cookie and consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers say the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots. Learn about ScreenshotNeo or sign up for 1,000 free screenshots a month, no card required.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Only the visible viewport was captured | The full-page option was omitted, false, or unsupported by that Selenium driver/binding. | Set fullPage: true in Playwright/Puppeteer. For Selenium, use and verify the browser-specific full-document method. |
| Lower page sections are blank or absent | Lazy loading or client-side rendering had not finished. | Wait for a page-specific ready signal; scroll to trigger lazy sections and wait for their content before capture. |
| Navigation or readiness wait hangs | The site keeps connections open or the chosen condition never occurs. | Use a less restrictive navigation condition and wait for the specific content required. Add an explicit timeout appropriate to your workflow. |
| Screenshot is unexpectedly huge or memory use rises | The document is exceptionally tall or rendered at device-pixel scale. | Capture only the needed element, use CSS-pixel scale where available, or capture sections separately. Avoid concurrent captures that exceed available memory. |
| Fonts or images differ between runs | Resources were not ready, or browser, viewport, scale, or environment changed. | Wait for the relevant resources, standardize browser and viewport configuration, and compare equivalent output settings. |
| Selenium reports an unsupported operation | The requested full-page operation is not implemented by the selected browser/driver/binding combination. | Check that binding’s API. Use a supported full-document method or a browser automation framework with a documented full-page option. |
| File is missing or empty | Wrong working directory, failed write, or a false return value from Selenium’s file method. | Use an absolute path, check the method result, and verify the resulting file before downstream processing. |
8. Performance, reliability, and cost
Full-page capture requires rendering the document’s full extent, so a very tall page can take more memory and produce a larger image than a viewport capture. CSS-pixel output can reduce pixel dimensions where the framework supports it. JPEG or WebP can reduce output size compared with PNG, with the tradeoff that lossy formats may alter fine details. Choose based on whether the image is for visual comparison, archival, or transfer.
For reliable automation, close browsers in cleanup logic, set deliberate navigation and selector timeouts, and record which URL and capture settings produced each artifact. Retries should be limited to transient navigation or infrastructure failures; retrying a deterministic selector error will not make the page ready. No speed benchmark applies universally: site complexity, browser, machine, image dimensions, and readiness condition all affect the work.
Self-hosted browser automation has infrastructure and maintenance costs: browser processes, runtime, storage, and handling failures. ScreenshotNeo offers a free allowance of 1,000 shots monthly with no card. Paid plans 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. Every feature is on every plan. Use the service’s stated billing headers to distinguish billed clean captures from non-billed results.
9. Frequently asked questions
Does full-page mean a screenshot of the whole website?
No. It means the full scrollable document for the page you opened, not every URL on the site.
Can I get image bytes instead of a file?
Yes. Playwright’s screenshot call returns a buffer when you omit the path. Selenium’s Firefox Python API documents byte and base64 full-document methods. Puppeteer also exposes screenshot output APIs; consult its current API for the return type in your version.
Should I use a screenshot or PDF for a long page?
Use a screenshot when you need a raster image. Use PDF when you need a paginated document; ScreenshotNeo supports PDF output through its API.
Can I capture only a page element?
Yes. Playwright and Puppeteer have separate element screenshot methods. Selenium’s available element and full-document methods depend on the driver and binding.


