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.
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');
| Parameter | Default | What it does |
|---|---|---|
| url | required | The page to capture. http/https only; private and reserved addresses are refused. |
| format | png | png, jpeg, webp or pdf. |
| full_page | false | Capture the whole scrollable page, scrolling first so lazy images load. |
| viewport_width | 1280 | Viewport width in pixels. |
| viewport_height | 800 | Viewport height in pixels. |
| selector | — | Capture only the element matching this CSS selector. |
| block_ads | false | Block ad networks and collapse the empty slot they leave behind. |
| block_cookie_banners | false | Remove consent banners, including ones injected after load. |
| color_scheme | light | Render the page as light or dark. |
| delay | 0 | Extra wait before capture, in ms. |
| cache | false | Serve a cached image when one exists. Cache hits are never billed. |
| fail_on_blank | false | Return 502 instead of an image when the capture looks blank. |
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).
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.
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