ScreenshotNeo

BlogHow-to

Convert a URL to a JPEG with Puppeteer

Capture a URL as a JPEG with Puppeteer. Configure quality and page extent, wait for content, save the image or use its bytes, and troubleshoot common issues.

By the ScreenshotNeo team4 October 20266 min read

Use Puppeteer’s page.screenshot() method: navigate to the URL, set type: 'jpeg', and provide a .jpg path. The example below captures the full page at JPEG quality 85. Adjust the wait condition and quality for the site and image you need.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'networkidle2' });
  await page.screenshot({
    path: 'page.jpg',
    type: 'jpeg',
    quality: 85,
    fullPage: true,
  });
} finally {
  await browser.close();
}

The example uses ECMAScript modules. Install Puppeteer with npm install puppeteer, save the code in a file such as screenshot.mjs, and run node screenshot.mjs. Puppeteer’s package downloads a compatible browser as part of its normal installation workflow. See the official screenshots guide.

1. Choose what to capture

By default, Puppeteer captures the current viewport. Set fullPage: true to capture the full page extent. You can instead specify a clip rectangle to capture a region. A full-page screenshot can be very tall, so consider its pixel dimensions and memory use before capturing long pages.

Need Option
Visible viewport Omit fullPage or set it to false.
Entire page Set fullPage: true.
A specific rectangle Pass a clip rectangle in the screenshot options.
A specific element Find it with page.$() and call its screenshot() method.

For an element capture, the handle’s screenshot method scrolls the element into view if necessary:

const card = await page.$('.product-card');
if (!card) throw new Error('Could not find .product-card');
await card.screenshot({ path: 'card.jpg', type: 'jpeg', quality: 85 });

For region-based capture, define clip with the rectangle’s x, y, width, and height. If the area extends outside the viewport, review the captureBeyondViewport option in the ScreenshotOptions API.

2. Set JPEG format and quality

Set type: 'jpeg' to request JPEG. When saving to a path, Puppeteer can also infer the format from a .jpg or .jpeg extension. Stating the type explicitly makes the intended output clear.

The quality option accepts a value from 0 to 100. Higher values generally preserve more image detail and create larger files; lower values trade detail for smaller output. Quality does not apply to PNG. The example’s value of 85 is a starting point, not a universal optimum. Compare the output at the size and detail level your application needs.

3. Wait for the page content you need

page.goto() has navigation wait conditions, including networkidle2, which Puppeteer’s guide uses in an example. A navigation condition says something about the load process; it does not guarantee that a site’s client-rendered content, images, or animations are ready for your capture.

Choose a condition that fits the target. If the screenshot depends on a particular component, wait for that component explicitly:

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('.report-ready');
await page.screenshot({ path: 'report.jpg', type: 'jpeg', quality: 85 });

If a page never becomes idle because it keeps long-lived connections open, a network-idle wait may not be appropriate. Wait for a meaningful selector or use a suitable navigation condition, then add a page-specific wait. For lazy-loaded content, a full-page option alone may not cause every site to load all content; the page may need scrolling or application-specific readiness logic first.

4. Save to disk or use the returned image

When you pass path, Puppeteer writes the screenshot to that file. Without path, page.screenshot() returns image data as a Uint8Array by default. You can write those bytes yourself or pass them to another part of your application:

import { writeFile } from 'node:fs/promises';

const imageBytes = await page.screenshot({ type: 'jpeg', quality: 85 });
await writeFile('page.jpg', imageBytes);

Screenshot options also support requesting a base64 string. Consult the Page.screenshot() API for the current return and encoding details. A path is convenient for a local file; returned bytes are useful when the next step is an upload, response, or in-memory transformation.

5. Complete runnable examples

cURL

cURL does not launch Puppeteer or render a web page. It can only fetch an image that a service generates. If you need Puppeteer specifically, use the Node.js examples above. To request a JPEG from ScreenshotNeo, use its screenshot API:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

Python’s requests library also does not run Puppeteer. To use Puppeteer, run the Node.js script. For a managed screenshot request with ScreenshotNeo:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

Node.js and Puppeteer

This is the direct Puppeteer implementation. It closes the browser even if navigation or capture fails:

import puppeteer from 'puppeteer';

const url = process.argv[2] ?? 'https://example.com';
const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto(url, { waitUntil: 'networkidle2' });
  await page.screenshot({
    path: 'page.jpg',
    type: 'jpeg',
    quality: 85,
    fullPage: true,
  });
  console.log('Saved page.jpg');
} finally {
  await browser.close();
}

Run it with node screenshot.mjs https://example.com. The returned file is JPEG because the screenshot type is explicit.

6. Troubleshoot common problems

Symptom Likely cause What to do
Browser executable missing The Puppeteer browser was not installed or the environment cannot find it. Install Puppeteer using the project’s package manager and follow its browser installation guidance. Check whether your deployment environment requires a separately configured browser.
Navigation times out The page is slow, has persistent network activity, or cannot be reached from the runtime. Check URL and network access. Choose a navigation wait condition suited to the page, set an appropriate timeout, and wait for the specific content needed instead of relying on network idle.
Screenshot is blank or missing content The capture ran before client-rendered content or images appeared. Wait for a page-specific selector or readiness signal. For lazy content, trigger the loading behavior before capture.
File is PNG instead of JPEG The screenshot type was not specified and the output path did not indicate JPEG. Set type: 'jpeg' and use a .jpg or .jpeg extension.
Quality option has no visible effect Quality is not applicable to PNG, or the difference is hard to see at the displayed size. Confirm JPEG output and compare saved file sizes and image detail at the intended display size.
Capture is cut off The default captures the viewport, or the desired region was not included. Set fullPage: true for the full page, use an element screenshot, or define a clip rectangle.
Very tall capture consumes too much memory A full-page page can produce a large raster image. Capture a smaller region or element, reduce viewport dimensions where suitable, or process the page in sections.

7. Performance, reliability, and cost

With Puppeteer, your application is responsible for running the browser, navigating to each target, managing browser processes, and storing or forwarding the resulting bytes. Reuse a browser process for multiple captures when appropriate, while creating a separate page per task and closing pages and browsers reliably. Limit concurrent captures to the resources available in your runtime; large full-page screenshots and many simultaneous pages increase memory pressure.

For reliable captures, use explicit timeouts, wait for the content that matters, and close resources in a finally block. Treat a successful navigation as distinct from a visually complete application page. Puppeteer itself has no per-screenshot service charge, but running browsers consumes your compute, storage, and engineering time. The actual infrastructure cost depends on your runtime and workload.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. Make one request for a screenshot; see the API documentation for options and setup. The example below uses the provided API pattern:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up for 1,000 free screenshots a month, with no card.

FAQ

How do I save a Puppeteer screenshot as a JPG?

Call page.screenshot() with type: 'jpeg' and a path ending in .jpg or .jpeg.

Does Puppeteer capture the full page by default?

No. The default is the viewport. Set fullPage: true to capture the full page extent.

Can I screenshot one element instead of the page?

Yes. Select its element handle and call ElementHandle.screenshot(). See the ElementHandle screenshot API.

Is quality 85 always best?

No. It is an example value. Choose a quality by comparing image detail and file size for your use case.

References