Puppeteer screenshots without running Chromium.
The Puppeteer code is short. Running it in production is harder. Compare the two approaches, map each Puppeteer option to an API parameter, and see when your own browser is still the better choice.
The problem
page.screenshot() works locally, but in production you also need a Chromium build that fits your host, limits on concurrent pages, cookie-banner removal, lazy-load scrolling and checks that user-supplied URLs cannot reach private networks.
How Framejet handles it
Framejet runs Puppeteer on managed Chromium behind one GET endpoint. You send the URL and the options you would have passed to Puppeteer; it returns PNG or JPEG bytes, and failures cost no credit.
The Puppeteer version
Taking a screenshot with Puppeteer takes a few lines of code. This is the whole program:
import puppeteer from "puppeteer";
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 800, deviceScaleFactor: 2 });
await page.goto("https://example.com", { waitUntil: "networkidle2" });
await page.screenshot({ path: "shot.png", fullPage: true });
await browser.close();It works on a laptop. The work starts when the same code runs in production, for URLs you did not choose:
- Deploying Chromium. The full Chromium binary does not fit a typical serverless function, so you need a stripped build such as
@sparticuz/chromium, or a container you run and patch. - Memory and concurrency. A heavy page can use hundreds of megabytes. Without a limit on parallel pages, one slow page holds up every capture queued behind it.
- Cookie banners. A headless browser is always a first-time visitor, so many sites show it a consent dialog. Removing it reliably takes a script that you maintain as consent tools change.
- Lazy loading.
fullPage: truedoes not scroll. Images below the fold stay as placeholders unless you scroll the page first. - User-supplied URLs. If your users choose the target, your browser can be pointed at
localhostor a cloud metadata address. Every request, including redirects, has to be checked against private networks.
The same capture as one HTTP call
Framejet runs Puppeteer on managed Chromium and handles those five problems before returning the image. From Node.js, the call replaces the browser code:
const url = new URL("https://framejet.dev/v1/take");
url.search = new URLSearchParams({
url: "https://example.com",
width: "1280", height: "800", dpr: "2",
full_page: "true",
});
const res = await fetch(url, { headers: { "X-Api-Key": process.env.FRAMEJET_KEY } });
await fs.writeFile("shot.png", Buffer.from(await res.arrayBuffer()));No browser dependency ships with your app. A failed capture returns a JSON error and uses no credit.
Puppeteer options and their API parameters
| Puppeteer | Framejet parameter |
|---|---|
page.setViewport({ width, height }) | width, height |
deviceScaleFactor | dpr (1–3) |
page.screenshot({ fullPage: true }) | full_page=true |
page.screenshot({ type: "jpeg" }) | format=jpeg |
page.click(selector) | actions=click:<selector> |
page.type(selector, text) | actions=type:<selector>=<text> |
page.waitForSelector(selector) | actions=waitfor:<selector> |
a timed wait before capture | delay=<ms> or actions=wait:<ms> |
Selector steps run in one browser session before the capture. The capture-after-clicking guide covers them in detail.
When to keep Puppeteer
An API does not cover every case. Keep your own browser when you need:
- Logged-in or private pages. Framejet captures public HTTP and HTTPS URLs only. It does not accept cookies, custom headers or addresses on private networks.
- Arbitrary JavaScript. Actions are limited to click, type, wait-for-selector, wait and scroll.
page.evaluatehas no equivalent. - PDF or other output. Framejet returns PNG or JPEG.
- Very high, steady volume. If you already run browser infrastructure at full use, a per-capture price may cost more than your servers.
For everything else, compare the price with the time you spend on Chromium updates. The free plan includes 200 screenshots a month, enough to test your current Puppeteer targets side by side.
No browser to ship
Your app makes an HTTP request instead of bundling and updating Chromium.
Same options
Viewport, device scale, full page, format and selector steps map directly to query parameters.
Clear limits
Public URLs, PNG or JPEG, selector actions only. Keep Puppeteer for logged-in pages, custom scripts or PDFs.