ScreenshotNeo

BlogHow-to

How to Save a Puppeteer Response as a Buffer

Use Puppeteer’s `response.buffer()` to read a response body into a Node.js Buffer, then write it to disk or process it in memory.

By the ScreenshotNeo team4 October 20267 min read

Call and await Puppeteer’s HTTPResponse.buffer() method:

const body = await response.buffer();

It returns a Promise<Buffer> containing the response body in memory. It does not save a file automatically. To persist the result, pass the Buffer to Node.js filesystem APIs. Puppeteer also warns that the browser may re-encode the body based on response headers or other heuristics, so do not assume the Buffer always contains untouched wire bytes. See the official Puppeteer buffer() reference.

1. Capture and save a response body

This runnable example navigates to a URL, obtains the navigation response, checks its status, reads the body as a Buffer, and writes it to a file.

const puppeteer = require('puppeteer');
const fs = require('node:fs/promises');

async function main() {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    const response = await page.goto('https://example.com', {
      waitUntil: 'domcontentloaded',
      timeout: 30_000,
    });

    if (!response) {
      throw new Error('Navigation did not produce a main-resource response');
    }

    if (!response.ok()) {
      throw new Error(`Request failed with HTTP ${response.status()}`);
    }

    const body = await response.buffer();
    await fs.writeFile('response.html', body);
    console.log(`Saved ${body.length} bytes from ${response.url()}`);
  } finally {
    await browser.close();
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Install Puppeteer in your project with npm install puppeteer if it is not already installed. The response from page.goto() is for the main document request; it is not automatically the response for an image, script, API call, or other subresource.

Write the bytes to disk

fs.writeFile(path, body) accepts the Buffer directly. Choose the file extension based on the response content type or the resource you requested; the Buffer itself does not identify the file format. For example:

const contentType = response.headers()['content-type'] ?? '';
const body = await response.buffer();

if (contentType.includes('image/png')) {
  await fs.writeFile('response.png', body);
} else {
  await fs.writeFile('response.bin', body);
}

Keep the result in memory

You can also pass the Buffer to a parser, hash function, upload client, or other API that accepts Node.js Buffers. If you need a string, decode deliberately and use the expected character encoding; binary data should remain a Buffer.

2. Capture a particular request’s response

For a response other than the main navigation, listen for responses and select the one you need. Match on the request URL or another stable property, since a page can receive many responses during a load.

const puppeteer = require('puppeteer');
const fs = require('node:fs/promises');

async function main() {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    const targetUrl = 'https://example.com/data.json';
    const responsePromise = page.waitForResponse(
      (response) => response.url() === targetUrl,
      { timeout: 15_000 },
    );

    await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
    const response = await responsePromise;

    if (!response.ok()) {
      throw new Error(`Request failed with HTTP ${response.status()}`);
    }

    const body = await response.buffer();
    await fs.writeFile('data.json', body);
  } finally {
    await browser.close();
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Register the response wait before triggering the action that causes the request. Otherwise a fast response can arrive before the listener is in place. Replace the URL predicate with a condition that uniquely identifies the request in your page.

3. Choose the right response representation

HTTPResponse offers several body-reading methods. Use the one that matches what the next step needs:

Method Result Use it when
buffer() Promise<Buffer> You need Node.js bytes, binary handling, or to write the body to a file.
content() Promise<Uint8Array> A typed byte array is suitable for the receiving API.
text() Promise<string> You want decoded UTF-8 text. Puppeteer documents an error if the content is not UTF-8.
json() Parsed JSON value You want JSON parsed as a JavaScript value. It can fail if the body cannot be parsed with JSON.parse.

These methods are documented in the Puppeteer HTTPResponse class reference. For example, use await response.json() when you need an object, but use await response.buffer() if you need to preserve a binary representation for downstream processing.

4. Check response status and metadata

Reading a body and deciding whether the request succeeded are separate tasks. Check the response metadata explicitly when your application needs to reject HTTP errors:

const status = response.status();
const succeeded = response.ok();
const headers = response.headers();
const request = response.request();
const finalUrl = response.url();

if (!succeeded) {
  throw new Error(`HTTP ${status} for ${finalUrl}`);
}

const body = await response.buffer();

A response body may still be available for a non-success status, which can be useful for diagnostics. Decide whether to save or inspect that body before throwing if your workflow needs error details. The methods status(), ok(), headers(), request(), and url() are part of the documented HTTPResponse API.

5. Edge cases, performance, and reliability

Buffer means memory use

The complete body is materialized as a Buffer in your Node.js process. Large responses therefore require memory for the body, in addition to the browser’s own memory and any copies made by your code or libraries. Avoid collecting many large response bodies concurrently unless your process has capacity for them.

Not guaranteed to be raw wire bytes

Puppeteer’s reference cautions that the browser can re-encode a response body according to HTTP headers or heuristics. If byte-for-byte preservation of a specific wire representation is a requirement, verify that the browser-provided body is appropriate for that requirement. Do not describe buffer() as guaranteed to return untouched network bytes.

A navigation operation may not yield the response you expect in every navigation scenario. Handle a null response before calling buffer(), and use a response listener when you are targeting a specific request.

Wait strategy affects when your code proceeds

The example uses domcontentloaded so it does not wait for every dependent resource. Choose the navigation or response wait that matches the event you need. A response wait can time out if the action never triggers the request or the predicate does not match.

Control concurrency and close the browser

Use try/finally around browser lifetime so errors during navigation, body retrieval, or file writing do not leave the browser running. For batch work, limit the number of simultaneous pages and body reads according to available memory and target-site behavior.

6. Troubleshooting

Symptom Likely cause Fix
response.buffer is not a function The value is not a Puppeteer HTTPResponse, or the installed API/version differs from the example. Check where the response value came from and consult the reference for your installed Puppeteer release.
Cannot read properties of null page.goto() did not return a response, but the code called a method anyway. Check for a missing response before calling buffer(); use a response event if you need a particular request.
The saved file is empty or unexpected The selected response may not be the resource you intended, or the server returned an empty/error response. Inspect response.url(), status(), headers(), and the request; ensure your predicate uniquely matches the target.
Response wait times out The triggering action did not run, the request URL differs, or the response arrived before the listener was registered. Register the wait first, then trigger navigation or interaction; loosen or correct the predicate and use an appropriate timeout.
text() fails on a binary response The body is not valid UTF-8 text. Use buffer() or content() for bytes.
json() fails The body is not valid JSON, possibly because it is an error page or another content type. Check status and content type, then inspect the body as text or bytes when appropriate.
Memory use grows during capture Large buffers or too many concurrent responses are retained. Write or process each body promptly, release references, and reduce concurrency.

7. Or skip the browser setup

If your actual goal is a page screenshot rather than retrieving an HTTP response body, ScreenshotNeo provides a website screenshot API. One GET request returns an image or PDF. See the API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
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 accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the page verdict and billing status in headers. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 shots a month with no card; paid plans start at $5 for 3,000 shots. Sign up for 1,000 free screenshots a month, with no card required.

8. FAQ

Does response.buffer() save a file?

No. It gives you a Buffer in memory. Use a filesystem write operation such as fs.writeFile() to save it.

Can I call buffer() more than once?

The API reference describes the method and its return type; if repeated reads matter to your workflow, verify behavior against the Puppeteer version you use and retain the first Buffer when practical.

Is this the same as taking a screenshot?

No. It reads the body of an HTTP response. A screenshot captures rendered page pixels; use a screenshot API or Puppeteer’s page screenshot functionality for that output.