ScreenshotNeo

BlogHow-to

How to Capture a Webpage Screenshot with Selenium Using Chrome’s CDP

Capture a webpage with Selenium and Chrome’s CDP, including formats, clipped regions, full-page caveats, runnable code, and troubleshooting.

By the ScreenshotNeo team4 October 20268 min read

Use Selenium Python’s execute_cdp_cmd to send Chrome DevTools Protocol (CDP) the Page.captureScreenshot command. The command returns image data as a base64 string; decode it before writing the image file. For an ordinary screenshot of the current browsing context, Selenium’s simpler save_screenshot method is usually enough. Use CDP when you need its format, clip, or beyond-viewport parameters.

Capture a screenshot with Selenium and CDP

This example opens a page, asks Chrome to capture it as PNG, decodes the returned data, saves it, and always closes the browser.

import base64
from selenium import webdriver

options = webdriver.ChromeOptions()
# For a headless run, uncomment:
# options.add_argument("--headless=new")

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.com")

    result = driver.execute_cdp_cmd(
        "Page.captureScreenshot",
        {
            "format": "png",
            "captureBeyondViewport": True,
        },
    )

    image_bytes = base64.b64decode(result["data"])
    with open("screenshot.png", "wb") as image_file:
        image_file.write(image_bytes)
finally:
    driver.quit()

Install Selenium in the Python environment with python -m pip install selenium. Selenium Manager can manage a compatible browser driver in supported setups; in restricted CI environments, install and configure Chrome and ChromeDriver according to that environment’s requirements. The code uses the Python binding because Selenium’s execute_cdp_cmd method is documented there.

What the CDP command returns

Page.captureScreenshot returns a response dictionary whose data field contains base64-encoded image data. The code decodes that string to bytes before saving. Selenium documents execute_cdp_cmd(cmd, cmd_args) as its method for sending a CDP command and receiving the response as a dictionary. See the [Selenium CDP command documentation](https://www.selenium.dev/selenium/docs/api/py/webdriver_chromium/selenium.webdriver.chromium.webdriver.html) and the [Chrome DevTools Protocol Page domain](https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-captureScreenshot).

Choose between Selenium’s screenshot API and CDP

Approach Best for Output handling Options
driver.save_screenshot(path) A straightforward screenshot of the current browsing context Selenium writes the file directly Simple screenshot workflow
Page.captureScreenshot through CDP Cases needing CDP-specific capture controls Decode the returned base64 data and write bytes Format, clip, JPEG quality, and experimental beyond-viewport capture

Selenium’s standard method is the shorter choice when you do not need CDP parameters:

from selenium import webdriver

driver = webdriver.Chrome()
try:
    driver.get("https://example.com")
    if not driver.save_screenshot("screenshot.png"):
        raise RuntimeError("Selenium did not save the screenshot")
finally:
    driver.quit()

Selenium’s WebDriver screenshot API also uses a base64-encoded screenshot response internally, while save_screenshot handles saving for you. See [Selenium’s screenshot documentation](https://www.selenium.dev/documentation/webdriver/interactions/windows/#take-screenshot).

CDP capture options

The Page command documents these relevant parameters. Check the protocol version available in the Chrome build you run, especially for experimental behavior.

Parameter Use Notes
format Choose png, jpeg, or webp PNG is the documented default.
quality Set JPEG image quality from 0 to 100 Applies to JPEG; it is not a general PNG quality setting.
clip Capture a specific rectangle Supply the clip coordinates and dimensions expected by the protocol, and check the rendered page layout when the result is misaligned.
captureBeyondViewport Request capture beyond the visible viewport Experimental and defaults to false. Full-document results can depend on Chrome/CDP version and page behavior.

For example, request WebP output or a JPEG clip by changing the parameters:

# WebP capture
webp_result = driver.execute_cdp_cmd(
    "Page.captureScreenshot",
    {"format": "webp"},
)
with open("screenshot.webp", "wb") as image_file:
    image_file.write(base64.b64decode(webp_result["data"]))

# JPEG capture of a protocol clip rectangle
jpeg_result = driver.execute_cdp_cmd(
    "Page.captureScreenshot",
    {
        "format": "jpeg",
        "quality": 85,
        "clip": {
            "x": 0,
            "y": 0,
            "width": 800,
            "height": 600,
            "scale": 1,
        },
    },
)
with open("region.jpg", "wb") as image_file:
    image_file.write(base64.b64decode(jpeg_result["data"]))

The clip coordinates describe the region to capture; confirm its position against the page’s layout and the protocol schema for the installed browser. Do not assume that the experimental beyond-viewport parameter behaves identically in every Chrome version.

Full-page captures and page readiness

Setting captureBeyondViewport to true requests beyond-viewport capture, but it is not a version-independent guarantee of a complete, correctly laid out document screenshot. The option is experimental. Chrome’s tip-of-tree protocol reference can change frequently and does not promise backward compatibility; use the protocol documentation corresponding as closely as possible to the Chrome version in your local or CI environment.

Capture after navigation and after the page content you need has appeared. Selenium’s navigation wait behavior does not ensure that every asynchronous widget, image, or application update has finished. For a page with a known readiness marker, wait for it before issuing the CDP command:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

# After driver.get(url):
WebDriverWait(driver, 20).until(
    lambda browser: browser.find_element(By.CSS_SELECTOR, "main")
)
# Then call Page.captureScreenshot.

For lazy-loaded content, a page may load images only as they approach the viewport. A single beyond-viewport capture does not itself promise that every lazy image has loaded. If full-page completeness matters, use a page-specific readiness condition and verify the capture in the target Chrome version.

Run it reliably in CI

  • Pin and record the Chrome and ChromeDriver versions used by your job so CDP behavior is easier to reproduce.
  • Use a headless Chrome configuration appropriate to your environment, and make sure the browser can access the target URL.
  • Wait for a page-specific condition when the screenshot depends on asynchronous content.
  • Put driver.quit() in a finally block so exceptions during navigation or capture do not leave the browser process running.
  • Save the command response or relevant browser and driver versions when diagnosing protocol errors; inspect the returned dictionary before assuming it has a data field.

CDP introduces a compatibility consideration: Chrome’s tip-of-tree protocol may not match an older installed browser, and the reference makes no backward-compatibility guarantee. Keep the capture parameters conservative if your automation must run across multiple browser versions.

Troubleshooting

Symptom Likely cause What to do
KeyError: 'data' The command response did not contain the expected image field, perhaps because the command failed or returned a different response. Inspect and log the returned dictionary, confirm the CDP command spelling and arguments, and check Chrome’s version and protocol support.
Only the visible viewport appears captureBeyondViewport defaults to false, or the installed browser does not support the expected experimental behavior. Set it to true, check the browser version, and validate the resulting image. For a dependable full-page workflow, verify behavior in the exact CI Chrome build.
The capture is blank or incomplete The page may not have reached the state required for the capture, or content may be loaded asynchronously. Wait for a page-specific selector or state before capturing; check that navigation succeeded and the target content is present.
The captured region is shifted or has unexpected dimensions The clip rectangle does not match the page’s current layout or the intended coordinate region. Review x, y, width, height, and scale, then compare them with the rendered page.
CDP command is rejected The installed browser’s protocol may not support the supplied parameter or may differ from the tip-of-tree schema. Check the Chrome/CDP version and remove or adjust version-sensitive parameters. The protocol overview notes that tip-of-tree changes frequently and has no backward-compatibility guarantee.
Chrome or ChromeDriver fails to start The browser, driver, or CI environment is missing a compatible installation or configuration. Install compatible versions and configure the browser for the runner. Confirm a basic Selenium navigation succeeds before investigating screenshot-specific parameters.
Browser processes remain after a failure Cleanup was skipped on an exception path. Call driver.quit() in a finally block, as in the examples.

Performance, reliability, and cost

Screenshot capture requires a browser to start, navigate, render the page, and encode an image. The total work therefore depends on the target page and the browser environment; no fixed capture time or resource requirement applies to every page. Reuse a browser session when your workload and isolation requirements allow it, but create a fresh session when you need a clean browser state between captures. Always close sessions you no longer need.

PNG, JPEG, and WebP are supported by the CDP command. Choose based on the output you need; JPEG’s documented quality setting trades image fidelity against encoded size. CDP itself adds version-management work because the protocol can change. This local Selenium workflow has no per-capture ScreenshotNeo charge, but it uses your own browser and compute resources.

Or skip the browser setup

If you need screenshots without managing Chrome and ChromeDriver, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns an image or PDF; the API accepts the same parameter names other screenshot APIs use, which can make a switch easier. The [ScreenshotNeo documentation](https://screenshotneo.com/docs/) lists its API options.

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}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
  • Cookie and consent banners are accepted like a visitor, and 60+ known consent platforms, newsletter popups, and chat widgets are removed before the shot; each step can be turned off.
  • Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Responses include X-Page-Verdict and X-Billed headers.
  • An MCP server gives AI agents, including Claude, Cursor, and other MCP clients, the take_screenshot, get_page_info, and capture_pdf tools.
  • The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is on every plan.

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

FAQ

Can Selenium send CDP commands directly?

Yes. In Selenium’s Python binding, use driver.execute_cdp_cmd(command, arguments) and inspect the returned dictionary.

Which image formats can CDP capture?

The Page command documents PNG, JPEG, and WebP. PNG is the default; the quality parameter applies to JPEG.

Does captureBeyondViewport guarantee a complete full-page screenshot?

No. It is experimental, defaults to false, and should be checked against the Chrome version and page behavior you use.

When should I use save_screenshot instead?

Use it for a straightforward screenshot of the current browsing context when you do not need CDP-specific controls; it saves directly to a file.

Sources