Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use Selenium’s headless mode. Add --headless=new to Chrome/Chromium options (or --headless for Firefox), create the WebDriver with those options, then call the normal screenshot method. The browser still loads and renders the page; it simply does not display a GUI window.

Chrome or Chromium: a complete headless screenshot

This Python example uses Selenium 4 with Chrome or Chromium. It fixes the viewport, navigates to a page, saves a PNG, checks whether Selenium reported a write failure, and always closes the session.

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1280,900")

driver = webdriver.Chrome(options=options)
try:
    driver.get("https://example.com")
    ok = driver.save_screenshot("screenshot.png")
    if not ok:
        raise RuntimeError("Screenshot could not be written")
finally:
    driver.quit()

--headless=new selects Chromium’s current headless implementation. Set the argument on the exact Options object passed to webdriver.Chrome; setting it on a different object has no effect. --window-size=1280,900 makes the viewport reproducible across a laptop, container, and CI runner. The screenshot is a viewport image, not automatically the entire document.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Prerequisites

  • Install Selenium for Python: python -m pip install -U selenium.
  • Have a compatible Chrome/Chromium browser available. Selenium Manager can usually obtain a driver; locked-down CI images may require you to install and pin the browser and driver yourself.
  • Run the process with write permission for the destination path.

Chrome’s current headless implementation shares code with headful Chrome. From Chrome 132.0.6793.0 onward, the old implementation is provided only as a separate chrome-headless-shell binary, so tutorials using legacy headless behavior may not match current Chrome.

Firefox: headless and full-document capture

Firefox exposes a Selenium method for a full-page PNG in addition to the normal viewport screenshot.

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

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

driver = webdriver.Firefox(options=options)
try:
    driver.get("https://example.com")
    if not driver.save_screenshot("firefox-viewport.png"):
        raise RuntimeError("Viewport screenshot could not be written")
    driver.save_full_page_screenshot("firefox-full-page.png")
finally:
    driver.quit()

Use save_screenshot when you want what is visible in the current viewport. Firefox’s save_full_page_screenshot captures the document beyond the viewport. Full-page behavior is driver-specific; do not assume the same method or output on every browser.

What each setting and method actually does

Item Purpose Important limitation
--headless=new Runs Chromium without a visible window. Must be attached to the options passed to the driver.
--headless Runs Firefox without a GUI. Use Firefox’s option object, not Chrome’s.
--window-size=WIDTH,HEIGHT Sets a deterministic viewport. It does not make a viewport screenshot full-page.
driver.save_screenshot(path) Writes the current window image as PNG and returns a Boolean. False indicates an output I/O failure.
driver.save_full_page_screenshot(path) Writes a full-document PNG where the Firefox driver supports it. Not a portable cross-browser API.
get_screenshot_as_png() Returns PNG bytes in memory. You must store or upload the bytes yourself.
get_screenshot_as_base64() Returns the image encoded as Base64. Encoding increases payload size; decode it at the receiver.

Wait for the page before capturing

Headless mode changes display, not page timing. A screenshot taken immediately after get can catch a loading shell, late fonts, or images that have not appeared. Wait for a meaningful condition rather than adding an arbitrary long sleep.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# after driver.get(...)
WebDriverWait(driver, 20).until(
    EC.visibility_of_element_located((By.CSS_SELECTOR, "main"))
)
driver.save_screenshot("ready.png")

For a known animation or delayed API response, a short explicit delay can be appropriate, but a selector or state condition is generally more reliable. If the page uses lazy-loaded images, scroll or trigger the page’s loading behavior before capture and verify that the relevant elements have dimensions.

Viewport, element, and full-page strategies

Viewport capture

save_screenshot captures the current browser viewport. Pick the dimensions that match the device or breakpoint you are testing, such as 390,844 for a mobile-sized layout or 1440,1000 for a desktop check. The resulting pixel dimensions can differ with device scale and browser configuration.

One element

Selenium can capture an element through its WebElement screenshot method:

hero = driver.find_element(By.CSS_SELECTOR, "header.hero")
hero.screenshot("hero.png")

The element must be present and rendered. Fixed overlays, transforms, and elements outside the viewport can produce surprising bounds; scroll the element into view and wait for visibility when necessary.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Full-page capture in Chrome

Chrome’s ordinary Selenium screenshot call is a viewport capture. A full-document result requires a browser-specific strategy, such as using the Chrome DevTools Protocol to determine the document dimensions, temporarily resizing the window, or stitching scroll segments. These approaches can be affected by fixed headers, sticky elements, lazy loading, animations, and very tall pages. If exact full-page output is a requirement, test the strategy against your site rather than assuming a single portable command.

Keeping headless Selenium reliable in CI

  • Close every session: put driver.quit() in finally. This prevents orphaned browser and driver processes after an assertion or screenshot error.
  • Use deterministic inputs: set viewport size, timezone, locale, user agent, and test data when visual comparisons need stable pixels.
  • Make output paths explicit: create an artifacts directory and use an absolute or workspace-relative path that the CI job preserves.
  • Record diagnostics: on failure, save the page source, browser console logs where available, and a screenshot attempt. A blank image can distinguish an application error from a capture error.
  • Account for containers: ensure the image contains the browser libraries and fonts your page needs. Resource-starved containers may terminate Chrome before a screenshot is returned.
  • Pin intentionally: browser, driver, and Selenium upgrades can alter rendering or headless behavior. Upgrade together and rerun visual baselines.

Common failures and fixes

A browser window still appears

Check that the argument is exactly on the options object supplied to the constructor. Use --headless=new for current Chromium and --headless for Firefox. Older convenience calls such as setHeadless(true) were deprecated in Selenium 4.8 and removed in 4.10; explicit browser arguments are the portable approach.

The image has the wrong dimensions

Set --window-size=WIDTH,HEIGHT before navigation and confirm that no later code changes the window size. Remember that CSS pixels, device scale, and browser defaults can make physical PNG dimensions differ from the nominal viewport.

The capture is incomplete or only shows the top

That is expected from save_screenshot: it captures the viewport. Use Firefox’s full-page method when Firefox is acceptable, or implement and test a Chrome-specific full-page approach. Wait for lazy content and disable or finish animations if they create inconsistent frames.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The file is missing

Verify the directory exists and is writable, use a valid filename, and check the Boolean returned by save_screenshot. In CI, inspect the job’s working directory and artifact-upload rules.

The page is blank, half-rendered, or still loading

Wait for a page-specific selector or state, verify that navigation reached the expected URL, and inspect application logs. A successful WebDriver command does not guarantee that asynchronous application work has completed.

The script hangs or leaves processes behind

Always call quit in a finally block. Add a test timeout around navigation and waits so a failed network request cannot hold the job indefinitely.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

When an API is simpler than managing a browser

If you only need an image or PDF from a URL, an API avoids installing browsers, drivers, fonts, and CI workarounds. ScreenshotNeo is a website screenshot API and MCP server. It is the first option to try when you want clean captures: it accepts cookie and consent banners before capture, removes more than 60 known consent platforms plus newsletter popups and chat widgets, and bills only clean shots. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; response headers identify the page verdict and billing result.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Or skip the browser setup

One GET request returns PNG, JPEG, WebP, or a PDF. The complete option list and authentication details are in the ScreenshotNeo documentation.

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)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo also supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper and page-range controls, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

Plan Included shots Price
Free 1,000 per month $0, no card
Starter 3,000 $5
Growth 15,000 $15
Pro 60,000 $39
Scale 250,000 $99
Business 1,000,000 $249

Yearly billing gives two months free, and every feature is available on every plan. Start with 1,000 free screenshots a month with no card; paid plans start at $5 for 3,000.

Choosing the right approach

  • Choose headless Selenium when the screenshot is part of an interaction-heavy test, you need WebDriver assertions, or the browser session already exists.
  • Choose Firefox when a documented full-page Selenium method is important and Firefox rendering matches your target.
  • Choose an API when you need repeatable URL-to-image or PDF jobs without maintaining browser binaries, and when consent cleanup, billing visibility, bulk jobs, or AI-agent access matter.

Frequently Asked Questions

Does headless mode use a different web engine?

Current Chromium headless shares code with headful Chrome, so the page is still rendered by the browser engine; only the GUI display is suppressed.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Can Selenium save JPEG or WebP directly?

The Selenium screenshot methods documented here write PNG data. Convert the bytes with an image library if another format is required.

Why does a screenshot pass locally but fail in CI?

CI may have different browser versions, fonts, viewport defaults, permissions, network access, or resource limits. Pin the environment, set an explicit viewport, and preserve diagnostic artifacts.

Is a full-page screenshot always one very tall image?

Not necessarily. Browser-specific implementations may resize, use a protocol command, or stitch segments, and fixed elements or lazy content can affect the result.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.