Guide
Take a website screenshot in Node.js
Playwright launches Chromium and writes a PNG. A hosted screenshot API skips that install and returns image bytes from one HTTP call. This guide shows a complete local script first. The DataTuber example is only the route this website implements.
- Published by
- DataTuber
- Published
- Updated
- Reading time
- 8 min read
Key takeaways
- Playwright is the direct approach: install the package, install Chromium, open a page, and call page.screenshot.
- A viewport capture is the window size you set. fullPage: true uses that width and the document’s scroll height.
- A hosted API does not require a browser on your machine. Each service defines its own fields. This page does not compare vendors.
- On this site, POST /api/screenshots accepts a public https URL, a viewport name, and png or jpg. It does not accept a pixel size or a full-page flag.
- Check the HTTP status before you save the body. A DataTuber error is JSON with a message. A success body is the image.
Ways to capture a page
Two approaches cover most Node.js screenshot work. Run a browser yourself, or send the URL to a service that runs one for you.
Playwright and Puppeteer are the usual libraries. Playwright keeps the browser download separate from npm install. The puppeteer package downloads Chromium as part of its install. The script on this page uses Playwright. Both can open localhost, set a viewport, and capture the full document height.
A hosted screenshot API accepts a page URL and returns an image. Your process does not launch a browser. Authentication, allowed hosts, viewport fields, and full-page support are defined by that API. Later sections describe only the route this DataTuber website implements. They are not a catalog of other services.
Prerequisites
Use Node.js 18 or newer. You need a network connection the first time you install Playwright’s Chromium build, and enough disk for that browser. Confirm the runtime with node --version before you install anything.
The script uses import and top-level await. Save it as screenshot.mjs, or use a package.json with "type": "module" and a .js file. Either way, run it with node.
Install Playwright and Chromium
npm install playwright adds the library. It does not add the browser. npx playwright install chromium downloads the Chromium build Playwright launches. Skip that second command and launch fails because the executable is missing.
npm install playwright
npx playwright install chromium
node screenshot.mjs
A complete Playwright example
This opens https://example.com at 1280×720, writes viewport.png for that window, then writes full-page.png for the full document. browser.close() runs even when navigation throws. Replace the URL with the page you want to capture.
import { chromium } from "playwright";
const browser = await chromium.launch();
const page = await browser.newPage({
viewport: { width: 1280, height: 720 },
});
try {
const response = await page.goto("https://example.com", {
waitUntil: "load",
timeout: 30_000,
});
if (!response?.ok()) {
const status = response ? response.status() : "no response";
throw new Error(`Navigation failed (${status}).`);
}
await page.screenshot({ path: "viewport.png", type: "png" });
await page.screenshot({
path: "full-page.png",
type: "png",
fullPage: true,
});
} finally {
await browser.close();
}
waitUntil: "load" waits for the load event. Pages that draw after that event can still look unfinished. Wait for a selector you care about before calling screenshot if the first image is blank. networkidle waits until network connections settle, and pages that keep a connection open can sit there until timeout.
Viewport and full page
The viewport is the browser window: a width and a height in CSS pixels. page.screenshot() without fullPage captures that rectangle only. Content below the fold is not in the file.
fullPage: true keeps the viewport width and extends the image to the document’s scroll height. Playwright scrolls and stitches that capture. A short page looks almost the same either way. A long article does not.
Full-page capture is a poor fit for a page that grows forever, such as an infinite scroll. Fixed headers can also repeat or cover content in the stitched image. Capture the viewport, or a single element with locator.screenshot(), when the full document is not a stable height.
The script does not set deviceScaleFactor, so the PNG is one pixel per CSS pixel. Raise it only when you need a denser image. That produces more pixels. It is not a quality score.
Browser automation and a hosted API
Choose Playwright when the page is on your machine, behind a login you can script, or when you need an exact width, height, and full-page flag. Choose an HTTP API when you do not want to install or operate a browser. The table states what this website’s route accepts. It does not rank products, and it does not report timings.
Playwright in your Node process compared with POST /api/screenshots on this website. Not a speed or quality ranking.
What you install
- Playwright
- playwright, then Chromium
- POST /api/screenshots
- Nothing beyond Node.js 18
Who runs the browser
- Playwright
- Your Node process
- POST /api/screenshots
- This website’s capture route
Size
- Playwright
- Any width and height you set
- POST /api/screenshots
- A viewport name, not pixels
Full page
- Playwright
- fullPage: true
- POST /api/screenshots
- Not a field on this route
Which URLs
- Playwright
- Any URL the machine can open, including localhost
- POST /api/screenshots
- Public https URLs only
Failure you handle
- Playwright
- A thrown navigation or launch error
- POST /api/screenshots
- An HTTP status and a JSON message
The route this site implements
POST https://datatuber.com/api/screenshots is the screenshot route served by this website. Do not send the call to api.datatuber.com. That host is not this API. The body is JSON with url, viewport, and format.
viewport is desktop, tablet, or mobile. In the current presets, desktop is 1280×720, tablet is 768×1024, mobile is 390×844. format is png or jpg. jpeg is accepted and stored as jpeg. Custom pixel sizes and a full-page flag are not part of this request.
The URL has to be https, with no username, password, port, or IP address. The server resolves the hostname and rejects localhost, link-local, and private addresses. http URLs are rejected.
Replace YOUR_API_KEY with a key from a verified account if you want the image stored for that workspace. The dashboard shows the secret once. Leave the placeholder in any example you commit. The homepage demo calls the same path without a key and does not store the image.
import { writeFile } from "node:fs/promises";
const origin = "https://datatuber.com";
const response = await fetch(new URL("/api/screenshots", origin), {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://www.python.org",
viewport: "desktop",
format: "png",
}),
});
if (!response.ok) {
const payload = await response.json().catch(() => null);
const message =
payload && typeof payload.message === "string"
? payload.message
: response.statusText;
throw new Error(message);
}
const image = Buffer.from(await response.arrayBuffer());
await writeFile("screenshot.png", image);
A successful response body is the image, with Content-Type image/png or image/jpeg, and cache-control no-store. Any other status is JSON. When the route recognized the failure, that JSON has a string message. Writing the body to disk before you check response.ok saves an error as if it were a PNG.
Homepage demo compared with a keyed call on the current DataTuber route. Limits come from the beta plan and rate buckets in the account migration.
Authorization
- Homepage demo
- No Authorization header
- API key
- Bearer token
Path
- Homepage demo
- POST /api/screenshots
- API key
- POST /api/screenshots
Image kept
- Homepage demo
- Not stored
- API key
- Stored for that workspace
Allowance
- Homepage demo
- 5 captures an hour for a hashed IP
- API key
- 100 screenshots in a UTC month, and 6 requests a minute
Shared gap
- Homepage demo
- 11 seconds between captures
- API key
- 11 seconds between captures
Nothing is charged before January 1, 2027. Pricing has no checkout. Field details are on Screenshots.
Common errors
Playwright: if launch says the executable does not exist, run npx playwright install chromium in that project. If goto throws a timeout, the load event did not happen within timeout. Increase it, or wait for a specific selector instead of the whole page. A connection error means the URL did not open from that machine. A blank PNG usually means the pixels you wanted were not painted yet.
A full-page file that is enormous, or that never finishes, usually means the page keeps growing. Capture the viewport instead.
This site’s route returns JSON, not an image, when the call is refused. "Send a URL, viewport, and format." means a required field was missing or not one of the accepted names. "Only https URLs are accepted." and "IP addresses are not accepted." are URL rules. "This hostname is blocked." and "The hostname resolved to a blocked address." reject local and private targets. "That API key is not valid." means the bearer token did not match a key. "Verify your email before using DataTuber." means the account cannot create or use a key yet.
"Screenshot capture is not configured on this server." means this deployment has no capture credentials. A 429 message asks you to wait. Those strings are the messages implemented in this repository.
What this page is based on
- Playwright
- The script uses playwright’s launch, newPage, goto, and screenshot methods. No timed run of that script is reported here.
- This website
- POST /api/screenshots, the URL checks, the viewport presets, and the account migration in this repository. Checked October 30, 2026.
- Not included
- No timed live capture, no performance numbers, and no ranking of other screenshot products.
Questions
Do I have to install a browser?
For the Playwright script, yes. npm install playwright is not enough. Run npx playwright install chromium as well. A hosted HTTP API does not install a browser on your machine.
What is the difference between a viewport and a full-page screenshot?
The viewport is the window size. fullPage: true keeps that width and captures the scroll height of the document. This site’s API only accepts the names desktop, tablet, and mobile. It has no full-page field.
Can Playwright screenshot localhost?
Yes, if the page is reachable from the machine running Node. POST /api/screenshots on this site rejects localhost, IP addresses, and private hosts.
Which Node.js version do I need?
Node.js 18 or newer. The HTTP example uses global fetch. If Playwright refuses to launch, its install notes will name the Node version it expects.
Which host do I call for DataTuber?
https://datatuber.com/api/screenshots. api.datatuber.com is not serving this API. Getting started shows the same body in cURL, JavaScript, Python, and PHP.
Will the DataTuber call charge a card?
No payment is collected before January 1, 2027. Pricing has no checkout.
Related
- Guides
The short index of notes published on this site.
- Getting started
The same POST in cURL, JavaScript, Python, and PHP.
- Screenshots
The body fields, and what a keyed call stores.
- Pricing
The beta allowance, and the earliest date billing can start.