ScreenshotNeo

BlogHow-to

How to Capture a Full-Page Screenshot of a Webpage with WebDriver

Capture a full-page screenshot with Selenium in Firefox, or use Chrome DevTools Protocol in Chrome. Learn the code, limits, and troubleshooting steps.

By the ScreenshotNeo team4 October 202610 min read

Short answer: A normal WebDriver screenshot captures the current browsing context; it is not a portable guarantee of a full-document image. With Selenium, use Firefox’s documented full-page screenshot API for a full-page PNG. With Chrome, use the Chrome DevTools Protocol (CDP) Page.captureScreenshot command with captureBeyondViewport: true. CDP is a Chrome-specific protocol route, not a general WebDriver feature. In either browser, wait for the page to settle and inspect the result for your target site.

Choose the capture method

Browser and route What it captures Output Key caveat
Selenium with Firefox Full document through Selenium’s Firefox API PNG file or PNG bytes Firefox-specific API; use a Selenium binding that exposes it
Chrome with CDP Beyond-viewport capture when enabled Base64 image data, decoded to a file Browser-specific protocol, separate from ordinary WebDriver screenshot
Ordinary WebDriver screenshot Current browsing context/window Usually a PNG file or bytes, depending on binding Do not assume it captures the full document

Selenium’s [window documentation](https://www.selenium.dev/documentation/webdriver/browser/windows/) demonstrates the ordinary current-window screenshot behavior. The [Python Firefox API](https://www.selenium.dev/selenium/docs/api/py/selenium_webdriver_firefox/selenium.webdriver.firefox.webdriver.html) documents full-document methods, and Selenium’s [Java full-page interface](https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/firefox/HasFullPageScreenshot.html) lists FirefoxDriver as an implementation. Chrome’s [CDP Page domain](https://chromedevtools.github.io/devtools-protocol/tot/Page/) documents Page.captureScreenshot and captureBeyondViewport. The live CDP reference can evolve, so check the protocol supported by your installed Chrome and Selenium versions.

Python: full-page screenshot with Selenium and Firefox

Install Selenium, ensure Firefox is installed, and run this script. Selenium’s driver management should be configured for your environment; if it cannot locate or start Firefox, see troubleshooting below. The Firefox API documents save_full_page_screenshot(filename) and get_full_page_screenshot_as_png().

python -m pip install selenium
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.firefox.options import Options

url = "https://example.com"
output = Path("full-page.png")

options = Options()
options.add_argument("-headless")  # Remove this line to see the browser.

with webdriver.Firefox(options=options) as driver:
    driver.set_window_size(1440, 1000)
    driver.get(url)

    # Wait for the document load event. JavaScript-rendered pages may need
    # an additional explicit wait for their important content.
    driver.save_full_page_screenshot(str(output))

print(f"Saved {output.resolve()}")

The filename should end in .png. To get bytes instead of saving through the driver, use:

from pathlib import Path
from selenium import webdriver
from selenium.webdriver.firefox.options import Options

options = Options()
options.add_argument("-headless")

with webdriver.Firefox(options=options) as driver:
    driver.get("https://example.com")
    png_bytes = driver.get_full_page_screenshot_as_png()
    Path("full-page.png").write_bytes(png_bytes)

Wait for dynamic content before capturing

A navigation completing does not mean that every application has finished rendering. Wait for a page-specific element or condition before taking the screenshot. For example:

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

# After driver.get(url):
WebDriverWait(driver, 20).until(
    EC.visibility_of_element_located((By.CSS_SELECTOR, "main article"))
)
driver.save_full_page_screenshot("article.png")

Replace the selector with an element that signals the content you need. A fixed delay can be useful for a known animation or delayed widget, but a condition tied to the page is usually more reproducible. Lazy-loaded images may only load when scrolled into view; verify whether the Firefox full-document capture triggers the behavior your page needs, and explicitly scroll or otherwise load content when necessary.

Chrome: use the Chrome DevTools Protocol

WebDriver’s standard screenshot operation and CDP are distinct layers. In Chrome, the CDP Page.captureScreenshot command accepts captureBeyondViewport, whose documented default is false. Enable it to request capture beyond the viewport. The example below uses Selenium Python’s CDP command interface and writes the returned base64 data as a PNG.

import base64
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait

url = "https://example.com"
options = Options()
options.add_argument("--headless=new")

with webdriver.Chrome(options=options) as driver:
    driver.set_window_size(1440, 1000)
    driver.get(url)
    WebDriverWait(driver, 20).until(
        lambda d: d.execute_script("return document.readyState") == "complete"
    )

    result = driver.execute_cdp_cmd(
        "Page.captureScreenshot",
        {
            "format": "png",
            "captureBeyondViewport": True
        }
    )
    Path("full-page.png").write_bytes(base64.b64decode(result["data"]))

This is a CDP call through Selenium’s Chrome integration, not a portable Selenium WebDriver capability. Selenium’s CDP integration and the available protocol commands can vary with versions; if the call is unavailable or behaves differently in your setup, consult the [CDP Page reference](https://chromedevtools.github.io/devtools-protocol/tot/Page/) and your Selenium binding documentation. The protocol documents the screenshot result as base64-encoded image data.

CDP options to know

Page.captureScreenshot has protocol options beyond the basic example. Use only options supported by your browser’s CDP version:

Option Purpose Practical note
format Image encoding format The example requests PNG. Confirm supported values in the protocol version you use.
captureBeyondViewport Whether to capture outside the viewport Its documented default is false; set it explicitly for this use case.
clip Restricts capture to a specified rectangle Useful for a region; it is not a general full-document solution.
fromSurface Controls whether to capture from the surface Leave the default unless your rendering case requires different behavior.
quality Image quality for supported lossy formats Relevant to JPEG or WebP where supported, not PNG.

Java: documented Firefox full-page API

Selenium’s Java HasFullPageScreenshot interface describes full-page capture and is implemented by FirefoxDriver. This example saves the API’s screenshot data to a file. Check the interface in the Selenium version you use because it is Firefox-specific.

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;

import org.openqa.selenium.firefox.FirefoxDriver;

public class FullPageScreenshot {
    public static void main(String[] args) throws Exception {
        FirefoxDriver driver = new FirefoxDriver();
        try {
            driver.get("https://example.com");
            String dataUrl = driver.getFullPageScreenshotAs("png");
            String base64 = dataUrl.substring(dataUrl.indexOf(',') + 1);
            Files.write(Path.of("full-page.png"), Base64.getDecoder().decode(base64));
        } finally {
            driver.quit();
        }
    }
}

The interface’s available method signatures can vary by Selenium release. Use the [Selenium Java API](https://www.selenium.dev/selenium/docs/api/java/org/openqa/selenium/firefox/HasFullPageScreenshot.html) for the version in your project and adapt the save call accordingly. This Java route is the documented Firefox implementation, not a cross-browser API.

Using another binding or browser

First identify the browser, Selenium language binding, and their versions. Then check whether that binding exposes Firefox’s full-page API. Selenium documents the Python Firefox file and byte methods, and a Java full-page interface implemented by FirefoxDriver. Other bindings may expose different method names or may not provide the same method. For Chrome, use CDP only when your Selenium integration supports issuing the relevant command. Do not copy a binding-specific call and assume it works unchanged in another language or browser.

Firefox also documents full-page capture in its Developer Tools and command-line screenshot options. Those are Firefox tools, not Selenium APIs; the [Firefox screenshot guide](https://firefox-source-docs.mozilla.org/devtools-user/taking_screenshots/index.html) explains that --fullpage includes portions outside the current window bounds.

Capture quality and page behavior

  • Set a reproducible viewport: use the same window size, browser version, device scale settings, locale, and font environment for repeatable output.
  • Wait for the content you need: page load completion may precede client rendering, image decoding, or delayed content. Wait on meaningful page state.
  • Lazy content: full-page capture APIs do not by themselves establish that every lazy image or section will load. Scroll through the document first if the site loads content only when it approaches the viewport, then wait for the content.
  • Fixed and sticky elements: headers, cookie banners, and floating controls may appear in unexpected positions or repeat in stitched captures. Inspect the output at multiple scroll positions when the page uses them.
  • Animation and changing content: videos, carousels, clocks, and live feeds can make captures nondeterministic. If visual consistency matters, wait for a stable state or use site-specific CSS or test setup to freeze the changing content.
  • Frames and embedded content: cross-origin frames and browser rendering boundaries can affect what appears. Validate the target page rather than assuming every embedded surface is represented identically.
  • Very long documents: output dimensions and memory needs grow with page height. A browser or image decoder can run out of memory, and some consumers cannot handle extremely tall images. Capture sections or use PDF/page-based output when a single raster image is impractical.

Custom scrolling and stitching fallback

If neither browser route is available, you can build a custom viewport-stitching workaround: capture successive viewport images, scroll between them, then combine the images. This is not an official Selenium full-page method and has fidelity risks:

  • Use overlap between captures so small scroll rounding differences do not leave gaps.
  • Fixed headers and floating buttons are captured in every viewport and can repeat in the combined image.
  • Lazy-loaded content must be given time to appear after scrolling.
  • Dynamic pages may change while the browser scrolls, producing duplicated or missing content.
  • Device pixel ratio, browser zoom, and fractional scroll positions can cause seams or mismatched dimensions.

For a reliable workflow, prefer the browser’s documented full-page path where it fits your browser, and treat stitching as a page-specific workaround that requires visual inspection.

Troubleshooting

Symptom Likely cause Fix
AttributeError for save_full_page_screenshot The driver is not FirefoxDriver, or the installed Selenium binding/version does not expose that method. Use Firefox with a binding version whose API documents the method, or use the documented bytes method. For Chrome, use the CDP route.
Screenshot contains only the visible viewport The call used the ordinary WebDriver screenshot API, or the Chrome CDP request did not enable beyond-viewport capture. Use Firefox’s full-page API or explicitly set CDP captureBeyondViewport to true.
CDP command is unknown or unsupported The browser/Selenium combination does not expose that protocol method, or protocol versions differ. Check the browser and Selenium versions and CDP reference. Use Firefox’s API if Firefox is an option.
PNG file is empty or unreadable Base64 was not decoded, the wrong return value was written, or a data URL prefix was included in the decode input. For CDP, decode the response’s data. For Java data URLs, remove the prefix through the comma before decoding.
Missing sections or images Client rendering or lazy loading had not completed before capture. Wait for a page-specific selector; scroll to trigger lazy loads, then wait for images/content before capturing.
Repeated header or floating control A fixed or sticky element appears during full-page rendering or each stitched viewport. Use site-specific CSS/test configuration to hide it when appropriate, or use a browser full-page API and inspect the output. In stitching, remove duplicate regions carefully.
Driver fails to start Browser is missing, driver/browser compatibility is wrong, or headless settings differ by version. Install the target browser, resolve the driver for that browser version, and try without headless mode to inspect startup errors.
Capture is too tall or process runs out of memory The full raster exceeds browser, process, or downstream image limits. Capture the page in sections, reduce scale or dimensions where possible, or choose a page-based format instead of one enormous image.

Performance, reliability, and cost

Local Selenium capture has no per-request screenshot API charge, but it does require browser processes, driver setup, CPU, memory, storage, and maintenance of browser and binding versions. Full-page images can consume substantial memory for very tall pages; parallel browser sessions multiply resource needs. Reuse a controlled environment and avoid excessive concurrency if the machine becomes memory- or CPU-bound.

Reliability depends on the target page as much as the capture API: waits, animations, anti-automation behavior, network variation, and dynamic content all affect results. Record the browser, binding, viewport, and wait condition with each run so failures can be reproduced. Capture a known page as a smoke check after changing browser or Selenium versions.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. A single request returns an image or PDF, without maintaining Selenium and browser-driver setup. Use the ScreenshotNeo API docs for its request options.

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}`);

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture, with each cleanup step configurable. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers report the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.

FAQ

Does WebDriver have a standard full-page screenshot command?

The ordinary screenshot operation is not a cross-browser full-document guarantee. Selenium documents a Firefox full-page API; Chrome’s beyond-viewport route uses CDP.

Can I save the Firefox screenshot as JPEG?

The documented Python full-page file method expects a PNG filename. If you need another format, save the PNG and convert it with an image library, accounting for the conversion’s memory use on tall images.

Will full-page capture trigger every lazy image?

Do not assume so. Lazy-loading behavior depends on the page and capture route. Scroll through and wait for the required content, then verify the output.

Should I use stitching for production?

Use it only when a native browser path does not meet the need and you can validate the page-specific result. Fixed elements and changing content commonly make stitching unreliable.