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

To read JSON returned by a browser action, register a waitForResponse before the click or other action that triggers the request, await the matching response, check its HTTP status, and then call await response.json(). Use Playwright’s APIRequestContext instead when you want to call an API directly rather than inspect traffic from a page.

Get JSON from a page response

The pattern is the same in Puppeteer and Playwright: create the response-wait promise first, trigger the action, await the response, check the status, and parse the body. Waiting first matters because a fast request could finish before a later wait is registered.

Puppeteer: wait for a response after a click

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

await page.click('button#load-items');
const response = await responsePromise;
const data = await response.json();

console.log(data);

page.waitForResponse accepts a URL or a predicate and resolves with the matching HTTPResponse. The predicate above narrows the match by endpoint and status; include a request-method condition if the page can call that endpoint in multiple ways. Puppeteer Page.waitForResponse documentation and HTTPResponse documentation describe the wait and response methods.

Playwright: wait for a response after a click

const responsePromise = page.waitForResponse(response =>
  response.url().includes('/api/items') &&
  response.status() === 200 &&
  response.request().method() === 'GET'
);

await page.getByRole('button', { name: 'Load items' }).click();
const response = await responsePromise;
const data = await response.json();

console.log(data);

Playwright’s matcher may be a URL string, regular expression, or predicate. A predicate is useful when the URL alone is not specific enough; this example also checks status and method. See the Playwright Page.waitForResponse documentation.

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

Check status and parsing separately

A response with a 404 or 503 is still an HTTP response, and a body that parses as JSON does not prove the request succeeded. Check response.ok() or response.status() before treating the parsed value as successful data. Both Puppeteer’s HTTPResponse.json() and Playwright’s page Response.json() parse the body; parsing fails if the content is not valid JSON.

const response = await responsePromise;

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

let data;
try {
  data = await response.json();
} catch (error) {
  const body = await response.text();
  throw new Error(`Response was not valid JSON: ${body.slice(0, 300)}`, { cause: error });
}

Use the error handling appropriate to your runtime if it does not support the cause option for Error. The relevant response APIs are documented for Puppeteer and Playwright.

Call an API directly with Playwright

If no rendered page needs to make the request, use Playwright’s APIRequestContext. Its APIResponse is a different type from the Response produced by page traffic.

const response = await request.get('/api/items');

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

const data = await response.json();
console.log(data);

An APIResponse also provides body(), text(), status(), and ok(). Its body remains in memory until the request context closes; in a long-running workflow, dispose responses when you no longer need them so their bodies can be released sooner. See Playwright APIResponse documentation.

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

Choose the right way to capture the response

Need Use Why
Read one response caused by a click or other page action waitForResponse Registers a focused wait for the response the action should produce.
Observe responses broadly as they arrive Playwright page.on('response', handler) The event receives responses as status and headers arrive; it is suited to monitoring rather than waiting for one known interaction.
Call an API without relying on page behavior Playwright APIRequestContext Returns an APIResponse directly, rather than a page Response.
Stub, abort, or fulfill requests in Puppeteer Request interception Interception changes request flow; it is not necessary for passively reading a real response.

Troubleshoot response capture and JSON parsing

  • The wait times out. Make sure the wait is created before the action. Then narrow the predicate around a stable endpoint path and, when useful, the method and expected status. A broad matcher can miss the intended response among unrelated page traffic.
  • The response is 404 or 503. This is an HTTP response, not necessarily a transport failure. Inspect its status and body; do not assume that receiving a response means the operation succeeded.
  • The request fails without a response. In Playwright, requestfailed concerns cases where the client cannot get an HTTP response, such as a network error. Handle that separately from checking an HTTP response’s status.
  • response.json() throws. The body may be HTML (for example, an error page), empty, or another non-JSON format. Inspect response.text() and response headers to diagnose the actual content.
  • Interception stalls requests. In Puppeteer, enabling interception means each request must be continued, fulfilled, aborted, or satisfied from cache. The Puppeteer project’s Request Interception guide warns: “Once request interception is enabled, every request will stall unless it’s continued, responded or aborted.” Ensure every intercepted request has a resolving handler; do not enable interception just to read a response body.

Or skip the browser setup

If you need an image or PDF of a page rather than the JSON body of its network response, ScreenshotNeo is a website screenshot API and MCP server. Its one-call endpoint returns a PNG, JPEG, WebP, or PDF; it does not replace waitForResponse when the task is to read page JSON.

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

See the ScreenshotNeo API documentation. It removes known cookie-consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are not billed. Its MCP server lets AI agents take screenshots, and the free plan includes 1,000 shots a month without a card; paid plans start at $5 for 3,000 shots. Sign up for ScreenshotNeo free.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Frequently Asked Questions

Does response.json() return a string?

No. It returns the parsed JSON value, such as an object, array, string, number, boolean, or null.

Can I read the same response body with text() after calling json()?

Response bodies are generally consumed when read. If you need raw text for diagnosis, read it instead of calling json(), or capture the text first and parse it yourself.

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.

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.