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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use Chrome DevTools Protocol (CDP) Network events to observe AJAX traffic inside headless Chromium. Enable the Network domain before navigation or the interaction that triggers the call, record requestWillBeSent and responseReceived events, filter resource types to XHR and Fetch, then call Network.getResponseBody after loadingFinished. This gives you request metadata and response bodies without pausing page execution.

What you are capturing

In Chrome terminology, AJAX-style calls normally appear as resource type XHR or Fetch. CDP’s Network domain tracks the browser’s network activity: each request has a request ID, lifecycle events describe its progress, and the same ID lets you retrieve the response body. The result is a record of what Chromium observed during one run—not a guaranteed inventory of every backend endpoint or proof that the request can be replayed outside the browser.

Attach listeners before the page loads or before the click, form submission, timer, or JavaScript action of interest. If you attach after the activity, the earlier requests are already gone. Redirects can create related request records, and cache or service-worker behavior can change what reaches the network.

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.

Passive logging versus interception

Use Network for ordinary capture

The CDP Network domain is the right choice when you want to watch traffic, save metadata, and read completed responses. It does not pause the page. Enable it once, collect events, and handle success and failure paths.

Use Fetch only when you must intervene

The CDP Fetch domain is for interception: matching requests are paused at a request or response stage. Your client must continue, fail, or fulfill every paused request. Forgetting to resolve one can make the page appear frozen. Do not turn on interception merely to log traffic.

Prerequisites and a minimal capture design

  • A Chromium or Chrome installation that your automation library can launch in headless mode.
  • A current automation wrapper with CDP-session support. CDP tip-of-tree documentation changes and does not promise backwards compatibility, so pin and verify the wrapper version you deploy.
  • An authorized target and a policy for redacting credentials, tokens, cookies, and personal data before writing logs.

The implementation has six parts:

  1. Launch Chromium and create a page.
  2. Open a CDP session for that page.
  3. Send Network.enable before navigation.
  4. Store request metadata by request ID and keep only XHR and Fetch.
  5. On loadingFinished, request the body with Network.getResponseBody; on loadingFailed, preserve the failure.
  6. Serialize the selected records after the action is complete, then redact sensitive values.

Node.js example with Puppeteer and CDP

The following example uses Puppeteer’s CDP session. Check the current Puppeteer API for your installed version before production use; the protocol commands and event names are the important parts.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();
  const client = await page.target().createCDPSession();
  const calls = new Map();

  await client.send('Network.enable');

  client.on('Network.requestWillBeSent', event => {
    const type = event.type;
    if (type !== 'XHR' && type !== 'Fetch') return;
    calls.set(event.requestId, {
      requestId: event.requestId,
      url: event.request.url,
      method: event.request.method,
      requestHeaders: event.request.headers,
      resourceType: type,
      initiator: event.initiator,
      timestamp: event.timestamp,
      redirectResponse: event.redirectResponse || null
    });
  });

  client.on('Network.responseReceived', event => {
    const record = calls.get(event.requestId);
    if (!record) return;
    record.status = event.response.status;
    record.statusText = event.response.statusText;
    record.mimeType = event.response.mimeType;
    record.responseHeaders = event.response.headers;
    record.fromDiskCache = event.response.fromDiskCache;
    record.fromServiceWorker = event.response.fromServiceWorker;
  });

  client.on('Network.loadingFinished', async event => {
    const record = calls.get(event.requestId);
    if (!record) return;
    record.encodedDataLength = event.encodedDataLength;
    try {
      const body = await client.send('Network.getResponseBody', {
        requestId: event.requestId
      });
      record.body = body.body;
      record.base64Encoded = body.base64Encoded;
    } catch (error) {
      record.bodyError = error.message;
    }
  });

  client.on('Network.loadingFailed', event => {
    const record = calls.get(event.requestId);
    if (!record) return;
    record.failed = true;
    record.errorText = event.errorText;
    record.canceled = event.canceled || false;
  });

  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  // Replace this with the action that triggers your AJAX call.
  // await page.click('#load-data');
  await page.waitForTimeout(3000);

  console.log(JSON.stringify([...calls.values()], null, 2));
  await browser.close();
})();

Replace the URL and trigger with your authorized test case. For a deterministic capture, wait for a selector, a known response, or an application-specific completion signal instead of relying only on a fixed delay. The listener is installed before page.goto, so navigation-time requests are eligible for capture.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

Associating requests, responses, redirects and failures

Request IDs are the join key

Use the request ID as the map key. Add request URL, method, headers, initiator and timing fields from requestWillBeSent. Add status, MIME type, response headers and cache indicators from responseReceived.

Read bodies after completion

Call Network.getResponseBody after the corresponding response has completed, normally from loadingFinished. The returned body may be plain text or marked base64 encoded. A body can be unavailable for a failed, canceled, or otherwise unsupported request; record that condition instead of treating it as an empty response.

Do not flatten redirects

A navigation or API call that redirects can produce multiple records. Preserve the redirect response and each request ID so you can reconstruct the chain. Do not assume one logical URL equals one network event.

Record failures separately

loadingFailed supplies an error and may indicate cancellation. Keep failed records in your output; dropping them makes intermittent problems look like missing traffic.

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

Filtering and interpreting the output

Filtering by XHR and Fetch removes images, stylesheets and most document noise, but it also narrows the evidence. WebSockets expose messages through a different event family, and EventSource and other streaming responses have different semantics. If your application uses those transports, capture their dedicated events rather than expecting one completed response body.

Inspect initiators to learn which script or action caused a call. Keep status and timing data, and note whether a response came from disk cache or a service worker. Cached requests may not expose the original request headers. Security controls can also produce provisional or incomplete header information, so do not present partial metadata as the complete wire exchange.

When a HAR file is enough—and when it is not

A HAR-style log is useful for request metadata and timing, but it is not automatically the response content. Chrome’s network extension interfaces expose a separate body-content operation. If your goal is to analyze JSON returned by an endpoint, explicitly retrieve and store the body with CDP rather than assuming a HAR contains it.

Reliable capture workflow

Capture the complete trigger

  • Open the CDP session and enable Network before navigation.
  • If the call occurs after a click, install listeners before the click.
  • Wait for the application state that proves the operation finished.
  • Save both successful and failed records.

Control noise and storage

Keep only the fields needed for your test. Bodies can be large or binary; enforce size limits, store base64 data deliberately, and redact authorization headers, cookies, API keys, email addresses and other personal data. Never publish an unredacted capture.

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

Make runs comparable

Record browser version, viewport, user agent, locale, timezone, network conditions and whether cache or service workers were enabled. Differences in those settings can change which calls occur and whether a response is served locally.

Troubleshooting common failures

No AJAX requests appear

Most often the listener was attached too late, the action was never triggered, or the request is a WebSocket, EventSource, document, or another resource type. Enable Network first, reload, perform the action again, and inspect all resource types temporarily to confirm the transport.

getResponseBody fails

Request the body only after loadingFinished. Check that the request ID is the same one received in the lifecycle event, and retain a failure record when loadingFailed occurred. Some responses are unavailable because they were canceled, streamed, served through a path the protocol cannot expose, or discarded after the target changed.

The page hangs after adding interception

A Fetch interception handler paused a request without continuing, failing, or fulfilling it. Remove interception for passive logging, or resolve every paused event on every branch, including errors.

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

Headers look incomplete

Cache and service-worker responses can omit original request headers, while security restrictions can show provisional headers. Treat those fields as observations with context, not as a complete packet trace.

Duplicate or missing records appear

Redirects, retries, preflight requests, service workers and caching can all alter the event sequence. Preserve request IDs, redirect links, initiators and cache flags rather than deduplicating solely by URL.

JSON is unreadable

Check the base64Encoded flag before decoding. Also inspect the MIME type, compression handling supplied by the wrapper, and whether the response is actually a stream or binary payload.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability and security considerations

Network event logging adds bookkeeping and body retrieval, not a fixed throughput guarantee. Retrieving every body can increase memory and output size; filter early, cap retained data, and write records incrementally for long sessions. A timeout should end the capture cleanly and close the browser so a stuck page does not leak a process.

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

CDP is tied to the browser and wrapper versions. Pin versions in CI, test protocol commands after upgrades, and treat tip-of-tree changes as compatibility work. Captured headers and bodies may contain credentials or personal data: restrict file permissions, redact before sharing, and obtain authorization for every target.

Or skip the browser setup

If your goal is a clean visual capture rather than AJAX diagnostics, ScreenshotNeo provides a one-call website screenshot API and MCP server. It is not a replacement for CDP network logs, but it avoids maintaining a browser harness for screenshots.

cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

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)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo documentation for parameters. Before the shot, it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed as clean shots, and response headers identify the page verdict and billing status. Its MCP server includes take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.

Create a free ScreenshotNeo account to try the 1,000 monthly screenshots without a card.

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

Frequently Asked Questions

Can CDP capture requests made before my script attaches?

No. Enable Network and attach listeners before navigation or before the triggering interaction; reload when necessary.

Does a captured response prove I can replay the API call?

No. The record reflects one browser run. Cookies, authorization, CSRF state, redirects, service workers and server-side conditions may be required for replay.

Should I use a proxy instead of CDP?

A proxy observes traffic outside the page, while CDP associates events with browser targets and initiators. Choose based on whether you need browser context, cross-process coverage, or transport-level inspection.

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.

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