·6 min read

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 as png, jpeg, or webp
  • fullPage — capture the entire scrollable page, not just the viewport
  • deviceScaleFactor — 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.

Ready to try it?

Get your free API key and start building in under a minute.