ScreenshotNeo

BlogComparisons

Open-Source Website Screenshot Tools for Instant Page Previews

Compare open-source browser, CLI, and self-hosted tools for website previews, with runnable examples and a managed API option.

By the ScreenshotNeo team4 October 202612 min read

To generate website previews with open-source tools, choose Playwright when capture belongs in application code, shot-scraper for shell commands and scheduled captures, or ShotAPI when you want to run your own HTTP screenshot service. Each renders a page in a browser, so the result depends on the URL, viewport, load state, and browser behavior. The sources document features and setup, not a comparative speed or image-quality winner.

If you want to avoid installing and operating a browser, ScreenshotNeo is a hosted screenshot API and MCP server. The open-source options and the hosted service solve related but different operational problems.

1. Choose a capture workflow

Need Option Why it fits What you operate
Capture inside an application Playwright Page and element screenshots, full-page capture, and screenshot bytes for further processing. Your application and browser installation.
Run a command or schedule captures in CI shot-scraper CLI capture plus a documented GitHub Actions workflow for configured screenshots. Python environment, browser dependencies, and workflow.
Offer a self-hosted HTTP endpoint ShotAPI Its repository documents a service with viewport, format, selector, delay, dark-mode, and full-page options. The service, server environment, and browser installation.
Skip browser infrastructure ScreenshotNeo Managed URL-to-image or PDF capture, with clean shots billed and failure/cache outcomes identified in response headers. An API key and your application integration.

For an open-source implementation, start with the workflow you need: library, CLI, or HTTP service. These are documented capabilities, not an independently tested ranking. The ShotAPI README identifies the project as MIT licensed; check the repository for current status and terms before adopting it. ShotAPI repository.

2. Capture a website with Playwright

Playwright is a browser automation library. Its screenshot API can save an image to a path or return bytes, capture an element, and capture the full scrollable page. The examples below use the Python package. Install the package and browser once in your environment:

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

Save this as screenshot.py and run python screenshot.py https://example.com:

import asyncio
import sys
from pathlib import Path
from playwright.async_api import async_playwright

async def main(url: str) -> None:
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page(viewport={"width": 1440, "height": 1000}, device_scale_factor=1)
        try:
            response = await page.goto(url, wait_until="networkidle", timeout=60_000)
            if response is not None and response.status >= 400:
                raise RuntimeError(f"Navigation returned HTTP {response.status}")
            await page.screenshot(path="page.png", full_page=True)
            print("Saved page.png")
        finally:
            await browser.close()

if __name__ == "__main__":
    if len(sys.argv) != 2:
        raise SystemExit("Usage: python screenshot.py https://example.com")
    asyncio.run(main(sys.argv[1]))

networkidle is one possible readiness signal, but pages with persistent network connections may never reach it. For a page with a known main element, use a selector wait instead, then capture:

await page.goto(url, wait_until="domcontentloaded", timeout=60_000)
await page.locator("main").wait_for(state="visible", timeout=15_000)
await page.screenshot(path="main.png", full_page=False)

To capture a single element, locate it and call its screenshot method:

card = page.locator("article.preview-card")
await card.wait_for(state="visible")
await card.screenshot(path="card.png")

To process the image in memory instead of writing a file, omit the path and retain the returned bytes:

image_bytes = await page.screenshot(full_page=True)
# Pass image_bytes to an image processor, object store, or HTTP response.

For exact method options and language-specific APIs, use the Playwright screenshot documentation and the current API reference for your chosen binding. Viewport, browser engine, device scale factor, readiness condition, and full-page behavior all affect the capture. A full-page image can be very tall; choose a viewport or element capture if the consumer expects a compact preview.

3. Generate previews from the command line with shot-scraper

shot-scraper is useful for a one-off capture or a repeatable shell/CI workflow. Its documented setup installs the Python package and then the required browser:

python -m pip install shot-scraper
shot-scraper install
shot-scraper https://example.com/

The command writes a screenshot file; consult the current CLI help and documentation for output naming and additional options. The documentation also describes configuration through shots.yml and a GitHub Actions workflow that installs dependencies, captures configured URLs, and commits generated screenshots to the repository. Its release 0.14 guide gives a Python 3.10 example and caches Playwright browser files; verify current workflow syntax and runner details before using that example.

For scheduled previews, make the job repeatable: keep the URL list and capture settings in version control, pin or deliberately update dependencies, cache browser downloads where appropriate, and decide whether generated images should be committed or uploaded as build artifacts. A changing page can create noisy diffs, so use stable test pages or review preview changes as part of the workflow.

See the shot-scraper documentation for the documented CLI and workflow details.

4. Run a self-hosted screenshot HTTP service with ShotAPI

Choose a self-hosted service when other applications need a URL-to-image endpoint under your control. The ShotAPI repository documents a GET /take route and parameters for output format, viewport, scale, delay, CSS selector, dark mode, and full-page capture. Request syntax and deployment details can change, so use the repository README as the source of truth.

The documented setup is to clone the repository, install npm dependencies, install Playwright Chromium, and start its development command. The README also shows Docker build and run instructions. Follow the current README commands for the environment you deploy; avoid exposing an unauthenticated screenshot endpoint publicly unless you have added access controls and request limits.

For an integration, send the target URL and supported capture settings to the service’s documented route, then save or forward the image response. Restrict which URLs callers can request if the endpoint is reachable by untrusted users: arbitrary URL rendering can consume browser resources and can let callers probe network locations accessible to your server. The repository documents a project setup, not independent reliability or maintenance guarantees. See the ShotAPI repository; its README identifies an MIT license and labels displayed pricing as “Coming Soon,” so treat that pricing as unverified.

5. How can I take a screenshot of a website?

For one URL, run the shot-scraper command or the Playwright script. For application code, Playwright gives you direct control over navigation, readiness, viewport, and output handling. For repeated shell captures, keep a shot-scraper configuration in the project and invoke it in CI. If multiple applications need a shared endpoint, deploy a self-hosted service and secure it.

  1. Choose the target URL and output dimensions.
  2. Decide whether you need the visible viewport, the full page, or one element.
  3. Wait for a meaningful ready condition: a selector, a deliberate delay, or network idle when appropriate.
  4. Capture and store the image; record enough context to reproduce it, such as URL, viewport, browser version, and capture time.
  5. Check the image for missing fonts, blocked resources, overlays, or content that loads only after interaction.

6. How do I capture a full-page screenshot?

In Playwright, pass full_page=True in Python or fullPage: true in JavaScript. Microsoft describes this as capturing the whole scrollable page as though it fit on a very tall screen. See the Playwright documentation.

# Python Playwright
await page.screenshot(path="full.png", full_page=True)
// JavaScript Playwright
await page.screenshot({ path: 'full.png', fullPage: true });

Full-page capture is not a guarantee that every site will render exactly as a human scroll session. Lazy-loaded content may need scrolling or a wait before capture, sticky elements may appear differently, and exceptionally long pages can create large images or exceed process memory. If you only need a card, hero, or content region, capture that element instead.

7. JavaScript / Node.js Playwright example

Install Playwright and Chromium, save this as screenshot.mjs, and run node screenshot.mjs https://example.com:

import { chromium } from 'playwright';

const url = process.argv[2];
if (!url) throw new Error('Usage: node screenshot.mjs https://example.com');

const browser = await chromium.launch();
try {
  const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
  const response = await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60_000 });
  if (response && response.status() >= 400) {
    throw new Error(`Navigation returned HTTP ${response.status()}`);
  }
  await page.locator('body').waitFor({ state: 'visible', timeout: 15_000 });
  await page.screenshot({ path: 'page.png', fullPage: true });
  console.log('Saved page.png');
} finally {
  await browser.close();
}

For an element capture in JavaScript, use await page.locator('article.preview-card').screenshot({ path: 'card.png' }). For a buffer, call await page.screenshot() and pass the returned bytes to your application. Keep the browser lifecycle bounded with try/finally so navigation or file errors do not leave a browser process running.

8. cURL and Python requests for a hosted API

cURL and Python requests do not render pages by themselves. They call a screenshot service that runs a browser. These ScreenshotNeo examples use the supplied API endpoint and code shape; replace the sample URL with your target and provide your API key.

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

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

Use HTTPS for API calls so the key and request data are encrypted in transit. Avoid putting secret keys in public browser code or source repositories. For the available capture options and response behavior, see the ScreenshotNeo API documentation.

9. Or skip the browser setup

A single ScreenshotNeo GET request returns a screenshot or PDF for a URL. Here is the Node.js call:

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

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan. See the docs for options and setup, then sign up free for 1,000 screenshots a month with no card.

10. Capture options that change the result

Choice Effect Practical note
Viewport width and height Changes responsive layout and visible content. Use the dimensions where the preview will appear; a mobile viewport can produce a different page structure.
Full page or viewport Captures the scrollable document or only the visible screen. Full page can be very tall and expensive to process locally.
Element selector Limits capture to a target component. Wait until the element is visible; verify selector uniqueness.
Readiness condition Determines when capture starts. Prefer a known content selector when network activity never becomes idle.
Delay Allows time-based rendering or animation to settle. A fixed wait adds latency and may still be too short or unnecessarily long.
Output format and scale Affects compatibility, file size, and detail. Match the consumer’s format support and display scale.
Dark mode Can render a site’s dark appearance when supported by the tool. Check whether the target site responds to browser color-scheme settings.

ShotAPI documents several of these controls in its service README. Playwright exposes browser-level configuration directly. ScreenshotNeo supports full-page capture with lazy images loaded, selector capture, dark mode, device presets and custom viewport, retina scale, image formats, custom CSS and JavaScript, pre-capture clicks, hiding selectors, wait conditions, request blocking, headers, cookies, user agent, authorization, timezone, geolocation, transparent background, resizing, caching, signed links, asynchronous jobs, webhooks, bulk capture, usage API, and an OpenAPI specification. Check the API docs for parameter names and request details.

11. Reliability, performance, and cost

Browser-based tools

Self-managed tools make you responsible for browser installation, compatible dependencies, memory, process cleanup, and concurrency. Reuse browser processes or workers for batches instead of launching an unbounded number of browsers. Set navigation and selector timeouts, close pages and browsers in cleanup paths, and cap simultaneous jobs. These are operational practices, not benchmark claims; the researched sources do not provide comparable speed measurements.

Captures may differ when a site changes, uses personalized content, depends on fonts or third-party assets, or loads content after interaction. For repeatable previews, fix the viewport and browser version, wait for the actual content, and consider suppressing animations in a test environment. Keep failure handling explicit: a navigation error should not silently produce a blank artifact that downstream code treats as valid.

Hosted API

A hosted API moves browser operations out of your application environment, but requests still need timeouts, error handling, secure key storage, and sensible retry behavior. Do not retry every error indefinitely: validate the URL and request first, use bounded retries for transient failures, and avoid retrying a result identified as a cache hit or an unbillable page outcome without a reason. ScreenshotNeo’s response includes page-verdict and billing headers, and its stated policy is to bill only clean shots.

Cost model

For open-source software, a license does not remove infrastructure costs. Budget for the server or CI minutes, browser downloads, storage, image transfer, maintenance, and engineering time. The sources do not establish deployment prices or comparative total cost. ScreenshotNeo lists Free at 1,000 shots/month, Starter at $5 for 3,000, Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000, and Business at $249 for 1,000,000; yearly billing gives two months free. Confirm current plan details on its site before making a purchasing decision.

12. Troubleshooting

Symptom Likely cause Fix
Browser executable missing The Playwright package is installed but its browser was not downloaded. Run python -m playwright install chromium or the relevant install command for your language and environment.
Navigation timeout The site is slow, has persistent connections, or the chosen readiness signal never occurs. Use a realistic timeout and wait for a specific visible selector; avoid relying on network idle for pages that keep connections open.
Screenshot is blank or incomplete Capture happened before the main content rendered, the page returned an error, or scripts/resources failed. Check the navigation response and logs, wait for the main content selector, and inspect the URL from the same runtime.
Lazy images are missing Images load only when they approach the viewport or after scrolling. Use full-page behavior that triggers loading where supported, scroll through the page and wait, or capture only the relevant region.
Element screenshot fails The selector matches nothing, is hidden, or appears after the wait timeout. Check selector spelling and uniqueness, wait for visible state, and confirm the page is in the expected frame.
Unexpected mobile or desktop layout Viewport, device scale, or user-agent settings differ from the intended preview. Set dimensions and device parameters explicitly; record them with the output.
Different result between runs Dynamic content, animation, personalization, fonts, or external resources changed. Stabilize test data where possible, wait for key content, and control viewport and browser configuration.
Self-hosted endpoint is overloaded Too many concurrent browser sessions or expensive full-page captures. Queue work, cap concurrency, apply request limits, and monitor memory and process cleanup.
Hosted API response is not an image The request may represent an error or a page verdict rather than a clean capture. Check HTTP status and response headers before saving bytes as an image; see provider docs for response semantics.

13. Frequently asked questions

Are all website screenshot tools open source?

No. Playwright, shot-scraper, and the ShotAPI repository are the open-source paths covered here. ScreenshotNeo and ScreenshotOne are hosted services, not open-source alternatives.

Can I use generated previews in a website?

Yes. Store the image and serve it through your application or object storage, or use a provider’s documented delivery options. Confirm the target site’s access rules and the screenshot tool’s terms for your use.

Which option should I use for a single preview?

Use the CLI for a quick shell capture, or Playwright if the capture must integrate with application logic. Choose a hosted API when operating the browser stack would be unwanted overhead.

Does a full-page screenshot include content that appears only after user interaction?

Not necessarily. A screenshot captures the rendered state reached by the browser. Trigger the interaction or wait for the relevant content before capturing, subject to the tool and target site’s behavior.

Can I assume captures are pixel-identical across environments?

No. Browser version, fonts, viewport, device scale, operating environment, network-loaded assets, and dynamic page state can affect the rendered pixels.

Sources