Why Self-Hosted Puppeteer Costs More Than a Screenshot API
Puppeteer works until it doesn't. Early in a project, running Chrome on a $20 VPS feels fine. Then traffic grows, concurrent requests pile up, and the tab that should take two seconds starts taking forty. Memory climbs. The process crashes. You add a cron job to restart it and call it done. Six months later, you have a team member whose job is keeping Chrome alive.
This is the puppeteer vs screenshot api decision most teams make reactively instead of proactively. Below is a concrete cost breakdown of self-hosted Puppeteer, where the math tips toward an API, and when DIY still makes sense.
What Self-Hosted Actually Costs
Chrome is expensive to run. Each browser instance holds its own V8 heap, network stack, and renderer processes. A single headless Chrome tab uses roughly 300-400 MB of RAM at idle and spikes higher during active rendering. At five concurrent requests, that is 1.5-2 GB just for the browser, before your application server, database, or anything else running on the box.
Here is a realistic monthly breakdown for a team running 5,000 screenshots per month at low-to-moderate concurrency (five parallel captures):
| Cost Category | Monthly Estimate | Notes |
|---|---|---|
| EC2 t3.xlarge (4 vCPU, 16 GB) | ~$120 | Minimum for five concurrent tabs plus OS headroom |
| EBS storage + data transfer | ~$15 | OS image, Chrome binary, screenshot output |
| S3 storage for output files | ~$10 | At ~150 KB average per screenshot (WebP) |
| Monitoring + alerting | ~$20 | Datadog, Better Uptime, or equivalent |
| Engineering maintenance | ~$300-900 | 2-6 hrs/month at $150/hr. Often more. |
| Total | ~$465-1,065 | Before incident response time |
The engineering maintenance figure is the one teams consistently undercount. Chromium updates break headless behavior. Puppeteer version bumps require regression testing. Memory leaks surface in production at 3 AM. Docker images grow. You add a queue. You add retries. Each fix is small; the total is a part-time job.
Costs That Never Appear on the Cloud Bill
Beyond line items, self-hosted Puppeteer carries invisible costs:
Cold starts in serverless environments. Chrome cannot run inside AWS Lambda at normal function sizes or in Cloudflare Workers on the standard tier. If you need serverless, you end up with a sidecar service or a library like
@sparticuz/chromium, which adds latency, maintenance, and its own version fragility.Geo-targeting is a separate problem. If a client needs screenshots from a specific country, self-hosted requires a proxy layer on top of Chrome. That is another vendor, another failure point, and another monthly line item.
Burst capacity requires manual work. A batch of 500 URLs submitted at once will overwhelm a single instance. Scaling horizontally means a queue, a scheduler, and multiple Chrome processes coordinated across machines.
Storage and delivery are your problem. Screenshots need to land somewhere accessible. S3 with signed URLs, CDN configuration, expiry policies, and access control are all table-stakes work that an API handles for you.
Security patching. Chrome ships security updates frequently. Each one requires you to pull a new image, test, and redeploy. On a managed API, the vendor handles this.
What the API Approach Looks Like
The puppeteer vs screenshot api calculation changes when you count the full cost of self-hosted. Here is the same task using a dedicated rendering API:
# One request, no infrastructure to manage
curl -X POST https://api.rendex.dev/v1/screenshot \
-H "Authorization: Bearer rdx_your_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"format": "webp",
"fullPage": true
}' --output screenshot.webp
# Get your key at https://rendex.dev/login
Or using the JavaScript SDK:
// npm install @copperline/rendex
import { Rendex } from "@copperline/rendex"
// Get your key at https://rendex.dev/login
const rendex = new Rendex("rdx_your_key")
const { image, metadata } = await rendex.screenshot({
url: "https://example.com",
format: "webp",
fullPage: true,
})
// image is a Buffer — write to disk or upload directly
At 5,000 captures per month, cost lands on the Starter plan. Compare that to the $465-1,065 range above. The 500 free calls per month on the free tier let you validate the integration before spending anything.
For a feature-by-feature comparison with pricing, the Rendex vs ScreenshotOne breakdown covers both products honestly, including where ScreenshotOne has the edge. And if you need to process URLs in bulk, the async batch rendering overview shows how queue-based workflows operate.
When Self-Hosted Puppeteer Wins
Self-hosted is the right call in specific situations:
You already run Playwright or Puppeteer for testing. If your CI pipeline runs browser tests on a dedicated fleet, adding screenshot generation on the same infrastructure costs almost nothing marginal.
You need multi-step browser interaction before capture. Login flows, cookie banners that require clicks, SPAs that navigate through several states before reaching the target view. APIs can inject cookies and headers, but a full click-through auth flow still requires self-hosted control.
Volume is very high and you are cost-sensitive. Above roughly 100,000 screenshots per month, a dedicated server may cost less than per-call API pricing. Run the numbers at your actual volume.
The pattern: teams with fewer than 50,000 captures per month and no existing browser automation fleet almost always come out ahead on total cost with an API. Teams above that threshold with infrastructure already in place often find self-hosted cheaper on compute, though the operational overhead rarely disappears entirely.
Try the API Side of the Comparison
The quickstart guide gets you from zero to your first screenshot in under five minutes. The free tier includes 500 calls per month with no credit card required. If you want to test before writing any code, the browser-based screenshot tool runs a live capture against any public URL.
If you're still deciding, start with the free tier on a single service or pipeline and measure. The difference in operational load is usually apparent within a week. Get a free API key and see where the time actually goes.
Top comments (0)