Skip to content

[Feature]: Expose recorded ResourceTiming on APIResponse (parity with Request.timing()) #41747

Description

@shahzad31

🚀 Feature Request

Browser-originated requests expose detailed network phase timings via Request.timing() (DNS, connect, TLS, TTFB, receive). Requests made through APIRequestContext (request.get/post/..., fetch) return an APIResponse that has no equivalent — the only way to get any timing today is to wall-clock the call in userland, which yields a single opaque total.

I'd like to propose exposing the per-phase ResourceTiming on APIResponse, read-only, mirroring Request.timing().

Why this is a thin change, not a new capability

Playwright already collects and maintains these exact timings for APIRequestContext — they were added in #32613 (merged) and recorded into the HAR/trace (the trace viewer already shows the duration), and proxy edge cases have since been fixed (#32855). So the data already exists in memory; this request is only to surface it through the public API instead of requiring users to parse trace/HAR artifacts.

A previous PR (#32647) implemented exactly this but was closed with the rationale that "Playwright isn't a network performance testing tool." Respectfully, I'd push back on two points:

  1. This isn't about load/performance testing — it's diagnostic parity with browser requests, which already expose timing().
  2. "Rough timings can be measured in userland" only holds for total time. The phase breakdown requires socket-level events (lookup / connect / secureConnect / response) on the connection Playwright owns inside fetch.ts, including redirect and proxy handling — userland has no access to it. feat(fetch): expose timings #32647 itself surfaced the SOCKS/HTTP-proxy connect timing bug fixed in fix(har timing): record connect timing for proxied connections #32855, which no userland wrapper could have measured correctly.

Example

const response = await request.get('https://api.example.com/orders');
console.log(response.timing());
// { startTime, domainLookupStart, domainLookupEnd, connectStart,
//   secureConnectionStart, connectEnd, requestStart, responseStart, responseEnd }

The shape would be identical to Request.timing(), so there are no new concepts to learn. It could also be gated behind the existing omitTiming option to stay opt-out.

Motivation / use cases

  • Debugging slow or flaky API tests in CI: knowing whether latency came from DNS, TLS handshake, or the server, instead of one aggregate number.
  • Synthetic monitoring built on Playwright (e.g. Elastic Synthetics, Checkly) that runs browser and API checks side by side and needs consistent network fields across both. Downstream context: feat: multi step api runner elastic/synthetics#997.

Prior art / recurring demand

Happy to help revive #32647 or open a fresh PR if the team is open to a read-only accessor over the already-recorded timings. Thanks for considering!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions