Selenium's save_screenshot() only captures the visible viewport, and in Chrome there's no built-in full-page option. Here is tested Selenium 4 code for every common case, the fixes for broken output, and when an HTTP API is simpler.
Install with pip install selenium. Selenium 4.6+ downloads a matching chromedriver by itself (Selenium Manager), so you don't need to install the driver.
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--window-size=1280,800")
driver = webdriver.Chrome(options=options)
driver.get("https://example.com")
driver.save_screenshot("viewport.png")
driver.quit()
This is the viewport only. Note that --window-size sets the window, so the image can come out shorter than 800px. The fix is below.
Chrome's WebDriver has no full-page method, but Selenium can talk to the DevTools protocol directly. Ask for the full content size and capture beyond the viewport:
import base64
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--window-size=1280,800") # headless defaults to 800x600
driver = webdriver.Chrome(options=options)
driver.get("https://en.wikipedia.org/wiki/Web_scraping")
size = driver.execute_cdp_cmd("Page.getLayoutMetrics", {})["cssContentSize"]
shot = driver.execute_cdp_cmd("Page.captureScreenshot", {
"format": "png",
"captureBeyondViewport": True,
"clip": {"x": 0, "y": 0, "width": size["width"], "height": size["height"], "scale": 1},
})
with open("full.png", "wb") as f:
f.write(base64.b64decode(shot["data"]))
driver.quit()
This works with Chrome and Edge (execute_cdp_cmd is Chromium-only). The old trick of resizing the window to document.body.scrollHeight also works, but it breaks pages that size sections to 100vh, which turn huge.
geckodriver supports full-page capture natively: driver.save_full_page_screenshot("full.png") on a webdriver.Firefox() driver. It's Firefox-only, which is why Chrome needs the CDP route above.
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
heading = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.CSS_SELECTOR, "h1")))
heading.screenshot("heading.png")
Wait for the element to be visible first. Calling find_element straight after get() races with client-side rendering on JavaScript-heavy sites.
import base64
from selenium.webdriver.common.print_page_options import PrintOptions
opts = PrintOptions()
opts.page_width, opts.page_height = 21.0, 29.7 # A4, in cm (the default is US Letter)
opts.margin_top = opts.margin_bottom = 1.0
opts.margin_left = opts.margin_right = 1.0
opts.background = True # keep background colours and images
pdf = driver.print_page(opts)
with open("page.pdf", "wb") as f:
f.write(base64.b64decode(pdf))
print_page() works in headless Chrome and Firefox and uses the page's print CSS (@media print).
--window-size includes browser UI, so the viewport is smaller. For an exact viewport, override it through CDP: driver.execute_cdp_cmd("Emulation.setDeviceMetricsOverride", {"width": 1280, "height": 800, "deviceScaleFactor": 1, "mobile": False})."deviceScaleFactor": 2, or launch Chrome with options.add_argument("--force-device-scale-factor=2").driver.execute_script("return (async () => { for (let y = 0; y < document.body.scrollHeight; y += 600) { window.scrollTo(0, y); await new Promise(r => setTimeout(r, 100)); } window.scrollTo(0, 0); })()")driver.execute_script("const s = document.createElement('style'); s.textContent = '#onetrust-banner-sdk, .cookie-banner { display: none !important }'; document.head.appendChild(s)").SessionNotCreatedException: Chrome instance exited in Docker or CI: add --no-sandbox and --disable-dev-shm-usage (only in containers you trust).apt install fonts-noto-cjk fonts-noto-color-emoji."format": "jpeg", "quality": 80 in the CDP call, or compress afterwards.If screenshots are a side feature, you may not want Chrome, a driver and fonts in your deployment. Shotmint runs the browser for you, scrolls for lazy images and handles full-page capture:
# shot.py, run with: SHOTMINT_KEY=... python3 shot.py
import os
import requests
r = requests.get("https://shotmint.creasework.com/v1/screenshot", params={
"url": "https://en.wikipedia.org/wiki/Web_scraping",
"full_page": "true",
"device_scale_factor": "2",
"format": "jpeg",
"quality": "80",
"block_ads": "true", # also hides common cookie banners
}, headers={"X-API-Key": os.environ["SHOTMINT_KEY"]}, timeout=90)
r.raise_for_status()
with open("full.jpg", "wb") as f:
f.write(r.content)
| Selenium | Shotmint |
|---|---|
save_screenshot() | GET /v1/screenshot?url=… |
CDP captureBeyondViewport | full_page=true (scrolls first, so lazy images load) |
element.screenshot() | selector=h1 |
setDeviceMetricsOverride | width, height, device_scale_factor, mobile=true |
Injected display: none CSS | hide_selectors=.banner, or block_ads=true |
print_page(PrintOptions()) | /v1/pdf with format=A4, print_background, margin |
Keep it for browser automation and testing: logging in, clicking through flows, filling forms, cross-browser checks. The API is for when you just need the picture or the PDF of a URL or some HTML. Other languages: Python screenshot API guide, Playwright screenshots. No code at all: the full-page screenshot tool.
No signup, no card. Get a key in one click, then buy credits only if you need more (from €5 for 2,000, never expire).
Get a free API key Full API reference