ScreenshotNeo

BlogHow-to

How to Capture a Website Screenshot with Playwright in Python

Capture viewport, full-page, or element screenshots with Playwright in Python. Set up browsers, choose output formats, and troubleshoot common failures.

By the ScreenshotNeo team4 October 20269 min read

To capture a website screenshot with Playwright in Python, install the package and its browser binaries, open a page, navigate to the target URL, and call page.screenshot(). The default captures the current viewport; pass full_page=True for the full scrollable page, or call screenshot() on a locator to capture one element. Playwright can save the result to a file or return image bytes for further processing. See the official Playwright screenshots guide.

1. Install Playwright and a browser

Install the Python package and download the browser binaries Playwright needs. The package and browsers are separate installations:

python -m pip install playwright
python -m playwright install chromium

The examples below use Chromium. To install all supported browsers instead, run python -m playwright install. In a CI environment that is missing operating-system libraries, install browser dependencies too:

python -m playwright install --with-deps chromium

Playwright browser binaries are tied to Playwright releases. After upgrading the package, run the install command again if the browser is missing or launch fails. See Getting started with the Playwright library and the browser installation guide.

2. Capture a viewport screenshot

This complete synchronous script opens a page, waits for navigation to finish, writes a PNG, and closes the browser even if an error occurs:

from playwright.sync_api import sync_playwright


def main():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        try:
            page = browser.new_page()
            response = page.goto(
                "https://example.com",
                wait_until="load",
                timeout=30_000,
            )
            if response is not None and response.status >= 400:
                raise RuntimeError(f"Page returned HTTP {response.status}")

            page.screenshot(path="screenshot.png")
        finally:
            browser.close()


if __name__ == "__main__":
    main()

Run it with python screenshot.py. Playwright launches headless by default. A new page uses a default viewport; set an explicit viewport when the output dimensions need to be predictable.

page = browser.new_page(viewport={"width": 1440, "height": 900})

page.goto() returns a response for HTTP navigation, or None for cases such as some non-HTTP URLs. An HTTP error response does not necessarily mean navigation itself raised an exception, so inspect the status if the script should reject error pages.

3. Choose what to capture

Capture the full scrollable page

Use full_page=True for a full-page image. The default is the current viewport. Full-page output can be very tall on long pages.

page.screenshot(path="full-page.png", full_page=True)

Capture one element

Use a locator’s screenshot() method to save a component such as a header, chart, or card:

page.locator(".header").screenshot(path="header.png")

The locator screenshot action scrolls the element into view and waits for actionability. If the element is covered by an overlay, hidden, or not rendered, the capture may fail or not show what you expect. For a scrollable element, the screenshot includes only the content currently scrolled into view, not the element’s entire scroll area. See the Locator API.

Return image bytes instead of writing a file

Omit path to get the screenshot as bytes. This is useful for uploading the image, passing it to an image library, or comparing pixels without first creating a file.

image_bytes = page.screenshot()

with open("screenshot.png", "wb") as image_file:
    image_file.write(image_bytes)

Use asyncio

For applications that already use asyncio, use Playwright’s asynchronous API and await each browser operation:

import asyncio
from playwright.async_api import async_playwright


async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        try:
            page = await browser.new_page(viewport={"width": 1440, "height": 900})
            await page.goto("https://example.com", wait_until="load", timeout=30_000)
            await page.screenshot(path="screenshot.png", full_page=True)
        finally:
            await browser.close()


if __name__ == "__main__":
    asyncio.run(main())

Use the synchronous API for a short standalone script. Use the asynchronous API when integrating with an existing event loop or coordinating multiple asynchronous operations.

4. Configure format, scale, and repeatability

Playwright’s screenshot options let you choose output type, full-page behavior, and scale. The API documents PNG and JPEG. WebP support is version-sensitive: Playwright Python release notes identify screenshot support in version 1.62, so check the installed version before depending on it. See the screenshot options and release notes.

# PNG (default when the path ends in .png)
page.screenshot(path="page.png", type="png")

# JPEG; quality applies to JPEG output
page.screenshot(path="page.jpg", type="jpeg", quality=85)

# WebP on a Playwright Python version that supports it
page.screenshot(path="page.webp", type="webp")

Use PNG when you need lossless output or crisp text. JPEG is a lossy format and accepts a quality value; it can be useful when smaller photographic images matter more than pixel-perfect edges. Confirm WebP availability for the Playwright version installed in your environment.

For a higher-density image, use the screenshot scale option. "css" captures at CSS pixel dimensions; "device" uses device pixels and can produce a larger image. The scale option is documented for page screenshots:

page.screenshot(path="page.png", scale="css")

To reduce animation-related variation in locator screenshots, disable animations. Playwright also supports a stylesheet option for hiding dynamic content. These controls can help with repeatable captures, but they do not guarantee identical rendering across all sites or runs.

page.locator(".chart").screenshot(
    path="chart.png",
    animations="disabled",
    style="*, *::before, *::after { animation: none !important; transition: none !important; }",
)

Use stylesheet overrides carefully: hiding or changing site content affects what appears in the image. For visual tests, keep the browser version, viewport, data, and page state consistent as well.

5. Wait for the page state you need

A navigation event completing does not guarantee that every image, font, animation, or client-side update has settled. Choose a navigation wait condition that fits the page, then wait for a meaningful selector when the screenshot depends on a particular component.

page.goto("https://example.com", wait_until="domcontentloaded")
page.locator("main article").wait_for(state="visible", timeout=10_000)
page.screenshot(path="article.png", full_page=True)

Common wait_until choices include:

  • "load": wait for the page load event; used in the basic example.
  • "domcontentloaded": wait for initial HTML parsing, which can be useful when the page continues loading other resources.
  • "networkidle": wait for network activity to become idle. Sites with polling, analytics, or persistent requests may never reach this state, so a selector or deliberate short delay can be more reliable for those pages.

If a page reveals content only after scrolling, interact with it before capture. For example, scroll to the bottom to trigger lazy loading, then capture the full page:

page.goto("https://example.com", wait_until="load")
page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
page.locator("footer").wait_for(state="visible")
page.screenshot(path="full-page.png", full_page=True)

The wait condition and selector must match the site. A selector that appears before its content is ready may still require an additional application-specific readiness check.

6. cURL, Python requests, and Node.js alternatives

These are alternatives for obtaining a screenshot from the ScreenshotNeo API instead of managing a local Playwright browser. The Python Playwright examples above run the browser on your machine or in your environment; the API accepts one GET request with a URL and returns an image or PDF. Read the ScreenshotNeo API documentation for request options and response details.

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}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request can return a PNG, JPEG, WebP, or PDF. Its capture flow accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each of those steps can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses include X-Page-Verdict and X-Billed headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

There are 1,000 screenshots per month on the free plan with no card required. Paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month.

7. Troubleshooting

Problem Likely cause Fix
Browser launch says the executable is missing The browser binary for this Playwright package and engine is not installed. Run python -m playwright install chromium, or install all browsers with python -m playwright install.
Browser fails to launch in CI with missing shared libraries The environment lacks operating-system browser dependencies. Install them with python -m playwright install --with-deps chromium where supported, or use python -m playwright install-deps.
Launch broke after upgrading Playwright The installed browser version may not match the package release. Rerun the browser install command after upgrading. Playwright expects browser versions associated with its release.
Navigation times out The site is slow, the chosen wait condition never occurs, or a request is stuck. Set an appropriate timeout and try domcontentloaded or load instead of waiting for network idle. Then wait for the specific content needed for the screenshot.
Screenshot has missing images or incomplete content Resources or lazy-loaded content have not appeared by capture time. Wait for a relevant image or content selector. For lazy content, scroll the page to trigger loading before taking a full-page screenshot.
Element screenshot fails or omits the expected component The element may be hidden, covered, detached, or inside a scroll container. Wait for the locator to be visible, dismiss or account for overlays, and scroll the relevant container. Locator screenshots capture only the currently scrolled portion of a scrollable element.
WebP screenshot type is rejected The installed Playwright Python version may not support WebP screenshots. Check the installed version and its release notes; use PNG or JPEG if WebP is unavailable.
Output is unexpectedly huge or tall A full-page screenshot can include a long scrollable document, and device scale can increase pixel dimensions. Use a viewport screenshot, reduce the viewport or selected region, or choose CSS-pixel scale where appropriate.

Browser downloads can occupy hundreds of megabytes across installed engines. If disk space or download time matters, install only the browser engine your script uses. The official browser guide covers browser installation and dependencies.

8. Performance, reliability, and cost

  • Keep a browser open for batches. For multiple pages in one process, reuse the browser and create pages or browser contexts as needed instead of repeatedly downloading or launching an engine. Close pages and browsers when finished.
  • Capture only what you need. Viewport captures use less output space than very tall full-page images. Element captures can keep files focused on one component.
  • Set explicit timeouts and check results. A navigation timeout, an HTTP error response, and a successful page render are different outcomes. Handle each deliberately, and close the browser in a finally block or context manager.
  • Make visual output repeatable. Keep viewport and browser versions consistent, wait for content selectors, and disable animations when suitable. Live data, fonts, remote assets, and site changes can still alter screenshots.
  • Account for local runtime costs. A local Playwright script requires Python, browser binaries, and—in some environments—system dependencies. Browser binaries take disk space and captures consume CPU and memory; long full-page pages can increase those demands.
  • Use an API when you do not want to manage browsers. ScreenshotNeo’s plans include 1,000 free shots per month without a card, then paid tiers from $5 for 3,000. Its billing rules exclude bot checks, blank pages, timeouts, failed loads, and cache hits.

9. FAQ

Does Playwright run the browser visibly?

By default, Playwright launches headless browsers, so the browser window does not appear on screen.

Can I use the screenshot bytes without saving a file?

Yes. Call page.screenshot() without a path; it returns image bytes.

Does a full-page screenshot capture every part of a scrollable component?

No. full_page=True applies to the page screenshot. A locator screenshot of a scrollable element captures only its currently scrolled content.

Which output format should I choose?

Choose PNG for lossless output and clear text, JPEG when lossy compression is acceptable, and WebP only after confirming support in your installed Playwright version.