How to Get the Frame for a Puppeteer Response
Use `response.frame()` to find the frame that initiated a Puppeteer response. Handle `null` responses and wait for the right request before reading its frame.
Call response.frame() on Puppeteer’s HTTPResponse. It returns the frame that initiated the response, or null for a navigation to an error page. Check for null before calling methods on the frame. See the Puppeteer HTTPResponse.frame() reference.
const response = await page.waitForResponse(response =>
response.url().includes('/api/data') && response.status() === 200
);
const frame = response.frame();
if (frame === null) {
console.log('The response is associated with an error-page navigation.');
} else {
console.log('Initiating frame URL:', frame.url());
}
1. Install Puppeteer and create a page
The examples below use JavaScript with Puppeteer. Install it in a Node.js project with npm install puppeteer. Puppeteer downloads a compatible browser as part of its standard installation. If your project already manages a browser separately, follow its existing launch configuration.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto('https://example.com');
// Wait for or listen to a response here.
} finally {
await browser.close();
}
})();
In an ES module project, use import puppeteer from 'puppeteer'; in place of the require line. The API behavior discussed here is documented for Puppeteer 25.12.0; check your installed version if behavior differs.
2. Wait for the response and get its frame
page.waitForResponse() resolves with the matching HTTPResponse. Give it a URL or a predicate function. A predicate is useful when you need to match multiple response properties, such as a URL path and a successful status.
const response = await page.waitForResponse(response =>
response.url().includes('/api/data') && response.status() === 200
);
const frame = response.frame();
if (!frame) {
// Handle the response's error-page navigation case.
return;
}
console.log({
responseUrl: response.url(),
frameUrl: frame.url(),
});
Use a distinctive path or other response properties to avoid matching an unrelated request. For example, a page may request the same endpoint more than once, or a matching URL may return an error status. Remove the status condition if you need to inspect failed responses too.
Start the wait before the action that triggers the request
If a click or other action causes the response, create the wait promise first. Then trigger the action and await the promise. This avoids missing a fast response before the wait has been registered.
const responsePromise = page.waitForResponse(response =>
response.url().includes('/api/data')
);
await page.click('button.load-data');
const response = await responsePromise;
const frame = response.frame();
if (frame !== null) {
console.log('Initiating frame URL:', frame.url());
}
Use a response event when you want to observe responses
For ongoing observation, attach a listener to the page’s response event. The callback receives an HTTPResponse, so call frame() on that object too. Keep the null check because a response associated with an error-page navigation may not have a frame.
page.on('response', response => {
const frame = response.frame();
if (frame !== null) {
console.log(response.url(), frame.url());
}
});
Use waitForResponse() when your code needs one matching response before continuing. Use an event listener when you need to observe many responses. Remove listeners when they are no longer needed in long-running processes.
3. Choose the API for the job
| Goal | API | What it gives you |
|---|---|---|
| Find the frame that initiated a response you already have | response.frame() |
The initiating Frame, or null for the documented error-page navigation case. |
| Wait for a frame matching a URL or predicate to appear | page.waitForFrame() |
A matching frame when it appears. It does not identify the initiating frame for a particular response. |
| Inspect the request associated with a response | response.request(), then request.frame() |
The matching HTTPRequest and its frame, subject to the same null condition. |
For the frame associated with a known response, use response.frame(). Use page.waitForFrame() when the frame itself is what you are waiting for, rather than a response.
4. Account for null and navigation edge cases
- Error-page navigation:
response.frame()can returnnull. Check the result before usingframe.url()or other frame methods, and decide whether your application should skip, retry, or report that case. - Request frame access:
response.request().frame()is another way to inspect the matching request’s frame. Its documentation describes the same null condition; it is not a way to avoid null handling. page.goto()may return null: Puppeteer documents a null return forabout:blankand same-URL hash navigation. Do not assume every navigation call gives you anHTTPResponseon which you can callframe().- Matching responses: A broad predicate can resolve on the wrong request. Match the endpoint precisely and include a status condition only when that reflects what you want to inspect.
- Timeouts: If no response matches, the wait rejects after its timeout. Handle that rejection if the request is optional or may not occur.
5. Configure timeouts and cancellation
The documented default timeout for page.waitForResponse() in Puppeteer 25.12.0 is 30 seconds. Set a page default timeout with page.setDefaultTimeout(), or pass a timeout for an individual wait. An AbortSignal can cancel the wait.
// Set a default timeout for page waits.
page.setDefaultTimeout(10_000);
// Or set a timeout for this wait only.
const response = await page.waitForResponse(
response => response.url().includes('/api/data'),
{ timeout: 15_000 }
);
Use a timeout that fits the operation and the environment. A longer timeout can accommodate a slow response, but it also delays failure when the request never happens. When you cancel a wait with an abort signal, handle the resulting rejection as part of your control flow. Check the API reference for the installed Puppeteer version if its available options differ.
6. Troubleshoot common problems
| Symptom | Likely cause | Fix |
|---|---|---|
| The wait times out | The request did not occur, the predicate does not match, or the wait started after the triggering action. | Register the wait first, inspect the URL and status in the predicate, and confirm the action actually triggers that request. Adjust the timeout if the response legitimately takes longer. |
frame.url() throws because frame is null |
The response is associated with an error-page navigation. | Check response.frame() for null before calling frame methods; decide how that case should be handled. |
| The returned frame is not the one expected | The predicate matched another response, perhaps from a different request or frame. | Match a more specific URL path and, where appropriate, response status or other response properties. Log response.url() and the frame URL to inspect the match. |
page.goto() did not provide a response |
The navigation was to about:blank or a same-URL hash. |
Account for a null navigation response before attempting response methods. If your goal is to wait for a frame, use the frame-waiting API for that separate task. |
| The event handler reports errors or keeps logging | The handler assumes every response has a frame, or remains registered longer than intended. | Use the null check in the listener and remove the listener when observation is complete. |
7. Performance, reliability, and cost
response.frame() is a lookup on the response object; the practical work is usually waiting for the right network response and ensuring the page action that triggers it succeeds. A precise predicate helps avoid continuing on an unrelated response. Register waits before triggering actions, choose a bounded timeout, and handle timeout and null cases explicitly for more predictable automation.
This Puppeteer workflow runs a browser and therefore has the operational costs of your browser environment, such as compute and runtime. The dossier provides no benchmark or numeric cost estimate for this operation, so size resources using your own workload and deployment measurements.
8. Or skip the browser setup
If the task is to capture a website rather than inspect a response’s initiating frame, ScreenshotNeo offers a one-request screenshot API. See the ScreenshotNeo 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 removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.
9. Frequently asked questions
Does response.frame() return the main frame?
It returns the frame that initiated that response. That may be a child frame, depending on which frame made the request.
Should I use response.frame() or page.waitForFrame()?
Use response.frame() to identify the initiator of a particular response. Use page.waitForFrame() to wait for a frame matching a URL or predicate.
Can I get the frame from the request instead?
Yes. Get the request with response.request() and call its frame() method. Handle its documented null case as well.
What if the response never arrives?
The wait times out unless you change its timeout or cancel it. Check that the action occurs and the predicate matches the response you expect.


