You started with Puppeteer because it was the fastest way to get screenshots working. Then production happened. Memory leaks. Random timeouts. Chromium crashing under load. A new Puppeteer version breaking your build. A dedicated server just for headless Chrome eating into your hosting budget.
This guide explains why developers switch from self-hosted Puppeteer/Playwright to a hosted API, and how to migrate your existing screenshot or PDF code to URLSnap in under 10 minutes.
Puppeteer is a great library — for local development. In production, the problems stack up fast:
libnss3, libgbm, libasound2...).
If you've spent more than 2 hours debugging Puppeteer in production, you've already spent more than a year of URLSnap Starter plan costs.
| Feature | Self-hosted Puppeteer | URLSnap API |
|---|---|---|
| Screenshots | ✓ | ✓ |
| PDF generation | ✓ | ✓ |
| Full-page screenshots | ✓ | ✓ |
| Custom viewport size | ✓ | ✓ |
| JS rendering (SPAs) | ✓ | ✓ (headless Chromium) |
| Wait for page load | ✓ (manual) | ✓ (?delay param) |
| No server required | ✗ | ✓ |
| Auto scaling | ✗ (DIY) | ✓ |
| Zero dependency installs | ✗ | ✓ |
| Memory managed for you | ✗ | ✓ |
| Works in serverless (Lambda, Vercel) | ✗ (very hard) | ✓ |
| Free tier | ✓ (OSS) | ✓ (20 req/day) |
| Cost to start | $5–20+/mo server | $0 free / $9/mo Starter |
Here's typical Puppeteer code for taking a screenshot:
// Before: Puppeteer (Node.js)
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 800 });
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
const screenshot = await page.screenshot({ fullPage: true });
await browser.close();
// Don't forget: error handling, timeout logic, browser pool management...
Here's the same thing with URLSnap:
// After: URLSnap API (Node.js)
const response = await fetch(
`https://urlsnap.dev/api/screenshot?url=https://example.com&full=true&width=1280&apiKey=YOUR_KEY`
);
const screenshot = await response.arrayBuffer();
// Done. No browser to manage.
That's it. One HTTP call replaces the entire Puppeteer setup — no browser binary, no launch options, no pool management.
// Before: Puppeteer PDF
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
const pdf = await page.pdf({ format: 'A4', printBackground: true });
await browser.close();
// After: URLSnap PDF
const response = await fetch(
`https://urlsnap.dev/api/pdf?url=https://example.com&format=A4&apiKey=YOUR_KEY`
);
const pdf = await response.arrayBuffer();
If you're using Playwright for screenshots instead of Puppeteer, the same problems apply — and the same migration path works. URLSnap's API accepts any URL and returns a screenshot or PDF, regardless of what tool you were using before.
One of the biggest wins of switching to an API: it works everywhere, including places where Puppeteer flat-out doesn't:
fetch().GET https://urlsnap.dev/api/screenshot
# Required
url=https://example.com # URL to capture
apiKey=YOUR_API_KEY
# Optional
width=1280 # viewport width (default: 1280)
height=800 # viewport height (default: 800)
full=true # full-page screenshot
delay=2000 # wait N ms after page load (for animations/async)
format=jpeg # png (default) or jpeg
quality=85 # jpeg quality 1-100
GET https://urlsnap.dev/api/pdf
# Required
url=https://example.com
apiKey=YOUR_API_KEY
# Optional
format=A4 # A4 (default), Letter, Legal, A3, A5, Tabloid
landscape=true # landscape orientation
delay=1000 # wait N ms before generating PDF
URLSnap is priced to be cheaper than running Puppeteer yourself:
Compare that to a $20/month DigitalOcean droplet just to run Chromium — plus your time maintaining it.
Get your free API key in 30 seconds. 20 requests/day free, no credit card needed.
Get Free API Key View DocsYes. URLSnap uses headless Chromium — the same engine Puppeteer and Playwright use. SPAs, React apps, Vue apps, and anything else that requires JavaScript will render correctly. Use ?delay=2000 if your page has animations or async data loading.
For publicly accessible pages, yes. For pages behind authentication, you'd need to generate the URL with a pre-authenticated session token or use a public snapshot URL. This is a known limitation of hosted APIs vs self-hosted Puppeteer.
Browserless is a hosted Puppeteer endpoint that exposes the Puppeteer API over a WebSocket. It's more powerful but also more complex — you're still writing Puppeteer code, just running it remotely. URLSnap is simpler: one HTTP request returns one image or PDF.
URLSnap handles the 90% use case — screenshots and PDFs. If you need to interact with pages (click, fill forms, run custom JS), you still need Puppeteer or Playwright. But for read-only capturing, URLSnap will handle it.