ScreenshotNeo

BlogHow-to

How to Take Full-Page Website Screenshots with Playwright Python

Capture an entire page with Playwright Python. Get runnable sync and async examples, rendering controls, pytest setup, and fixes for common screenshot problems.

By the ScreenshotNeo team4 October 20267 min read

Use full_page=True in Playwright Python’s page.screenshot() call. It captures the page’s full scrollable area, including content below the viewport. In synchronous code, call page.screenshot(path="screenshot.png", full_page=True); in asynchronous code, use await page.screenshot(path="screenshot.png", full_page=True). The viewport controls how the page is rendered; full_page=True controls how much of it is captured. See the official Playwright screenshot guide.

1. Install Playwright and capture a full page

Install the Python package and its browser binaries. This example uses Chromium, saves a PNG, and closes the browser even if navigation or capture raises an error.

python -m pip install playwright
python -m playwright install chromium
from playwright.sync_api import sync_playwright

url = "https://example.com"

with sync_playwright() as p:
    browser = p.chromium.launch()
    try:
        page = browser.new_page()
        page.goto(url, wait_until="load", timeout=60_000)
        page.screenshot(path="screenshot.png", full_page=True)
    finally:
        browser.close()

The key option is full_page=True. Without it, the screenshot covers only the visible viewport. The example uses wait_until="load" as a basic navigation condition; it does not guarantee that a site has finished fetching data or rendering content after load. For dynamic pages, wait for a condition that reflects the content you need, as described below.

2. Use the asynchronous Python API

Choose the async API when the surrounding application already uses asyncio, or when you need to coordinate browser work with other asynchronous tasks. Do not call the sync API from an active async event loop.

import asyncio
from playwright.async_api import async_playwright

async def main():
    url = "https://example.com"

    async with async_playwright() as p:
        browser = await p.chromium.launch()
        try:
            page = await browser.new_page()
            await page.goto(url, wait_until="load", timeout=60_000)
            await page.screenshot(path="screenshot.png", full_page=True)
        finally:
            await browser.close()

asyncio.run(main())

In a framework or notebook that already runs an event loop, call await main() instead of asyncio.run(main()). Every Playwright operation in the async API needs await.

3. Wait for the page content you need

A successful navigation does not necessarily mean a single-page application has finished rendering. Prefer an application-specific signal, such as a results container becoming visible, over an arbitrary delay. For an element that appears after data loads:

page.goto("https://example.com/catalog", wait_until="domcontentloaded")
page.locator(".catalog-results").wait_for(state="visible", timeout=20_000)
page.screenshot(path="catalog.png", full_page=True)

The equivalent async calls are:

await page.goto("https://example.com/catalog", wait_until="domcontentloaded")
await page.locator(".catalog-results").wait_for(state="visible", timeout=20_000)
await page.screenshot(path="catalog.png", full_page=True)

Choose navigation waits deliberately. domcontentloaded waits for initial document parsing; load waits for the page load event. Pages with analytics, polling, or persistent network connections may never become network-idle, so a selector or app-owned readiness condition is often more reliable than waiting for all network activity to stop.

4. Set viewport and device scale

The viewport affects responsive layout, line breaks, and which elements the site renders. Set it on the browser context before navigation for reproducible captures. Device scale factor emulates pixel density; it is independent of full-page capture. The Playwright emulation guide documents viewport and device scale settings.

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    context = browser.new_context(
        viewport={"width": 1280, "height": 900},
        device_scale_factor=2,
    )
    try:
        page = context.new_page()
        page.goto("https://example.com", wait_until="load")
        page.screenshot(path="desktop-retina.png", full_page=True)
    finally:
        context.close()
        browser.close()

Use a wider or narrower viewport when you need to capture a particular responsive layout. A larger device scale factor can increase image dimensions and memory use. For a known phone or tablet profile, Playwright’s device registry can provide device settings; see the emulation guide and verify the profile suits your target.

5. Save a file or work with screenshot bytes

Pass path to write the image directly. If you omit it, screenshot() returns image bytes that you can upload or process without an intermediate file.

from pathlib import Path
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    try:
        page = browser.new_page()
        page.goto("https://example.com", wait_until="load")
        image_bytes = page.screenshot(full_page=True)
        Path("screenshot.png").write_bytes(image_bytes)
    finally:
        browser.close()

The screenshot API also supports output type, scale, timeout, masking, and other capture controls. Consult the Page screenshot API reference for options supported by your installed Playwright version. For PNG, omit a lossy quality setting. Check the current reference before relying on formats or options that may vary by release.

6. Full page, viewport, or one element?

Capture How Use it for
Visible viewport page.screenshot(path="view.png") A screenshot of the currently visible browser area.
Full scrollable page page.screenshot(path="full.png", full_page=True) A tall capture of the page content beyond the fold.
One element page.locator("article").screenshot(path="article.png") A particular element, such as a chart or article.

An element screenshot is not the same as a full-page screenshot. Playwright scrolls the locator into view before capturing it; for a scrollable container, only the content visible in that container is captured. See the locator screenshot reference.

7. Capture full-page screenshots on pytest failures

If you use the Playwright pytest plugin, enable screenshots and the full-page flag. The plugin captures screenshots on failure with --full-page-screenshot; it requires --screenshot.

python -m pip install pytest-playwright
python -m playwright install chromium
pytest --screenshot only-on-failure --full-page-screenshot

To make the settings defaults, configure pytest.ini:

[pytest]
addopts = --screenshot only-on-failure --full-page-screenshot

The plugin’s pytest reference notes that command-line browser, context, and page options apply to its default fixtures; manually created browser contexts are configured separately.

8. Troubleshooting

Symptom Likely cause Fix
Only the visible screen is in the image The screenshot call omitted the full-page option. Pass full_page=True to page.screenshot().
Lower-page content is blank or missing Content is lazy-loaded or rendered after navigation. Wait for the relevant content or trigger the site’s intended scroll/loading behavior before capture; then capture again.
Navigation or screenshot times out The page is slow, the chosen navigation condition never completes, or the screenshot exceeds its timeout. Set an appropriate timeout and wait for a concrete selector or readiness signal. Avoid a network-idle wait on pages with ongoing requests.
Layout differs between runs Viewport, device scale, browser, locale, or page state differs. Set context options explicitly, use the same browser engine, and wait for the page state your capture requires.
The output file is missing The path is relative to a different working directory, or the operation failed before saving. Use an absolute path or inspect the process working directory; let screenshot exceptions surface and check them before assuming success.
Pytest failure image is not full-page The full-page option requires screenshot capture to be enabled. Pass both --screenshot only-on-failure and --full-page-screenshot.
Async code reports an event-loop error asyncio.run() was called while a loop is already running, or sync Playwright was used in async code. Use the async Playwright API and await the capture from the existing loop.

9. Performance, reliability, and cost

A full-page image can be much larger than a viewport screenshot because it includes the entire scrollable height. Large pages and high device scale factors can increase capture time, memory use, and output size. Capture only the pages you need, use a viewport and scale that meet the image’s purpose, and avoid retaining image bytes longer than necessary in bulk jobs.

For reliable automation, close contexts and browsers in cleanup blocks, set navigation and capture timeouts, and wait for a meaningful readiness condition. Sites can change content, block automated browsers, or show different pages based on cookies and location; a successful screenshot call does not prove that the intended content loaded. Playwright itself has no per-screenshot fee in the documented API, but running browsers consumes your own compute and storage. This article makes no speed or cost benchmark claim.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. Instead of installing and operating a browser, request a screenshot from its API. See the ScreenshotNeo API documentation for request options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
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)
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 bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed; responses include page-verdict and billing headers. Its MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up free for 1,000 screenshots a month, no card required.

FAQ

Does a full-page screenshot scroll the page?

It captures the full scrollable page as if displayed on a very tall screen. It is distinct from a screenshot limited to the current viewport.

Can I use full-page capture with Firefox or WebKit?

The Python API exposes screenshots on pages across Playwright’s supported browser engines. If browser-specific rendering matters, capture with the engine your users or tests target.

Can Playwright screenshot a PDF?

page.screenshot() creates an image. For a PDF document, use Playwright’s PDF capability where supported and consult its API documentation for engine and option details.