The Problem: Capturing Websites Programmatically
You're building a SaaS app and need link previews. Or a monitoring dashboard with visual diffs. Or automated reports with embedded page captures. Whatever the use case, you need to turn URLs into images — reliably, at scale.
Here are 3 approaches, from DIY to fully managed, with real code you can use today.
Method 1: Puppeteer (Self-Hosted)
Puppeteer is Google's official Node.js library for controlling headless Chrome. It gives you full control but requires managing browser instances yourself.
const puppeteer = require('puppeteer');
async function takeScreenshot(url, outputPath) {
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 800 });
await page.goto(url, { waitUntil: 'networkidle2' });
await page.screenshot({ path: outputPath, type: 'png' });
await browser.close();
}Pros: Full control, free, can interact with pages before capture.
Cons: Resource-heavy (~200MB RAM per browser), needs server management, crashes under load, Chrome updates can break things.
Method 2: Playwright (Self-Hosted, Multi-Browser)
Microsoft's Playwright supports Chrome, Firefox, and WebKit. Similar to Puppeteer but with better cross-browser support.
const { chromium } = require('playwright');
async function takeScreenshot(url, outputPath) {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.setViewportSize({ width: 1280, height: 800 });
await page.goto(url, { waitUntil: 'networkidle' });
await page.screenshot({ path: outputPath });
await browser.close();
}Pros: Multi-browser, better API design, auto-wait features.
Cons: Same resource overhead as Puppeteer, larger install size.
Method 3: Screenshot API (Managed Service)
Skip the infrastructure. Send a GET request, get an image back. Here's how with Pandan Screenshot API:
const https = require('https');
const fs = require('fs');
const url = 'https://api.pandan.is/v1/screenshot?url=https://github.com&width=1920&format=png';
https.get(url, (res) => {
const stream = fs.createWriteStream('screenshot.png');
res.pipe(stream);
stream.on('finish', () => console.log('Screenshot saved!'));
});
// Or with fetch (Node 18+)
const response = await fetch(
'https://api.pandan.is/v1/screenshot?url=https://github.com'
);
const buffer = Buffer.from(await response.arrayBuffer());
fs.writeFileSync('screenshot.png', buffer);Pros: No server to manage, no Chrome crashes, scales automatically, under 3 seconds.
Cons: External dependency, rate limits on free tier.
Comparison Table
| Feature | Puppeteer | Playwright | API |
|---|---|---|---|
| Setup time | 30 min | 30 min | 0 min |
| Server needed | Yes | Yes | No |
| RAM per instance | ~200MB | ~200MB | 0 |
| Concurrent captures | ~5-10/server | ~5-10/server | Unlimited |
| Full page capture | Yes | Yes | Yes |
| PDF generation | Yes | Yes | Yes |
| Cost at 1K/mo | $5-20 (server) | $5-20 (server) | $9 (Pandan) |
When to Use Each
- Puppeteer/Playwright: When you need to interact with pages (login, fill forms, click) before capturing, or when you're already running Chrome for other automation tasks.
- Screenshot API: When you just need URL → image. No infrastructure to manage, no Chrome crashes at 3 AM, predictable pricing. Start with the free tier (100/month) and upgrade when you need more.
Bonus: Generate PDFs Too
Need PDFs instead of images? All three methods support it:
// Pandan API — one line
curl "https://api.pandan.is/v1/pdf?url=https://example.com&format=A4" -o page.pdf
// Puppeteer
await page.pdf({ path: 'page.pdf', format: 'A4' });
// Playwright
await page.pdf({ path: 'page.pdf', format: 'A4' });The API approach is especially useful for PDFs since Chrome's PDF rendering can be memory-intensive — let someone else handle the infrastructure.
Get Started
Try the Pandan Screenshot API free — 100 screenshots per month, no credit card, no SDK required. Just a GET request.