Framejet
Puppeteer or an API

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:

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

PuppeteerFramejet parameter
page.setViewport({ width, height })width, height
deviceScaleFactordpr (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 capturedelay=<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:

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.

Try it on the page your current script cannot reach. No card, 200 screenshots a month, and failures never spend a credit. Get a key.