Capture a Website Screenshot in Python Using Selenium Grid
Use Selenium Grid’s Remote WebDriver to capture and save a website screenshot in Python, with setup steps, troubleshooting, and a simpler API option.
To capture a website screenshot in Python using Selenium Grid, connect a Selenium Remote WebDriver to your running Grid endpoint, open the page, and call driver.save_screenshot("screenshot.png"). The browser runs on a Grid machine; the screenshot file is saved by the Python client process, so its destination must be writable on the client.
Selenium describes Grid as routing WebDriver commands from a client to remote browser instances. That makes it useful when you need to run browser automation remotely, in parallel, or against different browser versions and platforms. See the official Selenium Grid documentation.
1. Start Selenium Grid and install Selenium
For a local learning setup, start Selenium Grid in Standalone mode. Selenium’s getting started guide uses http://localhost:4444 as the default endpoint. If you use a Hub and Node setup or a distributed deployment, use the URL of the Grid endpoint reachable from your Python process.
Install the Python client in your environment:
python -m pip install selenium
Grid must be running, reachable, and configured to provide a browser that matches the options you request. The Grid setup guide covers Standalone, Hub and Node, and distributed deployment: Getting started with Selenium Grid.
2. Capture a screenshot with Python
This complete example connects to Chrome on Grid, sets a predictable viewport, loads a page, saves a PNG on the Python client, checks whether saving succeeded, and closes the remote session even if navigation or capture fails.
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
GRID_URL = "http://localhost:4444"
TARGET_URL = "https://example.com"
OUTPUT_PATH = Path("screenshot.png")
options = Options()
# Add browser-specific arguments here if your Grid browser requires them.
driver = webdriver.Remote(
command_executor=GRID_URL,
options=options,
)
try:
driver.set_window_size(1440, 1000)
driver.get(TARGET_URL)
saved = driver.save_screenshot(str(OUTPUT_PATH))
if not saved:
raise OSError(f"Screenshot could not be saved to {OUTPUT_PATH.resolve()}")
print(f"Saved screenshot to {OUTPUT_PATH.resolve()}")
finally:
driver.quit()
The remote connection requires a Grid address and browser options. This example uses Chrome options; select the appropriate options class for the browser configured on your Grid. Selenium’s Remote WebDriver guide explains the remote address and options used to create a session.
Save screenshot bytes instead
Use get_screenshot_as_png() if you need the image bytes for an upload or further processing. The bytes are returned to the Python client, so the following writes them locally:
png_bytes = driver.get_screenshot_as_png()
OUTPUT_PATH.write_bytes(png_bytes)
The Python API also offers get_screenshot_as_base64() when an encoded string is more convenient. See the official Remote WebDriver Python API reference.
3. Set the browser viewport and wait for the page
save_screenshot() captures the current window. Set its size before capture when viewport dimensions matter. The standard screenshot API reference does not establish full-page capture behavior, so treat this method as a current-window screenshot rather than assuming it captures the entire document.
For pages that render content asynchronously, wait for a known element before capturing. A fixed delay can be useful for a small, known animation or transition, but waiting for a page-specific condition is usually more reliable.
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(TARGET_URL):
WebDriverWait(driver, 20).until(
EC.visibility_of_element_located((By.CSS_SELECTOR, "main"))
)
saved = driver.save_screenshot(str(OUTPUT_PATH))
Choose a selector that appears only when the page is ready for your capture. If the target site has no stable readiness marker, use a bounded delay as a fallback and account for the additional time in each run.
4. cURL, Python, and Node.js with ScreenshotNeo
If your requirement is to get an image for a URL rather than operate a remote browser session yourself, ScreenshotNeo provides a website screenshot API. One GET request returns an image or PDF. The API accepts an access key and URL; see the ScreenshotNeo API documentation for its request options.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
Python
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)
Node.js
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} ${res.statusText}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
5. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Could not connect or create a session | Grid is stopped, its endpoint is wrong, or the client cannot reach that address. | Confirm Grid is running and use the endpoint reachable from the Python process. For a local Standalone setup, check http://localhost:4444. |
| Session request is rejected or remains pending | Grid cannot match the requested browser options to an available browser instance. | Check which browser is configured on Grid and request matching options. Review Grid node and session availability. |
| Screenshot is missing but the session worked | The output path is relative to the Python process’s working directory, or that process lacks write permission. | Use an absolute path or inspect Path.cwd(); ensure the client process can write to the destination. Check the boolean result from save_screenshot(). |
| Screenshot is blank, incomplete, or shows a loading state | The capture happened before the relevant page content rendered, or the selected viewport does not show the desired area. | Set the viewport before capture and wait for a page-specific element to become visible. Confirm that the requested content is in the current window. |
| Navigation or capture hangs | The remote browser may be waiting on a slow page or unavailable site; the client and Grid also depend on network connectivity. | Set an appropriate page-load timeout, investigate reachability from the Grid browser, and ensure driver.quit() runs in a finally block. |
| Local screenshot path is confused with browser downloads | WebDriver screenshots and remote browser downloads are separate workflows. | For a screenshot, save through the screenshot API on the Python client. Grid managed-download configuration is not a prerequisite for this method; consult Selenium’s Remote WebDriver documentation for remote browser behavior. |
6. Reliability, performance, and cost considerations
- Always end sessions. Call
driver.quit()infinallyso a failed navigation or file write does not leave the remote browser session open. - Wait for the content you need. A condition-based wait reduces premature captures without forcing every run to sleep for a fixed duration.
- Control viewport size. Use the same dimensions for comparable captures. A screenshot represents the current window, not necessarily the full document.
- Plan for remote dependencies. The Python client must reach Grid, and the Grid browser must reach the target site. Failures in either connection can prevent capture.
- Account for Grid capacity. Grid is intended for routing remote browser work and supports parallel execution across browser versions or platforms. Available browser instances and deployment topology affect how much work can run concurrently.
- Budget operational effort. With self-hosted Grid, you operate the Grid deployment and browser capacity. Selenium’s documentation describes deployment modes but does not provide a cost benchmark for this workflow; infrastructure cost depends on your environment.
7. Or skip the browser setup
ScreenshotNeo captures a URL with one API request, without setting up a Selenium Grid session. For example, use the Python request above or the cURL and Node.js calls in the previous section. ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. An MCP server gives AI agents tools to take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. See ScreenshotNeo for the product and the API documentation for options.
Sign up free for 1,000 screenshots a month, with no card.
8. FAQ
Does Selenium Grid itself save the screenshot to my computer?
The remote browser produces the screenshot through WebDriver, and the Python client writes it to the path supplied to save_screenshot(). Make that path writable on the client machine.
Can I save a screenshot as JPEG with save_screenshot()?
The Selenium Python API documents save_screenshot() as saving a PNG. If you need another format, retrieve the PNG bytes and convert them with an image library, or use a screenshot service that supports the desired output format.
Is a screenshot the same as a downloaded file?
No. A screenshot is returned through WebDriver’s screenshot command. Browser downloads have separate remote filesystem considerations and Grid configuration.
What is the difference between Selenium Grid and ScreenshotNeo?
Grid routes Selenium WebDriver commands to remote browser instances that you configure and operate. ScreenshotNeo is a screenshot API and MCP server: send a URL to its API to receive an image or PDF, with clean-shot handling and billing rules described above.


