Recommended Free Tools
The shortest reliable way to save a Selenium screenshot as a PNG is driver.save_screenshot("path/to/file.png"). It captures the current browser window, writes PNG bytes to a writable path, and returns True when the write succeeds. Check that return value when a failed capture must stop your job.
Save the current browser window as a PNG
This complete example creates a destination directory, opens a page, saves the viewport screenshot, and treats a failed file write as an error:
from pathlib import Path
from selenium import webdriver
out = Path("screenshots")
out.mkdir(parents=True, exist_ok=True)
with webdriver.Chrome() as driver:
driver.get("https://example.com")
ok = driver.save_screenshot(str(out / "example.png"))
if not ok:
raise OSError("Selenium could not write the screenshot")
The path should be writable and should end in .png. Selenium’s Python API documents save_screenshot(filename) as a screenshot of the current window saved to a PNG file. The method returns False for an IOError instead of raising that write failure itself. Selenium’s implementation delegates to get_screenshot_as_file(), opens the destination in binary-write mode, writes the PNG bytes, and returns True on success. A filename without a .png suffix produces a warning; Selenium does not silently convert another extension.
Use an explicit absolute path when jobs run elsewhere
Relative paths are resolved from the process’s current working directory, which may differ in an IDE, test runner, Docker container, or CI worker. Convert a known directory to an absolute path if artifacts must be found consistently:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
#1 Best Overall
from pathlib import Path
output = (Path.cwd() / "artifacts" / "home.png").resolve()
output.parent.mkdir(parents=True, exist_ok=True)
assert output.suffix.lower() == ".png"
Do not pass a directory, a read-only location, or a path whose parent has not been created. On Windows, a raw string such as r"C:\work\shot.png" avoids accidental escape sequences.
save_screenshot versus get_screenshot_as_file
For ordinary file output, the two methods have the same documented file behavior:
| Method | Scope | Output | Failure contract | Best use |
|---|---|---|---|---|
save_screenshot(filename) |
Current browser window | PNG file | Returns False when Selenium encounters an IOError |
Shortest, clearest file-saving call |
get_screenshot_as_file(filename) |
Current browser window | PNG file | Same behavior; returns True or False |
Code that uses Selenium’s “get” naming consistently |
get_screenshot_as_png() |
Current browser window | PNG bytes in memory | Lets surrounding code handle exceptions and byte processing | Transforming, uploading, hashing, or choosing the destination later |
Because save_screenshot delegates to get_screenshot_as_file, switching between the first two does not change capture scope or file format. Whichever you use, check the boolean result if a missing screenshot would invalidate the run.
Capture PNG bytes before writing
When the image needs inspection or post-processing, request bytes first and write them yourself:
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #2
from pathlib import Path
from selenium import webdriver
path = Path("screenshots/example.png")
path.parent.mkdir(parents=True, exist_ok=True)
with webdriver.Chrome() as driver:
driver.get("https://example.com")
png_bytes = driver.get_screenshot_as_png()
if not png_bytes:
raise RuntimeError("Selenium returned an empty screenshot")
path.write_bytes(png_bytes)
get_screenshot_as_png() returns binary PNG data for the current window. This form is useful when you need to send the bytes to object storage, run image analysis, add metadata, or decide the filename from page content. Path.write_bytes raises an exception for a write failure, so handle that exception at the boundary appropriate for your application.
Save only one element
Use the WebElement screenshot API when the whole viewport would include unwanted navigation or surrounding content:
from pathlib import Path
from selenium import webdriver
path = Path("screenshots/submit-button.png")
path.parent.mkdir(parents=True, exist_ok=True)
with webdriver.Chrome() as driver:
driver.get("https://example.com/form")
button = driver.find_element("css selector", "button.submit")
if not button.screenshot(str(path)):
raise OSError("Could not write the element screenshot")
The element method captures the selected element rather than the entire browser window. If you need bytes instead of a file, use the screenshot_as_png property:
element_png = button.screenshot_as_png
Path("screenshots/submit-button.png").write_bytes(element_png)
Locate the element after the page has loaded and after any navigation or DOM update that could replace it. A stale element reference means you must find it again.
Rank #3
Capture a full page: browser-specific behavior
A normal WebDriver screenshot is a viewport capture; it is not automatically the complete, vertically scrollable document. Full-document capture is a separate capability. Firefox’s WebDriver API documents get_full_page_screenshot_as_file(path) and save_full_page_screenshot(path) for full-page PNG output. These are Firefox-specific documented options, not a universal cross-browser guarantee.
from pathlib import Path
from selenium import webdriver
path = Path("screenshots/full-page.png")
path.parent.mkdir(parents=True, exist_ok=True)
options = webdriver.FirefoxOptions()
with webdriver.Firefox(options=options) as driver:
driver.get("https://example.com")
ok = driver.save_full_page_screenshot(str(path))
if not ok:
raise OSError("Firefox could not write the full-page screenshot")
If your target browser does not expose a full-page WebDriver method, do not assume that a viewport screenshot contains content below the fold. A custom scroll-and-stitch workflow can introduce duplicated fixed headers, timing gaps, lazy-loaded images, and device-pixel-ratio seams. Treat it as a separate engineering task and validate the resulting image on the browsers you support.
Make captures deterministic
Wait for the page state you actually need
driver.get() returning does not guarantee that a client-rendered chart, image, or font is ready. Use an explicit Selenium wait for a meaningful condition before saving. For an element screenshot, wait for that element to exist and be visible; for a page screenshot, wait for a page-specific marker rather than using an arbitrary long sleep.
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
WebDriverWait(driver, 20).until(
EC.visibility_of_element_located((By.CSS_SELECTOR, "main"))
)
driver.save_screenshot("screenshots/ready.png")
Choose the viewport explicitly
Responsive layouts change with window size. Set it before navigation when the screenshot is a visual regression artifact:
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minuteRank #4
driver.set_window_size(1440, 900)
driver.get("https://example.com")
In headless environments, also configure the browser’s headless mode and window dimensions through the relevant browser options. Keep those settings fixed between runs so a changed breakpoint is not mistaken for a page change.
Control filenames and concurrency
Use unique names when parallel tests share a directory. Include a test name, viewport, and timestamp or run identifier, and write to separate per-worker directories when possible. Two workers writing the same PNG can leave a valid-looking but incomplete artifact.
Common errors and fixes
- The file is missing. Verify the parent directory exists, the process has write permission, and the path is the one used by the test runner. Check the method’s boolean return value.
- The file has the wrong extension. End the filename in
.png. Selenium warns for another suffix rather than converting it. - The screenshot is blank or incomplete. Wait for the application-specific ready condition, ensure the correct window or tab is active, and check that the page did not navigate immediately before capture.
- An element cannot be found. Confirm the selector, wait for the element, and switch into the correct iframe before locating content inside it.
StaleElementReferenceException. A framework re-rendered the DOM. Wait for the update to finish and locate the element again instead of reusing the old object.- Full-page methods are unavailable. The documented full-page calls are Firefox-specific. Use Firefox for that API or implement and test a browser-appropriate stitching strategy.
- Images or fonts are absent. Capture only after the relevant resources are loaded; lazy content may require scrolling or an application-level “loaded” signal before capture.
- CI cannot start the browser. Install a compatible browser and driver, configure the environment’s headless requirements, and preserve the WebDriver startup error rather than reporting it as a screenshot write failure.
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server when you need a URL image without maintaining Selenium, a browser binary, and a driver. It accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
One GET request returns PNG, JPEG, WebP, or a PDF. See the ScreenshotNeo API documentation for all parameters.
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(`Screenshot failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', bytes);
ScreenshotNeo also supports full-page captures with lazy images loaded, CSS-selector element captures, dark mode, device presets and custom viewports, retina scale, PDF controls, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
Best Value
| 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 provides two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to get 1,000 screenshots each month without a card.
FAQ
Does Selenium save PNG or JPEG by default?
The screenshot methods described here write PNG output. Use a separate image conversion step if another format is required.
Can I upload the PNG without creating a temporary file?
Yes. Use get_screenshot_as_png() or an element’s screenshot_as_png property and pass the returned bytes to your upload client.
Why does a full-page screenshot differ from a viewport screenshot?
They have different capture scopes: one is the visible window, while a full-page method renders the document beyond the current viewport and may be browser-specific.
Frequently Asked Questions
Does Selenium save PNG or JPEG by default?
The screenshot methods described here write PNG output. Use a separate image conversion step if another format is required.
Can I upload the PNG without creating a temporary file?
Yes. Use get_screenshot_as_png() or an element’s screenshot_as_png property and pass the returned bytes to your upload client.
Why does a full-page screenshot differ from a viewport screenshot?
They have different capture scopes: one is the visible window, while a full-page method renders the document beyond the current viewport and may be browser-specific.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Quick Recap
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.

