Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →To capture requests and complete response bodies with PuppeteerSharp, subscribe to the page’s request and response lifecycle events before navigating or triggering the activity you want to inspect. Record outgoing-request details from Request, response metadata and bytes from Response, completion from RequestFinished, and failures from RequestFailed. For a general capture that must preserve the original body, use IResponse.BufferAsync().
Capture requests and response bodies with page events
This example records request details, response metadata and response-body bytes, then marks requests that finish or fail. It prints a summary after navigation; adapt the storage portion if you need to write the captured bytes to files or a database.
As an Amazon Associate I earn from qualifying purchases.
Install the PuppeteerSharp NuGet package and make sure its API matches the version in your project. The live API references describe these members, but do not establish a package version for this example, so check signatures against the documentation matching your installed package.
using System.Collections.Concurrent;
using PuppeteerSharp;
var browserFetcher = new BrowserFetcher();
await browserFetcher.DownloadAsync();
await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true
});
await using var page = await browser.NewPageAsync();
var captures = new ConcurrentDictionary<IRequest, CapturedRequest>();
page.Request += (_, request) =>
{
captures.TryAdd(request, new CapturedRequest
{
Url = request.Url,
Method = request.Method,
ResourceType = request.ResourceType.ToString(),
PostData = request.PostData
});
};
page.Response += async (_, response) =>
{
var request = response.Request;
var capture = captures.GetOrAdd(request, _ => new CapturedRequest
{
Url = request.Url,
Method = request.Method,
ResourceType = request.ResourceType.ToString(),
PostData = request.PostData
});
capture.ResponseUrl = response.Url;
capture.Status = response.Status;
capture.StatusText = response.StatusText;
capture.Headers = response.Headers;
capture.FromCache = response.FromCache;
capture.FromServiceWorker = response.FromServiceWorker;
// Preserve the body as bytes; do not assume every response is text.
capture.Body = await response.BufferAsync();
};
page.RequestFinished += (_, request) =>
{
if (captures.TryGetValue(request, out var capture))
capture.State = "finished";
};
page.RequestFailed += (_, request) =>
{
if (captures.TryGetValue(request, out var capture))
{
capture.State = "failed";
capture.FailureText = request.Failure;
}
};
await page.GoToAsync("https://example.com");
foreach (var item in captures.Values)
{
Console.WriteLine($"{item.Method} {item.Url} — {item.State}; status={item.Status}");
Console.WriteLine($"Response bytes: {item.Body?.Length ?? 0}");
}
public sealed class CapturedRequest
{
public string? Url { get; set; }
public string? Method { get; set; }
public string? ResourceType { get; set; }
public string? PostData { get; set; }
public string? ResponseUrl { get; set; }
public int? Status { get; set; }
public string? StatusText { get; set; }
public IDictionary<string, string>? Headers { get; set; }
public bool FromCache { get; set; }
public bool FromServiceWorker { get; set; }
public byte[]? Body { get; set; }
public string? State { get; set; }
public string? FailureText { get; set; }
}
The event handlers are attached before GoToAsync, so they can observe navigation requests as they are issued. If the request you care about happens after a user action, attach the handlers first and then perform that action.
#1 Best Overall
What each lifecycle event tells you
| Event | What it records | How to use it |
|---|---|---|
Request |
The page issued a request. The request exposes URL, method, resource type and post data when available. | Start a record for outgoing traffic. Post data may not always be available in decoded form, even when the request has post data. |
Response |
A response arrived. Its response object exposes URL, status, status text, headers, cache and service-worker indicators, and the associated request. | Store response metadata and call BufferAsync() if you need the body. |
RequestFinished |
The response body has downloaded and the request is complete. | Mark successful completion. This event is useful to distinguish a response that arrived from a request whose download completed. |
RequestFailed |
The request failed rather than reaching successful completion; failure text is available on the request. | Keep the failure context with the request record. A non-2xx HTTP status alone should not be labeled a transport failure. |
The events describe different stages, not interchangeable outcomes. A response can arrive before its body has finished downloading; completion is represented by RequestFinished. Failures use RequestFailed instead of successful completion and can occur before a response event.
Store complete bodies without corrupting binary responses
BufferAsync() returns the response body as bytes. Keep those bytes if you need a faithful capture or if the content type is unknown. Decode them as text, or parse them as JSON, only when the response content type and encoding justify that interpretation. Text and JSON helpers can be convenient for known text responses, but a text-only strategy is not a general full-response capture.
Response metadata can help interpret the body later: retain the response URL, status, headers, cache and service-worker indicators, and its linked request. For large or frequent responses, decide how long to retain bodies and where to persist them; storing every byte in memory can consume substantial memory during a long capture.
Windows 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 reinstallOutdated 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 matchHandle redirects as separate requests
A redirect response completes one request and leads to a new request for the redirected URL. Preserve each request/response record if the redirect path matters; otherwise, use the request’s redirect chain to explain how the final URL was reached. Do not overwrite the original URL with the destination, or the capture will hide part of the sequence.
Rank #3
Observation is different from interception
| Approach | Use it when | What changes |
|---|---|---|
| Page lifecycle events | You want logs, diagnostics or response inspection. | Traffic is observed; the handlers collect data. |
| Request interception | You must abort a request, continue it with overrides or fulfill a synthetic response. | Your code takes responsibility for handling intercepted requests. |
Do not enable SetRequestInterceptionAsync(true) just to capture traffic. Interception is for changing how requests are handled, while lifecycle events are sufficient for observation. If you do enable interception, ensure each request is resolved as intended so page traffic can proceed; exact operational details can depend on the backend and PuppeteerSharp version.
Common problems and fixes
- Navigation requests are missing: the handlers were registered after navigation started. Attach them to the page before calling
GoToAsyncor triggering the action. - The body is empty or unavailable in the record: verify that the response handler calls
BufferAsync()and that your record is associated withresponse.Request. Also account for failed requests, which may never produce a response. - Binary content is unreadable: do not decode every body as UTF-8 or assume it is JSON. Preserve the byte buffer and decode only known text formats with an appropriate encoding.
- A request appears failed even though the server returned an error page: distinguish a received HTTP status from a request failure. Record the response status separately; do not infer a transport failure from a non-2xx status.
- Only the final redirected URL appears: keep records per request and retain redirect relationships instead of replacing earlier URLs.
- Interception stops page activity: if you enabled interception, verify that the handler continues, aborts or fulfills requests deliberately. Turn interception off when you only need passive capture.
- An event or property does not compile: check the installed NuGet version and its corresponding API reference. The sample’s exact compatibility cannot be assumed across unspecified versions.
Performance, reliability and cost considerations
Capturing headers and request metadata is lighter than retaining every response body. Buffering all bodies increases memory and storage use, especially for media, large downloads or long-running pages. Filter by URL or resource type when your diagnostic goal permits it, and persist or discard buffers deliberately. If completeness matters, do not apply a filter that excludes the requests you need.
Subscribe before the event of interest to avoid missing early traffic. Keep the lifecycle outcome in your records so that a response arrival is not mistaken for completed download. No performance benchmark or universal memory figure is established here; actual overhead depends on the page, response sizes and capture policy.
Or skip the browser setup
For a clean screenshot or PDF rather than raw network-response bodies, ScreenshotNeo offers a one-request screenshot API. It is not a replacement for PuppeteerSharp when you need to inspect response bytes or request lifecycle details. One cURL call looks like this; see the ScreenshotNeo API documentation for options:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo accepts cookie or consent banners like a visitor and removes 60+ known consent platforms, newsletter popups and chat widgets before capture; each step can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies page verdict and billing status in headers. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools 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 get 1,000 screenshots a month with no card.
Frequently Asked Questions
Can I capture requests that happen after the initial page load?
Yes. Attach the event handlers before performing the user action or script that triggers those requests.
Free tools Windows power users keep installed
One-click scans. No signup required.
Does a response event mean the response body has finished downloading?
No. The lifecycle marks body download completion with RequestFinished.
Is PuppeteerSharp interception required to read a response body?
No. Response events expose the response object and its BufferAsync() method; interception is for modifying request handling.
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.




