How to Read JSON from a Puppeteer Response
Use `await response.json()` to parse a Puppeteer HTTP response. Learn how to capture the right response, check status codes, and diagnose parse failures.
To read JSON from a Puppeteer HTTPResponse, call await response.json(). It parses the response body and returns the resulting JavaScript value. Check the HTTP status separately: a 404 or 500 response can still arrive normally, and parsing fails if the body is not valid JSON.
Parse a navigation response
page.goto() returns the response for the main navigation resource when one exists. It can return null for navigations such as about:blank or a same-URL hash change, so guard the result before reading it.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
const response = await page.goto('https://example.com/data');
if (!response) {
throw new Error('Navigation did not provide an HTTP response');
}
console.log('URL:', response.url());
console.log('Status:', response.status(), response.statusText());
console.log('Content-Type:', response.headers()['content-type']);
if (!response.ok()) {
throw new Error(`HTTP ${response.status()}: ${response.statusText()}`);
}
const data = await response.json();
console.log(data);
} finally {
await browser.close();
}
})();
Save this as read-response.js, install Puppeteer in your project with npm install puppeteer, then run node read-response.js. Replace the URL with an endpoint that returns JSON. The endpoint may require headers, cookies, or authentication; those details depend on that service.
The official Puppeteer HTTPResponse reference documents json(), text(), headers(), url(), and status methods. The Page.goto reference describes the navigation response and cases where there is no response. Puppeteer’s current reference identifies its API version as 25.12.0.
Capture JSON returned by a browser interaction
Use page.waitForResponse() when the page makes a background request after an interaction. Create the wait promise before clicking or performing the action that triggers the request; otherwise, a fast response may arrive before Puppeteer starts waiting.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com/dashboard');
const responsePromise = page.waitForResponse(response =>
response.url().includes('/api/items') &&
response.request().method() === 'GET'
);
await page.click('button.load-items');
const response = await responsePromise;
if (!response.ok()) {
throw new Error(`HTTP ${response.status()}: ${response.statusText()}`);
}
const items = await response.json();
console.log(items);
} finally {
await browser.close();
}
})();
Replace the page URL, endpoint fragment, HTTP method, and selector with values for your application. Match enough identifying details to avoid selecting unrelated requests with similar URLs. waitForResponse() also accepts a URL or a predicate, and supports timeout and cancellation options. See the Puppeteer waitForResponse reference.
Choose the right response
| Need | Use | Watch for |
|---|---|---|
| The main document or resource loaded by navigation | await page.goto(url) |
The result may be null for certain navigations. A document response may be HTML rather than JSON. |
| A specific API response caused by a click or other page action | page.waitForResponse(predicate) |
Register the wait before the action and filter by endpoint, method, or other stable request details. |
A response object means an HTTP response completed; it does not guarantee a successful status or JSON payload. Puppeteer’s ok() reports whether the status is in the 200–299 range. Inspect status(), statusText(), url(), and headers() when deciding how to handle the result.
What response.json() returns
json() resolves to the parsed JSON value. The result can be an object, array, string, number, boolean, or null, depending on the response body. It is not the same as JSON.stringify(), which serializes a JavaScript value into JSON text.
The method parses the body as JSON and throws if the body cannot be parsed by JSON.parse. It does not silently turn an HTML error page or malformed payload into an object. The method returns a promise, so use await or handle its rejection.
Inspect a response when parsing fails
Read the body as text to see what the server actually returned. Include response metadata in diagnostics, but avoid logging secrets or personal data if the payload may contain them.
async function readJsonWithDiagnostics(response) {
const metadata = {
url: response.url(),
status: response.status(),
statusText: response.statusText(),
headers: response.headers()
};
const bodyText = await response.text();
try {
return JSON.parse(bodyText);
} catch (error) {
console.error('Response was not valid JSON:', metadata);
console.error('Body preview:', bodyText.slice(0, 500));
throw error;
}
}
Use either response.json() or response.text() for a given response body, rather than assuming the body can be consumed repeatedly. The text method returns UTF-8 text and can itself throw if the content is not valid UTF-8. If you need to preserve a response for multiple kinds of inspection, decide on a single read path and retain the resulting text or parsed value.
Handle non-2xx responses deliberately
An HTTP error status does not necessarily mean there is no response body. A server may return JSON describing an error, or it may return an HTML error document. Decide whether your application should parse error JSON before throwing, or fail immediately on non-2xx status.
async function getJson(response) {
if (!response) {
throw new Error('No HTTP response was produced');
}
const status = response.status();
const contentType = response.headers()['content-type'] || '';
const bodyText = await response.text();
let body;
try {
body = JSON.parse(bodyText);
} catch {
body = bodyText;
}
if (!response.ok()) {
throw new Error(
`HTTP ${status} from ${response.url()}: ${
typeof body === 'string' ? body.slice(0, 300) : JSON.stringify(body)
}`
);
}
if (!contentType.toLowerCase().includes('json') || typeof body === 'string') {
throw new Error(`Expected JSON from ${response.url()}, got ${contentType || 'unknown content type'}`);
}
return body;
}
This example reads the body once as text and then parses it, which lets it report useful information for both successful and failed statuses. Content-Type is a useful diagnostic signal, not proof that a body is valid JSON; parsing remains the decisive check.
Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
response.json() throws a parse error |
The body is HTML, plain text, empty, malformed JSON, or another non-JSON payload. | Inspect await response.text(), status, URL, and headers. Check that the selected endpoint returns JSON. |
| You parsed a page document instead of API data | page.goto() gives the main navigation response, often an HTML document. |
Wait for the specific API request with page.waitForResponse() and a focused predicate. |
| The wrong request was captured | The predicate matched an image, script, preflight, or unrelated API call. | Filter by a stable URL path and request method; add other known conditions if needed. Set up the wait before the action. |
| Status is 404 or 500 | The server completed an HTTP error response. Its body may or may not be JSON. | Check status() or ok() independently, then inspect the body and handle the error format the service returns. |
page.goto() returned null |
The navigation did not produce a response object, as with certain special or hash-only navigations. | Guard the return value. If the data is from a background call, wait for that request instead. |
| The request failed before there was a response | A network failure prevented normal completion; this differs from an HTTP error response. | Inspect the relevant request lifecycle, including requestfailed versus requestfinished, and diagnose connectivity or the target request. |
response.text() also fails |
The body could not be decoded as UTF-8 text. | Check the response encoding and server payload. Puppeteer documents text() as UTF-8 text. |
Puppeteer distinguishes request failure from a completed request and response status. A 404 is still an HTTP response; a network failure may mean there is no HTTP response to parse. See the HTTP request reference for request lifecycle methods and events.
Reliability and performance notes
- Match narrowly. Broad URL predicates can capture the wrong response when a page loads many resources. Include the endpoint path and request method when those are known.
- Set waits before actions. This avoids missing a response that completes quickly.
- Use timeouts intentionally. A request may not occur because the page is in a different state, a click failed, or the application skipped the call. Set a timeout appropriate to the page and handle timeout errors with context.
- Do not infer payload format from status. A successful response can contain invalid JSON, and an error response can contain valid JSON.
- Read once and retain results. Parse and store the value you need, or read text once and parse it yourself when richer diagnostics are required.
- Keep browser work bounded. Close pages and browsers in cleanup paths, as in the examples. Avoid launching a new browser for every individual response if your surrounding application can safely reuse a managed browser.
Parsing itself is usually not the expensive part of browser automation; navigation, page scripts, and network activity can dominate. Actual latency and resource use depend on the page and runtime, so measure your own workflow rather than relying on a universal timing estimate.
Or skip the browser setup
If you need a screenshot of the page rather than its API data, ScreenshotNeo provides a website screenshot API and MCP server. A single GET request returns an image or PDF. The screenshot API is documented at ScreenshotNeo docs.
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}`);
- Cookie and consent banners are accepted like a visitor, then removed along with 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off.
- Bot checks, blank pages, timeouts, and failed loads are not billed; cache hits are also free. Response headers report the page verdict and billing status.
- An MCP server lets AI agents use
take_screenshot,get_page_info, andcapture_pdf. - 1,000 screenshots a month are free with no card. Paid plans start at $5 for 3,000 screenshots. All features are on every plan.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
FAQ
Does response.json() return a string or an object?
It returns the JavaScript value represented by the JSON body. JSON can represent arrays and primitive values as well as objects.
Can I use response.json() in page.evaluate()?
The examples here parse Puppeteer’s Node-side HTTPResponse. A browser-side fetch() has its own Fetch API response object and execution context; keep those two response objects distinct.
Does response.ok() verify the JSON?
No. It indicates a 2xx HTTP status. The body still has to be parsed, and parsing can fail.
Where can I confirm Puppeteer API behavior?
Use the official API references for HTTPResponse, Page.goto, and Page.waitForResponse.


