Same headless Chrome output, none of the browser babysitting. HTML to Image replaces self-hosted Puppeteer for screenshot and image generation workloads.
Disclosure: we built HTML to Image. Puppeteer is excellent open-source software; this page is about whether you should run it yourself just to make images.
| Factor | Puppeteer (self-hosted) | HTML to Image |
|---|---|---|
| Cost to start | Free (open source) | $0 to start — 25 renders/month free |
| Infrastructure to run | Your servers or Lambda, plus Chrome | None — hosted API |
| Ongoing maintenance | Chromium updates, dependencies, memory | None |
| Rendering engine | Headless Chrome | Headless Chrome |
| JavaScript execution | Yes | Yes |
| Scaling & concurrency | Browser pools and queues you build | Handled by the API |
| Named templates | No | 25 |
| CDN-hosted output | No — you store and serve output | Yes — every render returns a CDN URL |
| Best for | full browser automation: testing, crawling, complex interactions | rendering images without owning browser infrastructure |
Keep Puppeteer when the browser is the point. Multi-step automation, authenticated crawling, end-to-end tests and bespoke PDF pipelines need a programmable browser, and Puppeteer is the standard tool for that. If screenshots fall out of automation you already run and maintain, there is nothing to migrate.
Buy the API when the browser only exists to produce images. That is the trap: the screenshot code takes an afternoon, then the browser needs somewhere to live. On Lambda that means special Chromium builds, layer size limits and slow cold starts — we wrote up the whole saga in Replacing Puppeteer on AWS Lambda for screenshots. On servers it means memory headroom, crash recovery, process reaping and a redeploy for every Chromium security update. HTML to Image is the same Chrome behind a single curl call, with the output already hosted on a CDN.
Zero infrastructure. No Chrome binary, no Docker image gymnastics, no @sparticuz/chromium version-matching, no memory tuning, no zombie processes. One HTTP call replaces the launch-navigate-screenshot-close-upload pipeline and everything that keeps it alive.
Scaling is someone else's job. Concurrency, queueing and retries are handled behind the endpoint. A traffic spike that would exhaust a browser pool is just more API calls, and webhook delivery covers async batches.
More than raw captures. Twenty-five named templates render invoices, OG images and social cards from a JSON payload — jobs that in Puppeteer-land each mean writing and hosting an HTML page first. Output lands on our CDN with a stable URL, replacing the S3 upload step.
Full programmability. Clicking, typing, waiting on arbitrary conditions, intercepting requests, running behind logins — a real automation API that an image endpoint does not attempt to be.
No per-render cost and no external dependency. At sustained very high volume with engineering time already sunk into a solid pool, the marginal render is nearly free and everything stays inside your network. You pay in upkeep instead of credits.
// Puppeteer — plus Chrome, hosting and upkeep
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 720 });
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
await page.screenshot({ path: 'shot.png' });
await browser.close();
// ...then upload shot.png somewhere yourself # HTML to Image — same Chrome, no browser to run
curl -X POST https://app.html2img.com/api/screenshot \
-H 'X-API-Key: your-key-here' \
-H 'Content-Type: application/json' \
-d '{"url":"https://example.com","width":1280,"height":720}'
# Response includes a CDN-hosted URL
The mapping is direct. page.goto() becomes the url field, the viewport becomes width and height, page.setContent() becomes the HTML endpoint, and waitUntil or selector waits become ms_delay and wait_for_selector. Any language that can make an HTTP request qualifies — no Node.js required.
Puppeteer's sticker price is zero and its running cost is not. Budget for Chrome's memory appetite (500MB to 1GB per instance), the pool-and-queue code around it, cold-start pain on serverless, and a redeploy on every Chromium security release. If a developer spends even a few hours a month on that plumbing, it costs more than our 10,000-render plan. Self-hosting starts to pay for itself only at sustained high volume with the engineering already amortised — and if that is you, fair enough.
We have written up the sharp edges in detail:
Yes. Both render in headless Chrome, so the pixels match what page.screenshot() produces for the same markup and viewport. The difference is everything around the render: with the API there is no browser binary to ship, no --no-sandbox flags, no memory ceiling to tune and no zombie Chrome processes to reap.
The library is free; the total isn't. Chrome wants roughly 500MB to 1GB of memory per instance, Lambda deployments need special builds like @sparticuz/chromium that lag Chrome releases and inflate cold starts, and every Chromium security release is a redeploy. Most teams also end up writing pooling, retry and timeout logic. Our article on replacing Puppeteer on AWS Lambda walks through the full list. Against that engineering time, $9 a month for 1,000 renders is usually the cheap option.
When you need a programmable browser rather than an image. Multi-step interactions, logging into apps, crawling, PDF pipelines with custom logic, or integration testing — that is what Puppeteer is for, and an image API does not replace it. If screenshots are a side-effect of automation you are already running, keep them in Puppeteer.
Map the page.goto() URL and viewport to the Screenshot endpoint's url, width and height fields, or send the HTML you were feeding page.setContent() to the HTML endpoint. waitUntil and selector waits map to our ms_delay and wait_for_selector parameters. The response is a CDN URL, which usually replaces your S3 upload step too.
The free tier covers 25 renders a month with no credit card. Point your existing Puppeteer job at the API and compare the output.
Jobs you'd otherwise hand-build pages for. Twenty-five pre-built designs rendered from JSON — no HTML, no browser, no hosting.