What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
To measure the time from a browser action to a particular API response in Puppeteer, register page.waitForResponse() before triggering the request, start a monotonic timer at the exact point you want to measure from, and stop it when the matching response arrives. This action-to-response interval is different from browser resource timing and from the time until the response body has fully downloaded.
Choose the timing boundary first
“Response time” can mean several different intervals. Decide which one you need before writing the measurement:
As an Amazon Associate I earn from qualifying purchases.
- Action to response: elapsed time between a defined test action and receipt of the matching HTTP response. This includes browser-side action work and the time to response receipt; it is not pure server processing time.
- Resource timing: browser-reported timing data for the resource, available through
response.timing(). It is not an action-to-response stopwatch, and it may benull. - Request completion: time until the response body has finished downloading, represented by Puppeteer’s
requestfinishedevent. - Request failure: a request that fails at the network or browser level, represented by
requestfailed. This is distinct from an HTTP error status.
Puppeteer’s official API references consulted here are for version 25.12.0, except Page.metrics(), which is documented on the Next API pages. Check the documentation for your installed release if its API differs.
Recommended Free Tools
Measure an action-to-response interval
Create the response waiter before the action that triggers the request. Otherwise a fast response could arrive before Puppeteer begins waiting. Use a predicate that identifies the intended request rather than matching a broad URL shared by unrelated traffic.
#1 Best Overall
import { performance } from 'node:perf_hooks';
const responsePromise = page.waitForResponse(response =>
response.url().includes('/api/search') &&
response.request().method() === 'GET'
);
const startedAt = performance.now();
await page.locator('input[name="q"]').fill('puppeteer');
await page.locator('button[type="submit"]').click();
const response = await responsePromise;
const actionSequenceToResponseMs = performance.now() - startedAt;
console.log({
actionSequenceToResponseMs,
url: response.url(),
status: response.status(),
ok: response.ok(),
resourceTiming: response.timing(),
fromCache: response.fromCache(),
fromServiceWorker: response.fromServiceWorker(),
});
This interval starts before filling the search field, so its accurate label is action-sequence-to-response time. If you want the interval to begin immediately before the click, move startedAt to just before click(); keep the waiter registration before the click.
performance.now() provides a monotonic clock suitable for elapsed-time measurements. The result ends when the matching HTTPResponse is received, not when all response bytes have necessarily finished downloading.
Make the response match specific
The predicate above checks both a URL fragment and the request method. If several requests can still match, add stable details that distinguish them, such as a query parameter or a request body property. Avoid relying on transient details that can change between runs. waitForResponse() accepts a URL or predicate and resolves with the matching HTTPResponse.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsCheck HTTP success separately
An HTTP 404 or 503 is still a completed HTTP request in Puppeteer’s request lifecycle. Use response.status() or response.ok() to decide whether the application-level result counts as successful; ok() is true for statuses 200–299. Do not treat an HTTP error status as equivalent to requestfailed.
Rank #2
Read browser-reported resource timing
For browser timing details associated with the response, call response.timing(). Its documented return type is Protocol.Network.ResourceTiming | null. Handle the null case, and do not describe the returned resource timing as the elapsed time from a user action: those are different measurement surfaces.
The response object also exposes fromCache() and fromServiceWorker(). Record these when interpreting results, because a cache or service worker can change the path that produced the response.
Measure through full body download
If the question is how long the request takes until its response body is fully downloaded, use the request lifecycle’s completion event, not just response receipt. Capture the response and completion for the same request, and use one monotonic clock for both timestamps.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallimport { performance } from 'node:perf_hooks';
let responseReceivedAt;
const startedAt = performance.now();
page.on('response', response => {
if (response.url().includes('/api/report') &&
response.request().method() === 'GET') {
responseReceivedAt = performance.now();
}
});
page.on('requestfinished', request => {
if (request.url().includes('/api/report') &&
request.method() === 'GET') {
const finishedAt = performance.now();
console.log({
responseReceivedAt,
finishedAt,
actionToCompletionMs: finishedAt - startedAt,
responseToCompletionMs: responseReceivedAt == null
? null
: finishedAt - responseReceivedAt,
});
}
});
// Trigger the request after registering both listeners.
For production test code, correlate the event to the exact request rather than matching only a URL fragment if multiple requests can use that path. For example, retain the specific request object from the matching response and compare it in requestfinished. Handle requestfailed separately so failed network requests do not silently disappear from your measurements.
Handle redirects and failures consistently
Redirects
A redirect finishes one request and issues a new request. Decide whether your metric describes the first hop, the final response, or the full logical operation. If you report the final result, match and correlate the final request in the redirect chain rather than assuming the first response represents the completed navigation or API operation.
Network failures
Listen for requestfailed when failures matter to the test. Report that outcome separately from a response with status 4xx or 5xx: an HTTP error has a response, while a failed request may have no usable HTTP response to time.
Make comparisons meaningful
For repeatable comparisons, keep the measurement definition and test conditions consistent. Record or control:
- The precise start event and end event.
- Whether redirects count as one operation or separate hops.
- Whether cache and service-worker responses are included.
- Whether an HTTP status outside 200–299 counts as a successful result.
- The browser, network, and CPU conditions used for each run.
These choices determine what the number means; changing them can make otherwise similar measurements incomparable.
Rank #4
Use page metrics to diagnose browser work
Page.metrics() reports page-level metrics such as layout, style recalculation, script execution, and task durations. Its timestamps use monotonic seconds since an arbitrary point in the past. These values can help investigate browser work around a slow interaction, but they are not a substitute for timing an individual response.
Troubleshooting
The wait times out
waitForResponse() has a documented default timeout of 30 seconds. Confirm the URL and predicate match the actual request, and confirm the action really triggers it. You can change the page’s default timeout with Page.setDefaultTimeout; an AbortSignal can cancel the wait.
The wrong response is measured
Narrow the predicate with request method and stable URL details, then inspect the returned response’s URL and status. A URL fragment alone may match unrelated requests.
Free tools Windows power users keep installed
One-click scans. No signup required.
The number is unexpectedly low
Check whether the response came from cache or a service worker, and confirm that the timer begins at the intended action. A waiter that measures only from click time will not include earlier setup such as filling a form.
Best Value
The number is unexpectedly high
Verify that the selected endpoint is response receipt rather than body completion, and check whether the timer includes page-side actions before the request. If a redirect is involved, establish whether the measured response is an intermediate hop or the final one.
The request returns an error status
Inspect status() or ok(). A 404 or 503 can complete normally at the HTTP lifecycle level even if the operation failed from the application’s perspective.
Or skip the browser setup
For a clean screenshot rather than a Puppeteer timing measurement, ScreenshotNeo provides a website screenshot API and MCP server. Its one-call request can return an image or PDF; it does not replace Puppeteer when you need action-to-response timing.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →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 for the request options. ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; those steps can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers indicate the page verdict and billing status. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.
Sign up for ScreenshotNeo’s free plan to try it with 1,000 screenshots a month and no card.
Quick Recap
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.




