Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
Browser Testing

How to Access a Specific Network Response as JSON With Puppeteer

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use page.waitForResponse() to create a response promise before the click or other action that triggers the request. Await the action, await the matched HTTPResponse, and call response.json():

const responsePromise = page.waitForResponse(
  response =>
    response.url().includes('/api/data') && response.status() === 200
);

await page.click('button');
const response = await responsePromise;
const data = await response.json();

console.log(data);

This pattern avoids a race with fast requests and gives your Node.js code the parsed JSON object returned by the page’s network request.

What waitForResponse() returns

Puppeteer’s Page.waitForResponse() returns a promise that resolves to the matching HTTPResponse. You can match with an exact URL string or with a synchronous or asynchronous predicate. Once you have the response, await response.json() parses its body.

The current official API reference surfaced for this method is Puppeteer 25.12.0. Its documented default timeout is 30 seconds. You can change the default with page.setDefaultTimeout(), set a per-wait timeout, use timeout: 0 to disable the wait timeout, and supply an AbortSignal when cancellation is needed.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Complete example: click, capture, and parse JSON

The following runnable example launches Chromium, opens a page, waits for the API response, and validates the resulting object before using it.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();

  try {
    await page.goto('https://example.com/dashboard', {
      waitUntil: 'domcontentloaded'
    });

    // Register this before the action that starts the request.
    const responsePromise = page.waitForResponse(
      response => {
        const request = response.request();
        return response.url().includes('/api/data') &&
          request.method() === 'GET' &&
          response.status() === 200;
      },
      { timeout: 30000 }
    );

    await page.click('[data-testid="load-data"]');

    const response = await responsePromise;
    const data = await response.json();

    if (!data || typeof data !== 'object') {
      throw new Error('The endpoint did not return the expected JSON object');
    }

    console.log(data);
  } finally {
    await browser.close();
  }
})();

The important ordering is deliberate: constructing responsePromise starts waiting immediately, while the subsequent click triggers the request.

Choose the right matcher

Exact URL

If the endpoint is stable and unique, pass its URL directly:

const responsePromise = page.waitForResponse(
  'https://example.com/resource'
);
await page.click('button');
const response = await responsePromise;
const data = await response.json();

An exact URL is easy to read, but it can break when the site adds query parameters, changes hosts between environments, or appends a cache-busting value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Predicate for URL, status, and method

A predicate is usually safer when several requests have similar paths. Check the URL and status together, and include the request method when GET, POST, or another method matters:

const responsePromise = page.waitForResponse(response => {
  const request = response.request();
  const url = new URL(response.url());
  return url.pathname === '/api/orders' &&
    url.searchParams.get('customer') === '42' &&
    request.method() === 'POST' &&
    response.status() === 201;
});

Matching a URL is only selection. It is not proof that the body is the application object you expect, so validate required fields after parsing.

Asynchronous predicates

The predicate may be asynchronous when you need to inspect response text before deciding whether it is the right response:

const responsePromise = page.waitForResponse(async response => {
  if (!response.url().includes('/api/search') || response.status() !== 200) {
    return false;
  }

  try {
    const text = await response.text();
    return text.includes('"resultType":"customer"');
  } catch {
    return false;
  }
});

Use this sparingly. Reading and parsing a body in the predicate can add work, and you still need to parse or otherwise process the selected response afterward according to your application contract.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Parse and validate the body safely

response.json() can reject when the selected response is not valid JSON. An HTTP 4xx or 5xx response may also contain an error schema rather than the success object. Keep selection, parsing, and validation separate:

const response = await responsePromise;

if (response.status() < 200 || response.status() >= 300) {
  throw new Error(`API returned HTTP ${response.status()}`);
}

let data;
try {
  data = await response.json();
} catch (error) {
  throw new Error(`Expected JSON from ${response.url()}: ${error.message}`);
}

if (!Array.isArray(data.items)) {
  throw new Error('JSON did not contain an items array');
}

for (const item of data.items) {
  console.log(item);
}

Do not assume every observed response has a readable JSON body. A response can be an HTML error page, an empty body, or a payload that does not match your endpoint’s documented contract.

Timeouts, cancellation, and slow pages

Increase or disable the wait timeout

The documented default is 30 seconds. Set a longer per-call timeout for a legitimately slow operation:

const responsePromise = page.waitForResponse(
  response => response.url().includes('/api/report'),
  { timeout: 90000 }
);

You can set a page-wide default for subsequent waits:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
page.setDefaultTimeout(60000);

timeout: 0 disables the response wait timeout, but use it only when another watchdog or cancellation mechanism guarantees that a hung page cannot leave your job waiting forever.

Abort a wait

When your job has its own deadline, pass an abort signal:

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 45000);

try {
  const response = await page.waitForResponse(
    response => response.url().includes('/api/data'),
    { signal: controller.signal }
  );
  console.log(await response.json());
} finally {
  clearTimeout(timer);
}

When an event listener is a better fit

page.on('response', handler) is useful for observing many responses, collecting diagnostics, or recording traffic over time. It is not a return-value API: registering a handler does not give the matching response to the await at the registration line.

const seen = [];
const handler = response => {
  if (response.url().includes('/api/data')) {
    seen.push(response);
  }
};

page.on('response', handler);
try {
  await page.click('button');
  await new Promise(resolve => setTimeout(resolve, 1000));
  for (const response of seen) {
    console.log(response.url(), response.status());
  }
} finally {
  page.off('response', handler);
}

For one known response that gates the next step, waitForResponse() is simpler and automatically expresses the one-shot intent. If you use an event listener, store the result or promise explicitly and always remove the listener when monitoring ends.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
The SQL Programming Language: .
  • Used Book in Good Condition
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting common failures

“Navigation timeout” or “response wait timeout”

  • Cause: the action did not run, the selector matched no usable element, the endpoint changed, or the request took longer than 30 seconds.
  • Fix: create the wait before the action, verify the selector, log observed response URLs, and set a justified timeout for slow operations.

The promise never resolves

  • Cause: the predicate is too broad in the wrong way (it never returns true), the request is made by a different frame or URL, or the click did not trigger a request.
  • Fix: temporarily use page.on('response', response => console.log(response.url(), response.status())) to discover the actual traffic, then tighten the matcher.

The wrong response is selected

  • Cause: a path substring matches polling, prefetch, or multiple API calls.
  • Fix: compare the full URL or pathname and query parameters, request method, and status; validate a distinguishing JSON field.

json() fails

  • Cause: the response is HTML, empty, compressed or otherwise not valid JSON for the selected endpoint, or it represents an error payload.
  • Fix: inspect status and content expectations, catch the parse error, and use response.text() while diagnosing the actual body.

The response arrives before the wait is installed

  • Cause: code performed await page.click() first and only then called waitForResponse().
  • Fix: construct the promise first, then await the action and the promise.

Performance and reliability practices

  • Match the narrowest stable endpoint rather than every response on the page.
  • Use one response wait per action when a single API result controls the next step.
  • Keep browser cleanup in a finally block so failures do not leave Chromium processes running.
  • Log the matched URL and status, not sensitive headers or tokens.
  • Validate the parsed shape at the boundary; downstream code should not have to guess whether it received success, error, or an unrelated object.
  • For repeated observation, prefer one managed listener and remove it rather than accumulating handlers across tests.

Or skip the browser setup

If your real goal is a clean image or PDF of a page rather than reading an API payload, ScreenshotNeo provides a single HTTP request. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server supplies take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

See the ScreenshotNeo documentation for parameters and authentication:

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

You get 1,000 screenshots per month free with no card. Paid plans start at $5 for 3,000 screenshots, and every feature is included on every plan. Create a free ScreenshotNeo account.

Minimal decision guide

Need Use
One known endpoint after one click waitForResponse() with an exact URL or precise predicate
Variable URL, status, or method checks A predicate that tests those properties
Inspection of many responses page.on('response') with explicit storage and cleanup
Parsed application data await response.json(), followed by status and schema validation

Frequently Asked Questions

Can I call waitForResponse() after clicking?

You should create the wait promise before clicking. Otherwise a fast response can arrive before Puppeteer starts monitoring it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Does a matching URL guarantee valid JSON?

No. The response may be an error, HTML, empty, or an unexpected JSON shape. Check status, parse with error handling, and validate required fields.

Should I use a response event or waitForResponse()?

Use waitForResponse() for one response that gates the next step. Use an event listener for ongoing observation, and remove it when finished.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.