Best Screenshot API for Capturing Web Pages in Dark Mode
Learn how dark-mode screenshots work, compare documented API options, and capture a page with a hosted service or Playwright.
Short answer: Choose an API that explicitly emulates the browser’s prefers-color-scheme: dark setting if you want a hosted screenshot service to request dark rendering. This only works as intended when the website supports that preference. If you need control over the browser and capture process, Playwright can emulate a dark color scheme and take the screenshot itself.
For a managed option, ScreenshotNeo offers a dark_mode screenshot option alongside viewport, full-page, and output-format controls. The available evidence supports comparing documented features and operating models; it does not establish a measured winner for fidelity, speed, uptime, or price across providers. Check each provider’s live documentation before adopting parameters in production.
1. What dark mode means for a screenshot
Dark mode is a browser preference that a website may choose to honor, not a universal filter applied to finished pixels. Browsers expose the preference through the prefers-color-scheme media feature. When a screenshot browser emulates dark, a site that responds to that feature can render its dark theme before the capture.
A site may instead use a theme switch that writes to local storage, a cookie, or application state. Some sites have no dark theme. In those cases, setting the browser preference may leave the page light or produce a partial result. A screenshot API cannot make a site’s unsupported theme appear automatically unless it offers additional page interaction or styling controls.
2. Choosing a screenshot approach
| Approach | Dark-mode control | Capture controls | Operational responsibility |
|---|---|---|---|
| ScreenshotNeo | Documented dark_mode option; rendering still depends on the site’s implementation. |
Full-page and element capture, viewport/device selection, PNG/JPEG/WebP/PDF, and other documented options. | Hosted API; no browser fleet to operate. |
| ScreenshotAPI.net | Documents dark_mode, described by the provider as attempting dark rendering where supported. |
Use its documentation to check the current capture options and limits. | Hosted API. |
| Playwright | Emulate prefers-color-scheme: dark in a browser context or page. |
Full-page, clipped, and viewport captures; PNG, JPEG, or WebP and other screenshot options. | You manage browser installation, execution, concurrency, and storage. |
This table compares documented mechanisms, not independently tested image quality or service reliability. Playwright documents colorScheme values including dark and screenshot options including full-page capture, format, scale, and clipping. Playwright color-scheme emulation · Playwright screenshot API. ScreenshotAPI.net describes its option as applying where a site supports a dark theme or built-in dark styles; that is a vendor description, not an independent test. ScreenshotAPI.net.
3. Capture a dark-mode screenshot with ScreenshotNeo
ScreenshotNeo is a hosted website screenshot API and MCP server from Yorker Media. Send a GET request with your API key, target URL, and dark-mode option. The examples below save the returned image bytes; get an API key and the exact current parameter documentation at ScreenshotNeo API documentation.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-d dark_mode=true \
-o shot.webp
Python
import requests
response = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://stripe.com",
"dark_mode": "true",
},
timeout=90,
)
response.raise_for_status()
with open("shot.webp", "wb") as image_file:
image_file.write(response.content)
Node.js
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com',
dark_mode: 'true',
});
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));
Use a URL-encoding mechanism such as --data-urlencode, URLSearchParams, or the Python params argument when the target URL contains query parameters, ampersands, or other reserved characters. Treat the API key as a secret: keep it server-side or in a secret store rather than embedding it in public browser code.
4. Capture dark mode yourself with Playwright
Playwright is the self-managed route: create a browser context with colorScheme: 'dark', navigate to the page, then capture it. Install Playwright and its Chromium browser in your project using the official Playwright installation instructions. The following runnable Node.js script uses the Playwright library and writes a full-page PNG.
// save as capture-dark.mjs
import { chromium } from 'playwright';
const target = process.argv[2] ?? 'https://stripe.com';
const browser = await chromium.launch({ headless: true });
try {
const context = await browser.newContext({
colorScheme: 'dark',
viewport: { width: 1440, height: 1000 },
deviceScaleFactor: 1,
});
const page = await context.newPage();
await page.goto(target, { waitUntil: 'networkidle', timeout: 45_000 });
await page.screenshot({ path: 'shot.png', fullPage: true, type: 'png' });
await context.close();
} finally {
await browser.close();
}
Run it with node capture-dark.mjs https://example.com. If a site keeps loading analytics or other background requests, networkidle may take too long; use domcontentloaded or load and add a targeted wait for the content you need. Playwright’s navigation API documents the available wait conditions.
Python alternative
For Python projects, install the Playwright package and its browser as described in the official Python setup guide. This script creates the browser context with dark preference and saves a full-page capture.
# save as capture_dark.py
import asyncio
import sys
from playwright.async_api import async_playwright
async def main():
target = sys.argv[1] if len(sys.argv) > 1 else "https://stripe.com"
async with async_playwright() as playwright:
browser = await playwright.chromium.launch(headless=True)
try:
context = await browser.new_context(
color_scheme="dark",
viewport={"width": 1440, "height": 1000},
device_scale_factor=1,
)
page = await context.new_page()
await page.goto(target, wait_until="networkidle", timeout=45_000)
await page.screenshot(path="shot.png", full_page=True, type="png")
await context.close()
finally:
await browser.close()
asyncio.run(main())
Run with python capture_dark.py https://example.com. For both scripts, create a new context per capture when you need independent cookies, storage, or color preferences. Use a fixed viewport and browser version for repeatable output.
5. Select capture options that match the job
- Viewport or full page: A viewport capture records what fits in the configured browser window. Full-page capture includes scrollable content, but unusually long pages can produce large files and may behave differently around sticky elements or lazy-loaded content.
- Output format: PNG is lossless and useful for visual comparisons; JPEG and WebP can reduce file size, with lossy quality tradeoffs. Confirm the response content type or file extension matches the requested format.
- Viewport and device scale: Keep width, height, and pixel scale stable when comparing screenshots. A different device scale can alter output dimensions and text rasterization.
- Wait strategy: Wait for the page state that matters: navigation completion, a selector, a short delay for animation, or network idle when appropriate. A fixed delay is simple but can waste time or still be too short.
- Theme controls: First request dark preference. If the site uses a custom toggle or stored theme, reproduce the site’s supported state with permitted cookies, storage, or interaction. Do not assume the browser preference overrides application logic.
- Dynamic content: For visual regression, freeze or mask timestamps, rotating banners, random content, and personalized regions if your capture tool supports it. Keep locale, timezone, fonts, and browser environment consistent.
ScreenshotNeo documents full-page capture with lazy images loaded, element capture by CSS selector, dark mode, device presets and custom viewports, retina scale, wait controls, custom CSS and JavaScript, and PNG/JPEG/WebP output. Check the API docs for the current parameter names and accepted values. Its parameter names also support names used by other screenshot APIs to make migration easier.
6. Troubleshooting dark-mode screenshots
| Symptom | Likely cause | What to do |
|---|---|---|
| The screenshot stays light. | The page does not respond to prefers-color-scheme, or its own theme state overrides it. |
Check the site in a browser with dark preference. If its UI uses a toggle, reproduce that state through supported storage or interaction; otherwise the site may not provide dark rendering. |
| Only part of the page is dark. | Embedded frames, third-party widgets, or site-specific styles handle theme independently. | Inspect the affected regions and determine whether their frame or widget has its own theme setting. Do not assume a global preference controls third-party content. |
| Capture fails or times out. | The page is slow, never reaches the chosen wait condition, or blocks automation. | Use a suitable navigation wait, raise the timeout within a practical limit, wait for a specific content selector, and inspect the target’s accessibility and network behavior. |
| Images or lower-page content are missing. | Lazy loading needs scrolling or time, or full-page behavior differs by page. | Use a full-page option with lazy-image support where available, or scroll through the page and wait for images before capturing in your own browser flow. |
| Image viewer reports a corrupt file. | An HTTP error or JSON error body was saved with an image extension. | Check HTTP status and response headers before writing bytes as an image. Log error bodies safely, without exposing API keys. |
| Visual comparisons change between runs. | Browser version, host OS, fonts, device scale, dynamic content, or timing changed. | Use the same execution environment and capture settings for baseline and comparison; mask or stabilize genuinely dynamic regions. |
7. Performance, reliability, and cost
Capture time and output size depend on the page and chosen options; the research does not establish comparative latency or benchmark results for the providers. Full-page captures, high device scale, heavy sites, long waits, and extra browser interactions can increase work per request. If you operate Playwright, you also own browser installation and updates, concurrency limits, job timeouts, retries, and artifact storage. A hosted API moves browser operations to the provider, while you still need to handle HTTP errors, request limits, and safe key management.
For repeatable visual checks, keep the browser version, operating system, fonts, viewport, device scale, locale, timezone, and page state consistent. Playwright notes that rendering may vary with host OS, browser version, settings, hardware, power source, and headless mode; use the same environment for reference and comparison captures. Playwright visual comparisons.
For ScreenshotNeo, the published plans are Free: 1,000 shots per month with no card; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; and Business: $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan. ScreenshotNeo says only clean shots are billed; bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, with the result identified by X-Page-Verdict and X-Billed headers. These are product terms, not a comparative pricing or reliability claim.
8. Or skip the browser setup
Use one API request to ask ScreenshotNeo for a dark-mode capture:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-d dark_mode=true \
-o shot.webp
ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. See the API documentation, then sign up for 1,000 free screenshots a month.
9. Frequently asked questions
Does dark mode change the pixels after the screenshot?
No. The documented approach requests dark rendering from the page’s browser environment before capture. A site that does not support the preference may not change appearance.
Can I use dark-mode captures for visual regression testing?
Yes, if you hold the browser and page conditions steady. Establish a baseline with the same theme preference, viewport, browser environment, fonts, and stable content.
Should I choose an API or run Playwright?
Choose a hosted API when you want a managed capture endpoint and its documented options. Choose Playwright when you need direct browser automation control and can maintain the browser runtime. Compare current limits and operational requirements against your workload.
Can a screenshot API force dark mode on every website?
No universal guarantee follows from setting a color-scheme preference. Some websites use their own toggle or saved theme state, and some do not implement a dark theme.
