Puppeteer vs Screenshot API: When to Self-Host vs Use a Service
If you need programmatic screenshots, you have two options: spin up Puppeteer (or Playwright) yourself, or call a screenshot API. The right choice depends on volume, your team's DevOps appetite, and how much your time is worth. Here's the honest breakdown.
What you actually pay for with self-hosting
Server cost
Puppeteer is memory-hungry. A single Chromium instance uses 200-500 MB. To handle concurrent requests you need either:
- A dedicated VPS with 4+ GB RAM ($20-40/month on DigitalOcean/Hetzner)
- A Lambda layer with chromium-min (subject to the 250 MB layer limit and 10 GB temp limit)
- A container running on Fly/Railway/ECS
For low traffic, $20/month covers it. For 10K+ renders/month, you're at $50-100 just for compute.
DevOps time
This is the hidden cost everyone underestimates. With self-hosted Puppeteer you own:
- Chromium updates — security patches every 4-6 weeks. Skip them and you're a CVE waiting to happen.
- Memory leaks — Chromium leaks. You need a watchdog that restarts the browser every N requests or every X minutes.
- Concurrency control — without a queue, 10 requests = 10 browsers = OOM.
- Font management — emoji, CJK, custom fonts. Alpine Linux has none of them by default.
- SSRF protection — without filtering, any URL submission is a path to your internal network.
- Timeout handling — pages that never finish loading; pages that crash Chromium.
- Image post-processing — cropping, format conversion, retina scaling.
Realistic estimate: 1-2 days to ship a working prototype, then 1-2 hours per week of ongoing maintenance. At $100/hr that's $400-800/month in engineer time alone.
Failure modes you'll hit
- "Works on my machine" — Mac dev, Linux prod, fonts render differently.
- "Works for 100 requests, then crashes" — memory leak, no watchdog.
- "Random timeouts in production" — CPU starvation, no concurrency limit.
- "Bot detection" — Cloudflare/Akamai catches headless Chrome on day 1.
- "Lambda cold start is 8 seconds" — Chromium boot time on serverless.
What you pay for with a screenshot API
Pricing scales with renders. At ~100 free renders/month and starter tiers around $9-19/mo for 1-2K renders, you're paying roughly:
- 1,000 renders/month — $7-9/month
- 10,000 renders/month — $35-79/month
- 100,000 renders/month — $180-299/month
For that you get: zero infrastructure, automatic Chromium updates, SSRF protection, retries, font support, ad blocking, retina, PDF output, webhooks. You write maybe 5 lines of code and ship.
The breakeven math
The honest breakeven is around 1 million renders/month. Below that, an API is almost always cheaper than the all-in cost of self-hosting (compute + engineer time).
Above 1M renders you start hitting volume tiers where running your own infrastructure genuinely beats per-request pricing — but you also need a real ops budget.
When to self-host anyway
- Compliance — regulated data that can't leave your network (healthcare, government, defense).
- Custom rendering — you need to inject specific browser flags, custom Chromium build, or specialized fingerprinting.
- Massive scale — 10M+ renders/month where you have the team to operate it properly.
- Already running it — if you have a working setup with bandwidth to maintain it, don't migrate just to migrate.
When to use an API
- You're a small team — DevOps time has the highest opportunity cost.
- You need it now — API integration is hours; self-hosting is days/weeks.
- Variable traffic — you don't want to pay for idle servers.
- You need PDFs — adds another rendering pipeline if self-hosting.
- You need webhook callbacks — async rendering plumbing is non-trivial to build.
The hybrid path
Many teams start with an API to validate their use case, then evaluate self-hosting once they hit 100K+ renders/month and have a clear cost picture. This is usually the right call — you avoid premature infrastructure investment, and the API code is so simple you can swap it out later.
Start with an API — switch later if you outgrow it. 100/month free.
Get your API key — free