How to Read Response Text in Puppeteer
Use Puppeteer’s HTTPResponse.text() to read a navigation or network response as UTF-8 text. Learn how to capture the right response and handle common edge cases.
To read a navigation response body in Puppeteer, await HTTPResponse.text() on the response returned by page.goto():
const response = await page.goto('https://example.com');
if (!response) {
throw new Error('Navigation did not produce an HTTP response');
}
const bodyText = await response.text();
console.log(bodyText);
text() returns the response body as a UTF-8 string. It is asynchronous, so you must await it. It can throw if the content is not a UTF-8 string. For JSON, use json(); for bytes, use buffer() or content(). See Puppeteer’s HTTPResponse API reference.
1. Read the response from page.goto()
page.goto() resolves with the main resource’s HTTP response when navigation produces one. Check for null before reading the body: navigations to about:blank or to the same URL with only a hash change can return no response.
const response = await page.goto('https://example.com', {
waitUntil: 'domcontentloaded',
});
if (response === null) {
console.log('No HTTP response was produced for this navigation.');
} else {
console.log('Status:', response.status());
console.log('URL:', response.url());
const text = await response.text();
console.log(text);
}
The waitUntil setting controls when navigation is considered complete; it does not change how text() reads the body. Puppeteer navigation options include lifecycle events such as load, domcontentloaded, and networkidle0/networkidle2. Pick the condition that fits the page and avoid waiting for network idle on sites with persistent requests.
2. Complete runnable Node.js example
Install Puppeteer in a Node.js project, then save this as read-response.js:
npm install puppeteer
const puppeteer = require('puppeteer');
(async () => {
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 returned no HTTP response');
}
console.log('Status:', response.status());
console.log('Content-Type:', response.headers()['content-type']);
console.log(await response.text());
} finally {
await browser.close();
}
})().catch(error => {
console.error(error);
process.exitCode = 1;
});
Run it with node read-response.js. Always close the browser in a finally block so a navigation or body-read error does not leave the browser process running.
3. Read a response after clicking a link
If a click causes a navigation, register the navigation wait and perform the click together with Promise.all(). This prevents the script from missing the navigation that the click starts:
const [response] = await Promise.all([
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
page.click('a.some-link'),
]);
if (response) {
console.log('Status:', response.status());
console.log(await response.text());
} else {
console.log('The navigation did not produce an HTTP response.');
}
Replace a.some-link with a selector that identifies the link. A click that changes only the URL hash may not cause a document navigation, so the response can be null. The same concurrent-wait pattern applies when another action triggers navigation. See the Puppeteer Page documentation.
4. Read a matching response from network traffic
Use the page’s response event when you need a response from an API request or another resource, rather than the main document navigation. Filter by URL (and, if needed, status or headers) so you do not read every image, script, and stylesheet on the page.
page.on('response', async response => {
if (!response.url().includes('/api/items')) return;
try {
const bodyText = await response.text();
console.log('Status:', response.status());
console.log('Body:', bodyText);
} catch (error) {
console.error('Could not read response body:', response.url(), error);
}
});
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
The listener runs for matching responses as they arrive. If your script needs to wait for a particular response and then use it later, create the wait before triggering the request:
const responsePromise = page.waitForResponse(response =>
response.url().includes('/api/items')
);
await page.click('button.load-items');
const response = await responsePromise;
console.log(await response.text());
Set a timeout or catch the wait’s rejection if the request might not happen. The PageEvent documentation describes the response event and its HTTPResponse value.
5. Choose the right body reader
| Method | Use it for | Result | Watch for |
|---|---|---|---|
text() |
Text content you want to inspect or print | UTF-8 string | It throws when content is not a UTF-8 string. |
json() |
A JSON response body | Parsed JavaScript value | The body must contain valid JSON. |
buffer() |
Binary data or bytes you need to process | Buffer | The browser may re-encode data based on headers or heuristics. |
content() |
Buffer data through the alternative body method | Buffer | Use a buffer method when a string is not the right representation. |
Example for JSON:
const response = await page.waitForResponse(r =>
r.url().includes('/api/items')
);
const data = await response.json();
console.log(data);
Example for bytes:
const response = await page.goto('https://example.com/file');
if (!response) throw new Error('No response');
const bytes = await response.buffer();
console.log('Bytes:', bytes.length);
6. Check status separately from reading the body
An HTTP status such as 404 or 503 can still come with a completed HTTP response and a readable body. Check status() or ok() to decide whether the server response represents success; do not assume that a non-2xx status means Puppeteer emitted a transport failure.
const response = await page.goto('https://example.com/missing');
if (!response) {
throw new Error('No HTTP response');
}
console.log('HTTP status:', response.status());
console.log('HTTP success:', response.ok());
console.log('Response body:', await response.text());
A failed network request and an HTTP error response are different cases. The Puppeteer API documentation distinguishes HTTP error statuses from request failures; inspect the response status for the former and request lifecycle events for the latter. See the Puppeteer API documentation index.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
response is null |
The navigation did not produce a document response, such as a hash-only change or about:blank. |
Check for null; use a response event or wait for the actual request if you need a resource body. |
text() throws |
The body is not available as a UTF-8 string, or the response body could not be read. | Catch the error. For binary content use buffer() or content(); confirm you are reading the intended response. |
| Body is empty or unexpected | You captured the wrong response, or the endpoint returned an empty body. | Log url(), status(), and headers; filter network responses more narrowly and inspect the server response. |
| JSON parsing fails | The response is not valid JSON, often because an error page or other content was returned. | Check status and content type, then read with text() to inspect the actual body. |
| The click wait hangs or times out | The action did not cause a document navigation, or navigation is waiting on an unsuitable lifecycle condition. | Use waitForResponse() for an API call; select an appropriate navigation condition and verify the selector/action. |
| Script reports network failure for a 404 | HTTP status failure is being confused with transport failure. | Inspect response.status() and response.ok(); a 404 response can still have a body. |
8. Performance, reliability, and cost
Reading a response body means retaining and decoding its contents, so avoid collecting bodies for every network resource on pages with many large assets. Filter by endpoint before calling text(), and prefer json() when the next step needs parsed JSON. Close the browser and pages when finished, and use explicit timeouts for navigation and response waits so a missing request cannot stall a job indefinitely.
Puppeteer runs a browser that your application must launch and manage. Its resource use and operating cost depend on your runtime, concurrency, page size, and how much response data you retain; the cited API documentation does not provide a universal cost or performance benchmark. If you only need a rendered screenshot or PDF rather than response-body inspection, a screenshot API can avoid maintaining browser setup for that capture workflow.
9. Or skip the browser setup
If the task is to capture a clean page image or PDF, ScreenshotNeo returns it from one GET request. Its API supports PNG, JPEG, WebP, or PDF, with options such as full-page capture, selector capture, viewport and device presets, wait conditions, custom headers, and cookies. The ScreenshotNeo API docs list the parameters; the examples below use the documented endpoint and request shape.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
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
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const bytes = Buffer.from(await res.arrayBuffer());
await require('node:fs/promises').writeFile('shot.webp', bytes);
Cookie and consent banners are accepted and more than 60 known consent platforms, newsletter popups, and chat widgets are removed before the shot; each of those steps can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing status in headers. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots, and every feature is on every plan. Sign up free for 1,000 screenshots a month, with no card required.
10. FAQ
Does response.text() return the rendered page text?
No. It returns the HTTP response body. For text currently visible in the rendered DOM, query the page’s elements instead.
Can I read a response after the page has navigated again?
Read and retain the body when the relevant response arrives. For network traffic, match the URL with waitForResponse() or a response listener before the action that triggers it.
Does a successful body read mean the HTTP request succeeded?
No. Check status() or ok() independently; error-status responses can still have readable bodies.
Which Puppeteer version should I use?
Use the API available in your project’s installed version and consult its matching documentation. The references here show Puppeteer’s current API documentation at research time.


