Screenshot API for Node.js

Capture a web page as an image from Node. Uses the built-in fetch, so there is no dependency to install and nothing to keep updated.

Quickstart

This snippet was executed against a live server before being published here.
import { writeFile } from 'node:fs/promises';

const params = new URLSearchParams({
  url: 'https://example.com',
  access_key: process.env.SCREENSHOTLINE_KEY,
  format: 'png',
  viewport_width: '1280',
  block_ads: 'true',
  block_cookie_banners: 'true',
});

const res = await fetch('https://api.screenshotline.com/take?' + params);
if (!res.ok) {
  const { error } = await res.json();
  throw new Error(error.message);
}

await writeFile('screenshot.png', Buffer.from(await res.arrayBuffer()));

// How much of your monthly quota is left, without a second request.
console.log(res.headers.get('x-quota-remaining'), 'renders remaining');
Need a key? Create a free account — 500 renders a month, no card, no confirmation email. Pass it as access_key=... or an X-Access-Key header. Paid plans start at $17 for 2,000 — pricing. Or try it with no key at all on the homepage demo.

Options

ParameterDefaultWhat it does
urlrequiredThe page to capture. http/https only; private and reserved addresses are refused.
url—The page to capture. The scheme is optional: example.com works.
formatpngpng, jpeg, webp or pdf.
full_pagefalseCapture the whole scrollable page, scrolling first so lazy images load.
viewport_width1280Viewport width in pixels.
viewport_height800Viewport height in pixels.
selector—Capture only the element matching this CSS selector.
block_adsfalseBlock ad networks and collapse the empty slot they leave behind.
block_cookie_bannersfalseRemove consent banners, including ones injected after load.
block_chatsfalseRemove live-chat widgets: Intercom, Drift, Crisp, Zendesk, Tawk and others.
block_popupsfalseRemove newsletter, donation and app-install interstitials.
hide_selector—Hide elements matching these CSS selectors. Comma separated.
inject_css—CSS applied after the page settles, before capture.
inject_js—JavaScript run after the page settles, before capture.
cookies—Session cookies, as a=1; b=2. Sent on the first request, so login walls work.
color_schemelightRender the page as light or dark.
delay0Extra wait before capture, in ms.
cachefalseServe a cached image when one exists. Cache hits are never billed.
fail_on_blankfalseReturn 502 instead of an image when the capture looks blank.

Errors

Failures return JSON with a stable code. Successful captures return the image bytes directly, with no envelope.

{
  "error": {
    "code": "invalid_access_key",
    "message": "Unknown or revoked API key."
  }
}

Common codes: missing_access_key (401), invalid_access_key (403), quota_exceeded (402), render_timeout (504), blank_capture (502).

Failed renders are not billed, and neither are cache hits. We detect a blank capture rather than assuming a 200 means success, so a bot wall that returns an empty page does not come out of your quota — up to 10% of your plan's renders a month, at least 50. Every response says whether it counted in X-Billed.

Signed URLs

The headline use case is putting a capture straight into an <img src>. A raw key in public HTML is a billing incident waiting to happen, so sign the URL instead: the signature covers every parameter, and a tampered URL is refused.

node tools/sign.js "https://example.com" full_page=true

Set REQUIRE_SIGNATURE=true to refuse unsigned requests entirely.

Quota headers

Every response carries your position, so you never need a second request to check:

X-Quota-Limit: 2000
X-Quota-Used: 417
X-Quota-Remaining: 1583
X-Quota-Period: 2026-09