Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk6 min

How to Capture Network Requests and Full Responses With PuppeteerSharp

Attach PuppeteerSharp page handlers before navigation, record each lifecycle stage, and use BufferAsync() to preserve full response bodies as bytes.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Handle 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.

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 GoToAsync or 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 with response.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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.