ScreenshotNeo

BlogHow-to

How to Screenshot a Website From the Command Line or With Python

Capture website screenshots with Chrome Headless, Playwright CLI, or Python, including full-page, element, dynamic-content, and API workflows.

By the ScreenshotNeo team1 October 20268 min read

Short answer: use Chrome Headless for a one-off command, Playwright CLI for repeatable shell automation, or Playwright Python when you need waits, loops, selectors, authentication, or post-processing. All three render JavaScript in a headless browser, so they can capture modern sites without opening a visible window.

For a single page, run:

chrome --headless --screenshot --window-size=1440,900 https://example.com

For a reusable Python program:

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")
    page.screenshot(path="screenshot.png")
    browser.close()

This guide covers setup, full-page and element captures, JavaScript-heavy pages, output formats, waits, failures, performance, and an API option when you do not want to maintain a browser runtime.

1. Choose the right approach

Approach Best for Useful controls
Chrome Headless One-off shell captures Viewport and timeout
Playwright CLI Repeatable shell workflows Filenames, full page, element targets, PNG/JPEG/WebP, high resolution
Playwright Python Applications and batch jobs Selectors, waits, loops, browser context settings, buffers, post-processing
ScreenshotNeo API Managed capture from any HTTP client Clean-page processing, caching, devices, PDFs, async jobs, bulk capture

Use the official documentation for version-specific behavior: Chrome Headless flags, Playwright CLI, screenshot command options, and Playwright Python screenshots.

2. Capture a website with Chrome Headless

Chrome’s --screenshot flag writes screenshot.png to the current directory. Add --window-size=WIDTH,HEIGHT to control the viewport:

chrome --headless --screenshot --window-size=1440,900 https://example.com

Set a longer capture wait for pages that load content after navigation:

chrome --headless --screenshot --window-size=1440,900 --timeout=10000 https://example.com

The exact executable may be named google-chrome, chromium, or chromium-browser on your system. Check the installed version when a flag behaves differently. Chrome’s documentation defines --timeout as the wait before capture; it does not guarantee that every asynchronous request has finished.

Full-page limitations

The direct Chrome flag is suited to a viewport capture. If you need a full scrollable page, an element, or image-format controls, use Playwright or an API designed for those options.

3. Use Playwright CLI for shell automation

Playwright CLI runs headless by default. Open a page, then save the current page:

playwright-cli open https://example.com
playwright-cli screenshot --filename=example.png

Capture the entire scrollable page:

playwright-cli open https://example.com
playwright-cli screenshot --full-page --filename=example-full.png

Select an output format and high-resolution capture where supported by your installed version:

playwright-cli screenshot --filename=example.webp --type=webp
playwright-cli screenshot --filename=example.jpeg --type=jpeg
playwright-cli screenshot --hires --filename=example-hires.png

The CLI also supports targeted screenshots using an element reference or selector. Use the command reference linked above for the syntax exposed by your installed release, because command names and flags can change between versions.

When CLI automation is a good fit

  • Scheduled shell jobs that only need navigation and capture.
  • Named output files for build artifacts.
  • Full-page screenshots without writing application code.
  • PNG, JPEG, or WebP output and high-resolution captures.

4. Capture screenshots with Python and Playwright

Install the package and browser

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

Playwright supplies its own browser builds. It also documents using branded Chrome or Edge channels and headless-shell installation; see the browser documentation when your deployment requires a particular channel.

Viewport screenshot

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="load")
    page.screenshot(path="screenshot.png")
    browser.close()

Full-page screenshot

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()

full_page=True captures the page’s full scrollable height. Very long pages can create large images and consume significant memory.

Capture one element

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()

Save bytes instead of a file

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 dynamic content

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", wait_until="domcontentloaded")
    page.locator("main").wait_for(state="visible")
    page.wait_for_timeout(1000)
    page.screenshot(path="after-render.png", full_page=True)
    browser.close()

Prefer a meaningful selector or application-ready condition over an arbitrary delay. Use a delay only when the page has no reliable readiness signal. networkidle can be unsuitable for pages with analytics, polling, or other connections that never become idle.

Asynchronous Python

import asyncio
from playwright.async_api import async_playwright

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

asyncio.run(main())

5. Important capture options

Need Playwright pattern Practical note
Viewport new_page(viewport={"width": 1440, "height": 900}) Fix dimensions for reproducible output.
Full page page.screenshot(full_page=True) May be expensive for very tall documents.
Element page.locator(".card").screenshot(...) Wait for the element to be visible first.
Format type="png", "jpeg", or "webp" where supported JPEG is smaller but loses transparency and quality; PNG preserves detail.
High resolution CLI --hires Increases pixel dimensions and file size.

For authenticated pages, create a browser context with the required cookies or storage state. For responsive testing, run the same capture at several viewport sizes. For a page that changes by locale, configure the context’s locale, timezone, and permissions before navigation.

6. JavaScript-heavy pages and lazy content

  1. Navigate with wait_until="domcontentloaded" or "load".
  2. Wait for a selector that proves the application rendered.
  3. Scroll or otherwise trigger lazy loading before a full-page capture.
  4. Capture only after fonts, images, and client-side data are ready.
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="domcontentloaded")
    page.locator("article").wait_for(state="visible")
    page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
    page.wait_for_timeout(500)
    page.screenshot(path="rendered.png", full_page=True)
    browser.close()

Do not assume a fixed sleep works for every page. A selector, a known API response, or an application-ready marker is more reliable.

7. Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for the complete option list.

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)
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 can capture full pages with lazy images loaded, a CSS-selected element, dark mode, device presets or custom viewports, retina scale, PDFs, custom CSS and JavaScript, clicks, selector or network-idle waits, blocked ads and trackers, custom headers and cookies, timezone and geolocation, transparent backgrounds, resizing, chosen TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage data, and an OpenAPI specification. Parameter names used by other screenshot APIs also work to ease migration.

Before capture, it accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers identify the page verdict and whether the request was billed.

An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. Plans include 1,000 shots per month free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

8. Troubleshooting

Symptom Likely cause Fix
Command not found Chrome or Playwright is not installed or is not on PATH. Install the browser and verify with its version command; install Playwright’s browser with python -m playwright install chromium.
Blank or incomplete image Capture happened before client-side rendering or lazy loading. Wait for a visible selector, relevant response, or controlled delay; scroll to trigger lazy content.
Timeout Slow origin, blocked request, or never-ending connections. Increase the timeout, use a more specific readiness condition, and inspect the page manually.
Element not found Selector changed, frame is different, or the element is not rendered yet. Use a stable selector, wait for visibility, and switch into the correct frame when needed.
Full-page output is huge The document is very tall or uses high-resolution capture. Capture a viewport or element, reduce dimensions, or use JPEG/WebP where appropriate.
Fonts differ from local browser Fonts are unavailable in the runtime or load after capture. Install required fonts, wait for them, and keep the browser environment consistent.
CAPTCHA or bot-check page The destination is challenging automation. Respect the site’s access rules; do not treat the challenge page as the intended screenshot. ScreenshotNeo identifies bot checks in its page verdict.

9. Performance, reliability, and cost

  • Reuse one browser process for multiple URLs instead of launching a new browser for every page.
  • Use a fixed viewport and readiness condition so output is comparable between runs.
  • Limit concurrency to what your CPU, memory, and target sites can handle.
  • Set explicit navigation and overall job timeouts, then record the URL and failure reason.
  • Prefer element or viewport captures when a full document is unnecessary.
  • Cache stable pages and avoid recapturing unchanged content.
  • For long-running services, pin compatible Playwright and browser versions and monitor disk space for artifacts.

Local browser cost is your compute, memory, browser installation, and maintenance. ScreenshotNeo charges only for clean shots; failed loads, blank pages, bot checks, timeouts, and cache hits are not billed. Its free tier includes 1,000 screenshots per month without a card, and paid plans start at $5 for 3,000.

10. Practical checklist

  • Choose Chrome Headless for one URL, Playwright CLI for shell automation, or Python for application logic.
  • Set the viewport explicitly.
  • Choose viewport, full-page, or element scope.
  • Wait for a real readiness signal on dynamic pages.
  • Trigger lazy loading before full-page capture.
  • Pick PNG, JPEG, or WebP based on quality and size requirements.
  • Record browser and library versions for reproducibility.
  • Handle timeouts, bot checks, and missing elements as expected failure modes.

11. FAQ

Can I take a screenshot without opening a visible browser?

Yes. Chrome Headless and Playwright run without a visible window by default.

What is the simplest full-page Python example?

Navigate with Playwright, then call page.screenshot(path="full-page.png", full_page=True).

Which format should I use?

Use PNG for lossless detail and transparency, JPEG for smaller photographic images, and WebP when your consumers support it.

Why is a network-idle wait sometimes unreliable?

Analytics, polling, and streaming connections can keep a page busy indefinitely. A selector or application-ready marker is usually a better condition.

Can an AI agent request screenshots?

Yes. ScreenshotNeo’s MCP server provides take_screenshot, get_page_info, and capture_pdf tools for MCP clients such as Claude and Cursor.