How to Automate Website Screenshots in Node.js (Without Puppeteer)
Capture website screenshots in Node.js with a single fetch call — no headless browser, no Chromium binary, no memory headaches.
Automating website screenshots is one of those tasks that sounds easy until you actually try it. The standard Node.js approach — spin up Puppeteer, launch a headless Chromium instance, navigate to a page, wait for rendering, take a screenshot — works, but it comes with a mountain of operational overhead.
What if you could capture a screenshot with a single fetch call? No browser binary, no 300MB dependency, no memory spikes. Here's how.
The Problem with Puppeteer-Based Screenshots
Puppeteer is powerful, but for screenshot automation it's like using a forklift to move a chair:
- Chromium binary (~300MB) — bloats your Docker images and deployment packages. Most serverless platforms can't even run it without workarounds.
- Memory usage — each Chromium instance uses 100-300MB RAM. Running multiple captures in parallel can crash your server.
- Zombie processes — improperly closed browser instances leak memory and file descriptors over time
- Rendering inconsistencies — font rendering, GPU acceleration, and timing issues mean the same page can look different across environments
For a deeper comparison of these trade-offs, see our guide on Puppeteer vs screenshot APIs.
The API Approach: One Fetch Call
With the API Snap Screenshot endpoint, capturing a website screenshot in Node.js is as simple as any other HTTP request:
import fs from "fs/promises";
const url = "https://github.com";
const res = await fetch(
`https://api-snap.com/api/screenshot?url=${encodeURIComponent(url)}&width=1280&height=800&format=png`,
{ headers: { Authorization: `Bearer ${process.env.SNAPAPI_KEY}` } }
);
const buffer = Buffer.from(await res.arrayBuffer());
await fs.writeFile("screenshot.png", buffer);
console.log("Screenshot saved!");That's it. No npm install puppeteer. No Chromium download. No browser lifecycle management. Just a fetch call and a file write.
Batch Screenshots with Concurrency Control
Need to capture hundreds of pages? With Puppeteer you'd manage a browser pool and worry about memory. With an API, you just control your request concurrency:
const urls = [
"https://example.com",
"https://github.com",
"https://stripe.com",
// ... hundreds more
];
async function captureScreenshot(url, index) {
const res = await fetch(
`https://api-snap.com/api/screenshot?url=${encodeURIComponent(url)}&width=1280&format=webp`,
{ headers: { Authorization: `Bearer ${process.env.SNAPAPI_KEY}` } }
);
const buffer = Buffer.from(await res.arrayBuffer());
await fs.writeFile(`screenshots/shot-${index}.webp`, buffer);
}
// Process 5 at a time to stay within rate limits
const CONCURRENCY = 5;
for (let i = 0; i < urls.length; i += CONCURRENCY) {
const batch = urls.slice(i, i + CONCURRENCY);
await Promise.all(batch.map((url, j) => captureScreenshot(url, i + j)));
}Express API Route: Screenshot as a Service
Expose screenshot generation as an internal API route for your team or product:
import express from "express";
const app = express();
app.get("/api/capture", async (req, res) => {
const { url, width = "1280", format = "png" } = req.query;
if (!url) return res.status(400).json({ error: "url is required" });
const screenshot = await fetch(
`https://api-snap.com/api/screenshot?url=${encodeURIComponent(url)}&width=${width}&format=${format}`,
{ headers: { Authorization: `Bearer ${process.env.SNAPAPI_KEY}` } }
);
const contentType = format === "webp" ? "image/webp" : "image/png";
res.set("Content-Type", contentType);
res.send(Buffer.from(await screenshot.arrayBuffer()));
});
app.listen(3000);Common Automation Use Cases
- Visual regression testing — capture screenshots before and after deployments, diff them to catch UI regressions
- Social sharing cards — generate OG images dynamically for blog posts or product pages
- Monitoring dashboards — capture periodic screenshots of internal tools and alert on visual anomalies
- Portfolio generators — automatically screenshot client websites for agency portfolios
- Content archival — preserve visual snapshots of web pages for compliance or research
Customization Options
The screenshot endpoint supports several parameters to fine-tune your captures:
width/height— set the viewport size (default: 1280x800)format— output aspng,jpeg, orwebpfullPage— capture the entire scrollable page, not just the viewportdeviceScaleFactor— capture at 2x for retina-quality screenshots
Pricing for Screenshot Automation
If you're automating screenshots for a cron job or CI pipeline, estimate your monthly volume. The free tier (100 calls/month) works for testing. For daily monitoring of 50 pages, you'd need ~1,500 calls/month — the Hobby plan ($9/mo) covers that with room to spare. For larger batch operations, the Pro plan at $29/mo handles 50,000 captures.
Get Started
Sign up for free, install nothing, and capture your first screenshot in three lines of code. Pair it with the URL Metadata API for rich link previews that include both metadata and visual thumbnails.