Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
The most dependable way to screenshot a URL in Python is to open it in a real browser with Playwright, wait for the page to load, and call page.screenshot(). This captures website content—not the browser window or address bar. Use full_page=True for the complete scrollable page, a locator for one element, and omit path when you need image bytes in memory.
Install Playwright and a browser
Playwright provides synchronous and asynchronous Python APIs. The synchronous API is simplest for a script; use the asynchronous API when the rest of your application already uses asyncio.
- Install the Python package:
python -m pip install playwright. - Install the browser binaries:
python -m playwright install chromium. - Save one of the scripts below and run it with Python.
Playwright release-specific behavior can change, so check the version installed in your environment when you need a feature such as WebP output. The examples below use the current documented API shape.
Minimal synchronous example
This complete script opens a URL and writes a PNG file:
#1 Best Overall
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com")
page.screenshot(path="screenshot.png")
browser.close()
The destination extension determines the image format in the normal case. A filename ending in .png, .jpg or .webp produces that format; you can also set the screenshot type explicitly. The browser closes even when the script completes normally, preventing stray Chromium processes.
Choose what the screenshot contains
Visible viewport
page.screenshot(path="screenshot.png") captures the currently visible page viewport. It does not include the browser’s tabs, address bar, bookmarks, or operating-system chrome.
Full scrollable page
Set full_page=True to capture the page’s scrollable website content as one image:
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 →from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 1440, "height": 900})
page.goto("https://example.com", wait_until="networkidle")
page.screenshot(path="full-page.png", full_page=True)
browser.close()
A full-page shot is still a page capture. It is not a picture of a desktop window and cannot show the URL pane. Very long pages can create large image files and may include content that appears only after scrolling.
One element
Use a locator when you need a component such as a header, chart, or article:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com")
page.locator(".header").screenshot(path="header.png")
browser.close()
The matching element must be visible. If another element covers it, the covered pixels will not appear as expected. A scrollable element shows only the content in its currently visible scroll position; use a page-level full capture if you need all of the page instead.
Rank #2
Image bytes instead of a file
Omit path and Playwright returns the encoded screenshot bytes. This is useful for HTTP responses, object storage, hashing, or visual comparison:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com")
image_bytes = page.screenshot(type="png")
with open("screenshot.png", "wb") as output:
output.write(image_bytes)
browser.close()
Wait for the page you actually want to capture
Navigation finishing does not always mean that a single-page application, image gallery, or chart has rendered. Choose a wait condition that matches the page:
page.goto(url)waits for the default navigation completion.page.goto(url, wait_until="networkidle")waits for a period with no network connections, which can be useful for mostly static pages but may never settle on sites with continuous polling.page.wait_for_selector(".report")waits for a required element.page.wait_for_timeout(2000)adds a fixed delay when a known animation or delayed widget needs time; a selector-based wait is usually more robust.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com/dashboard", wait_until="domcontentloaded")
page.wait_for_selector("main.dashboard")
page.screenshot(path="dashboard.png", full_page=True)
browser.close()
The documented screenshot timeout default is 30 seconds. Set a larger timeout for slow pages or a shorter one for a latency-sensitive job:
page.screenshot(path="shot.png", timeout=60_000)
Control format, quality, scale, and repeatability
PNG, JPEG, and WebP
PNG is lossless and has no quality setting. JPEG and WebP support a quality value, useful when storage or transfer size matters:
page.screenshot(path="preview.webp", type="webp", quality=80)
Do not pass quality for PNG. If a downstream system requires a particular format, set type explicitly rather than relying on a filename.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, 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 minuteCSS pixels and high-density output
Browser context settings control the relationship between CSS pixels and device pixels. A regular scale keeps files smaller; a device-pixel scale produces a denser image for retina-style displays. Configure the context before creating the page:
Rank #3
context = browser.new_context(
viewport={"width": 1280, "height": 800},
device_scale_factor=2
)
page = context.new_page()
Use a larger viewport when the target layout has desktop breakpoints. For mobile captures, select a narrow viewport and a mobile device context rather than resizing the output image afterward.
Mask dynamic content
For repeatable visual tests, mask changing regions and apply a screenshot stylesheet. Masking is useful for timestamps, ads, rotating avatars, and other content that should not affect comparisons. Keep selectors specific so you do not hide content you need to inspect.
page.screenshot(
path="stable.png",
mask=[page.locator(".timestamp"), page.locator(".live-counter")],
style=".cursor, .animation { visibility: hidden !important; }"
)
Use the asynchronous Python API
In an async web service or worker, use async_playwright and await navigation and capture calls:
import asyncio
from playwright.async_api import async_playwright
async def capture(url: str, filename: str) -> None:
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto(url, wait_until="networkidle")
await page.screenshot(path=filename, full_page=True)
await browser.close()
asyncio.run(capture("https://example.com", "example.png"))
Do not call the synchronous API from inside an already-running event loop. Pick one API style for the surrounding program.
Passing headers, cookies, and authentication
Many URLs look public but render different content for a logged-in user, locale, or device. Create a browser context with the required headers or cookies before opening the page. Treat credentials as secrets and never hard-code them in a committed script.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
context = browser.new_context(
extra_http_headers={"Authorization": "Bearer YOUR_TOKEN"},
locale="en-US",
timezone_id="UTC"
)
page = context.new_page()
page.goto("https://example.com/private")
page.screenshot(path="private.png")
browser.close()
For cookie-based sessions, provide a cookie list through the context or load a previously saved browser storage state. Verify that using those credentials is permitted by the site and your organization’s policy.
Rank #4
Browser screenshot versus desktop-window screenshot
A Playwright page screenshot contains the rendered website. It cannot include the browser address bar, tabs, or other application chrome. If the requirement is literally a picture of the browser window—including the URL pane—you need an operating-system or desktop-capture tool instead. That is a different task from taking a screenshot of a URL’s page.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Automated capture with Selenium
Selenium’s Python bindings also provide current-window screenshot methods, screenshot bytes, and full-document capture methods in versions that support them. Because the surfaced Selenium reference uses older release labeling, confirm the exact method names against the Selenium version installed in your project before relying on a full-document call. For a new URL-capture script, Playwright’s current walkthrough offers the more direct viewport, full-page, and locator examples.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF, so you do not install Chromium or maintain browser automation code. Cookie and consent banners are accepted and 60-plus known consent platforms, newsletter popups, and chat widgets are removed before capture; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and whether it was billed.
Use the API documentation at https://screenshotneo.com/docs/ for the complete option set, including full-page lazy-image loading, CSS-selector element capture, dark mode, 12 device presets or a custom viewport, retina scale, PDF paper and margin controls, custom CSS and JavaScript, pre-capture clicks, selector hiding, selector/delay/network-idle waits, request and resource blocking, headers, cookies, user agents, Authorization, timezone, geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, usage reporting, and the OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
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)
Node.js
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’s MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients, allowing an AI agent to request captures directly. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is included on every plan. Create a free ScreenshotNeo account to get started.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Troubleshooting common failures
“Executable doesn’t exist”
Install the browser binaries with python -m playwright install chromium. Installing the Python package alone does not download Chromium.
The screenshot is blank or incomplete
Wait for a meaningful selector, use an appropriate wait_until value, and check that the URL did not redirect to a login or bot-check page. For lazy-loaded content, scroll or use a full-page capture after the page has rendered.
Timeout during navigation or capture
The site may be slow, blocked, or continuously making requests. Replace an unsuitable networkidle wait with domcontentloaded plus wait_for_selector, and increase the screenshot timeout when the page genuinely needs more time.
The element screenshot fails
Confirm that the locator matches exactly one visible element, wait for it to appear, and check whether a modal or sticky layer covers it. A scrollable element’s screenshot does not automatically include all of its hidden scroll area.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsFonts or images differ from a normal visit
Set the intended viewport, device scale, locale, timezone, and authentication state. Wait for the relevant image or component rather than assuming navigation completion means every asset is ready.
The file is unexpectedly large
Use JPEG or WebP with an appropriate quality value, reduce the viewport or device scale factor, and avoid full-page capture when a viewport or element image is sufficient.
Practical decision guide
| Need | Use |
|---|---|
| Simple script and local PNG | Playwright synchronous API and page.screenshot(path=...) |
| Async application | async_playwright with awaited navigation and capture |
| Entire webpage | full_page=True, after a reliable render wait |
| One component | page.locator(selector).screenshot() |
| Image processing in memory | Omit path and consume returned bytes |
| Address bar or browser chrome | Desktop or browser-window capture, not a page screenshot |
| Managed API, cleanup, PDFs, or AI-agent access | ScreenshotNeo |
FAQ
Can Python screenshot a URL without opening a visible browser window?
Yes. Playwright launches Chromium headlessly by default, so the page is rendered without displaying a browser window. The output still contains website content only.
Can I capture a PDF instead of an image?
Playwright’s screenshot API creates images. Use a PDF-specific browser workflow or ScreenshotNeo’s capture_pdf capability when the required output is a PDF.
Why does a full-page image not match a manually scrolled desktop screenshot?
A full-page capture stitches the page’s scrollable content using browser rendering. A manual desktop screenshot includes only the window area visible at each moment and may include browser chrome.
Frequently Asked Questions
Can Python screenshot a URL without opening a visible browser window?
Yes. Playwright launches Chromium headlessly by default, so the page is rendered without displaying a browser window. The output still contains website content only.
Can I capture a PDF instead of an image?
Playwright’s screenshot API creates images. Use a PDF-specific browser workflow or ScreenshotNeo’s capture_pdf capability when the required output is a PDF.
Why does a full-page image not match a manually scrolled desktop screenshot?
A full-page capture stitches the page’s scrollable content using browser rendering. A manual desktop screenshot includes only the window area visible at each moment and may include browser chrome.
Free tools Windows power users keep installed
One-click scans. No signup required.
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.

