Recommended Free Tools
Give Selenium an explicit, resolved filename, create its parent directory first, and check the Boolean result. In Python, driver.save_screenshot() and driver.get_screenshot_as_file() write the current browser window to the exact .png path you provide; Selenium does not select a hidden screenshot folder for you.
The reliable pattern: resolve, create, save, verify
A screenshot path should be based on a directory you control, not on whatever directory happens to be current when the test starts. This matters because an IDE, a shell, a test runner and CI can each choose a different working directory.
from pathlib import Path
from selenium import webdriver
screenshot_dir = Path(__file__).resolve().parent / "artifacts" / "screenshots"
screenshot_dir.mkdir(parents=True, exist_ok=True)
output_file = screenshot_dir / "login-page.png"
driver = webdriver.Chrome()
try:
driver.get("https://example.com")
ok = driver.save_screenshot(str(output_file))
if not ok:
raise OSError(f"Selenium could not write screenshot: {output_file}")
print(f"Saved screenshot to {output_file}")
finally:
driver.quit()
This example does four important things:
- Builds an absolute path:
Path(__file__).resolve()anchors the destination to the test file’s location rather than the process working directory. - Creates missing folders:
mkdir(parents=True, exist_ok=True)creates bothartifactsandscreenshotswhen necessary. - Uses a PNG filename: Selenium’s Python API documents PNG output and expects the filename to end in
.png. - Checks the result:
save_screenshot()returnsTrueafter a successful write andFalsewhen an I/O error occurs.
If the browser setup itself fails, driver = webdriver.Chrome() raises before the save call. The Boolean check covers the later filesystem write, so a test cannot quietly pass while its artifact is missing.
How Selenium chooses the destination
The filename argument is the destination
The Python WebDriver method receives a filename and opens that exact filename for binary writing. There is no Selenium-managed “screenshots” directory to configure. The caller owns the path. A relative filename such as screenshots/home.png is interpreted relative to the process’s current working directory, not relative to the Python file containing the test.
#1 Best Overall
For predictable local and CI output, pass a full path. You can print Path.cwd() while diagnosing a relative-path problem, but resolving the project or test-artifact directory up front is safer than relying on it.
Parent directories are not created for you
Opening artifacts/screenshots/login-page.png fails when either parent directory is absent. Create the complete directory tree before calling WebDriver. exist_ok=True also makes repeated test runs safe when the folders already exist.
The extension should be .png
The API describes this operation as saving the current window to a PNG image file and warns when a supplied name does not end in .png. The extension does not convert the image to another format. If you need JPEG or WebP, save the PNG and convert it in a separate image-processing step.
Choosing a path that works on every machine
Anchor paths to the test file
Path(__file__).resolve().parent is useful for a small project whose artifacts should sit beside the tests. A repository-level layout might instead derive a known project root and append a standard artifact directory:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
from pathlib import Path
project_root = Path(__file__).resolve().parents[1]
output_file = project_root / "test-artifacts" / "screenshots" / "checkout.png"
output_file.parent.mkdir(parents=True, exist_ok=True)
Choose one convention and use it everywhere. Mixing relative paths with file-relative paths is a common reason screenshots appear in an unexpected directory.
Rank #2
Use the runner’s artifact directory in CI
Continuous-integration systems often collect a particular directory after a job. Point output_file at that directory (or copy the file there after capture) so the image survives workspace cleanup. The exact environment variable differs by runner, so read the runner’s documentation and convert its value to a Path before appending your screenshot filename.
Prevent accidental overwrites
Writing the same path again targets that path again. That is useful when you want one current image, but it destroys the previous image when retaining every failure artifact. Include a test name, case identifier or timestamp in the filename. Sanitize names supplied by tests so they cannot introduce path separators.
from datetime import datetime, timezone
stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
filename = f"login-failure-{stamp}.png"
output_file = screenshot_dir / filename
Two equivalent file-saving methods
| Method | Output | Use it when | Failure signal |
|---|---|---|---|
driver.save_screenshot(filename) |
Writes PNG to the supplied path | You want the concise, commonly used call | Returns True or False |
driver.get_screenshot_as_file(filename) |
Writes PNG to the supplied path | You prefer the explicitly named “get … as file” API | Returns True or False |
Both calls represent the same file-oriented workflow: create the directory, pass a full .png path and test the Boolean. The second form is equivalent in the documented Python API.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →When you should keep the screenshot in memory
File output is not required if your application uploads the image, embeds it in a report or chooses the storage location itself. Selenium also exposes byte and Base64 representations:
png_bytes = driver.get_screenshot_as_png()
with open(output_file, "wb") as image_file:
image_file.write(png_bytes)
base64_image = driver.get_screenshot_as_base64()
get_screenshot_as_png()gives you PNG bytes for an object store, HTTP upload or custom writer.get_screenshot_as_base64()gives a Base64 string, which is convenient when an HTML report expects an embedded image.- When you write the bytes yourself, your code—not Selenium—controls directory creation and error handling. Catch or propagate the file exception appropriate to your test framework.
Diagnosing “wrong folder” and missing-file problems
My screenshot is in a different directory
Cause: the path was relative, so it followed the process working directory. Fix: print the resolved destination and pass an absolute path:
print(output_file.resolve())
ok = driver.save_screenshot(str(output_file.resolve()))
Do not assume the directory containing the test is the working directory; those are separate concepts.
save_screenshot returns False
Cause: Selenium encountered an I/O error while opening or writing the requested filename. Typical causes include a missing parent directory, an unwritable location, a read-only CI workspace or an invalid path. Create the parent directory, select a writable artifact directory and raise an error that includes the resolved path. The method’s documented contract is to return False for an I/O failure rather than raising that failure itself.
The directory exists, but the file still is not there
Check that you are looking at the same absolute path your process used. Log output_file.resolve(), verify the Boolean, and make sure cleanup code did not remove the artifact after the test. In parallel tests, also check that another case did not reuse the same filename.
The filename has the wrong extension
Use a name ending exactly in .png. A different suffix does not request a different image format; it conflicts with the API’s PNG contract and can trigger a warning.
The browser closes before the image is written
Keep the save call inside the try block and call driver.quit() in finally, as in the example. If navigation or an assertion fails, capture the screenshot in an exception handler before quitting:
try:
driver.get("https://example.com")
# test steps and assertions here
except Exception:
failure_file = screenshot_dir / "unexpected-failure.png"
if not driver.save_screenshot(str(failure_file)):
print(f"Could not save failure screenshot: {failure_file.resolve()}")
raise
finally:
driver.quit()
This pattern preserves the original exception while making a failed artifact write visible.
Designing a maintainable screenshot helper
Centralize path construction and result checking so individual tests only provide a logical name. A helper also gives you one place to enforce filename sanitization, timestamps and artifact retention.
from pathlib import Path
import re
from selenium.webdriver.remote.webdriver import WebDriver
def save_png(driver: WebDriver, root: Path, name: str) -> Path:
safe_name = re.sub(r"[^A-Za-z0-9._-]+", "_", name).strip(".")
if not safe_name:
safe_name = "screenshot"
if not safe_name.lower().endswith(".png"):
safe_name += ".png"
root.mkdir(parents=True, exist_ok=True)
destination = (root / safe_name).resolve()
if destination.parent != root.resolve():
raise ValueError("Screenshot name resolved outside the artifact directory")
if not driver.save_screenshot(str(destination)):
raise OSError(f"Selenium could not write screenshot: {destination}")
return destination
# Example:
# path = save_png(driver, Path("test-artifacts/screenshots"), "cart-checkout")
# print(path)
Keep the helper’s root directory stable for the test run, and let the test framework publish that directory as an artifact. Returning the path makes it easy to include a link in logs or reports.
Performance, reliability and storage considerations
- Capture only when useful: screenshots add disk writes and storage. Capturing on assertion failures usually gives more diagnostic value than capturing every step.
- Use unique names in parallel runs: include a worker or test-case identifier to avoid races and overwrites.
- Prefer bytes for custom pipelines:
get_screenshot_as_png()avoids a temporary file when the next operation is an upload or report embedding. - Check every write: a missing screenshot should fail the artifact step loudly, even if the browser test result is otherwise successful.
- Keep artifacts bounded: configure your CI retention policy and remove stale local runs so a long test history does not fill the workspace.
The screenshot call captures the current browser window. Navigate, wait for the state you want to document, then save; a path fix cannot correct a page captured before it finished rendering.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
When you need a URL image rather than a browser test artifact, ScreenshotNeo provides a single HTTP request. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets. 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. Its MCP server lets Claude, Cursor and other MCP clients call take_screenshot, get_page_info and capture_pdf.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallSee the ScreenshotNeo API documentation for parameters and response details. A direct call looks like this:
Best Value
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 includes full-page captures with lazy images loaded, CSS-selector element captures, dark mode, device presets or custom viewports, retina scale, PDF controls, custom CSS and JavaScript, click-before-capture, selector hiding, selector/delay/network-idle waits, request and resource blocking, custom 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. Parameter names used by other screenshot APIs also work, which can simplify migration.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to try it without adding a card.
FAQ
Can Selenium save a screenshot as JPEG directly?
The documented Python file methods save PNG images. Save the PNG first, then convert it with an image library if a JPEG deliverable is required.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Should I use an absolute path in every test?
For reproducible local and CI artifacts, yes. A relative path is valid, but its meaning depends on the process working directory, which can change between launchers.
Is a successful browser test proof that the screenshot exists?
No. Treat the Boolean returned by the file-saving method as a separate check and fail or log clearly when it is False.
Frequently Asked Questions
Can Selenium save a screenshot as JPEG directly?
The documented Python file methods save PNG images. Save the PNG first, then convert it with an image library if a JPEG deliverable is required.
Should I use an absolute path in every test?
For reproducible local and CI artifacts, yes. A relative path depends on the process working directory, which can change between launchers.
Is a successful browser test proof that the screenshot exists?
No. Check the Boolean returned by the file-saving method and report a failure when it is false.
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.

