Skip to main content
This page explains how Browser API measures and bills bandwidth, what each field in the bandwidth breakdown means, and why the number you measure in your own script can differ from the billed total.

Overview

You are billed for one number: total. It is every byte that crosses the proxy tunnel for your session, measured on the wire. Everything else in the bandwidth breakdown explains where that total came from, so you can match it against your own measurements. If you count traffic yourself with Chrome DevTools Protocol (CDP) Network.* events, your number will differ from total, and is usually lower. That is expected. Three things make up the difference:
  • Work done before your session starts. For some sites, the Browser API warms up the browser before your target page loads, for example by loading the site’s homepage first. This is one of the ways we unblock sites with a high success rate. It happens before your session starts, so your script cannot see this traffic in its CDP events. It is reported as before_session.
  • Protocol overhead. Encryption (TLS) handshakes and framing add bytes to every connection. They are reported as overhead_bytes.
  • Traffic Chrome does not report to your script. Some requests cross the network without producing the CDP events your script listens to. They are reported as client_hidden_bytes.
After you account for these three, a small difference (the residual) usually remains. A few possible reasons for the residual are explained in Why your number differs from what we bill.
Nothing about pricing changes. The breakdown adds visibility and transparency only. How bandwidth is measured and billed is the same as before.

Where to find it

The same breakdown object is available in two places. Only the field name differs. 1. Session Logs API, after the session ends. Each session record keeps its existing bandwidth field, the billed total as a number. The breakdown is in a new field, bandwidth_breakdown. bandwidth always equals bandwidth_breakdown.total.
GET /browser_sessions returns the same two fields for each session in the list. 2. From inside your session, with a CDP command. Browser.getBandwidth returns only the breakdown object, under the key bandwidth. It has the same fields as bandwidth_breakdown in the API. There is no separate number; the billed total is bandwidth.total. Call it at any point to read the bandwidth used so far, for example right before you close the browser.
This works like other Browser API custom CDP commands, such as Browser.getSessionId. Values read mid-session cover traffic up to that moment; the final values are in the Session Logs API.

The bandwidth breakdown object

This is bandwidth_breakdown in the Session Logs API and bandwidth in the CDP command. The object has two groups of fields. Billing fields describe what you pay for. Reconciliation fields describe traffic the way Chrome sees it, so you can match your own CDP numbers. Reconciliation fields are never charged on top of total. All values are in bytes.
The tables below explain each field.

Billing fields

total always equals payload_bytes + overhead_bytes exactly.

Reconciliation fields

Resource types in both breakdowns: document, script, stylesheet, image, font, media, other. other includes every resource type that does not have its own field: API calls (XHR and Fetch), downloads, WebSocket and EventSource connections, web app manifests, text tracks, prefetch and preflight requests, pings, CSP violation reports and signed exchanges. It also includes resources Chrome reports as “Other” or with no type, and any new types Chrome adds in the future.

How session_bytes is counted

session_bytes follows this definition, which matches what you get from standard CDP events:
  • Request: the request line, the request headers, and any request body (POST data).
  • Response: the status line, the response headers, and the response body as transferred (encodedDataLength). A gzip or Brotli response counts at its compressed size.
  • Headers are counted uncompressed, as plain Name: value text. On the network, HTTP/2 compresses them, so this part is slightly larger than the wire bytes.

Why your number differs from what we bill

Your script and our meter count at two different points. Your script hears about traffic through Chrome’s CDP events, inside the browser. Our meter counts the encrypted bytes on the network connection between the browser and the proxy, after Chrome has done its work. Some traffic only exists at the second point, and some is counted differently at each. All four kinds of traffic cross the tunnel and are billed in total. Only the first one reaches your script as CDP events.

Where the difference comes from

What adds up

  • Exact: total = payload_bytes + overhead_bytes. Always.
  • Exact: the values in session_breakdown add up to session_bytes, and the values in before_session_breakdown add up to before_session.
  • Close, within a small residual: payload_bytes ≈ session_bytes + before_session + client_hidden_bytes.
Always include before_session when you compare. Warm-up can be large and can change a lot between sessions, but it is fully reported in before_session.

The small remaining difference (residual)

Calculate the residual like this:
With the example object above: What to expect:
  • Positive residual (payload_bytes is higher than the fields explain): small in most cases.
  • Negative residual (your CDP-based fields add up to more than payload_bytes): can reach about 10% on pages with many headers. This comes from header compression, and you are billed the smaller, compressed size.
On small pages, compare in bytes, not percent. A few KB of residual per session is normal. On a page of only a few dozen KB, that can look like a large percentage. What it is: real network traffic that is billed in total. It is not a metering error: total = payload_bytes + overhead_bytes always holds. A few possible reasons for the residual:
  • Header compression. HTTP/2 compresses headers on the network, while CDP reports them uncompressed. This makes your CDP sum higher, which is why the residual can be negative.
  • Encryption framing and connection messages. TLS record headers and HTTP/2 control messages (SETTINGS, PING, WINDOW_UPDATE) belong to the connection, not to any one request.
  • Streaming, long-lived or cancelled requests. Chrome’s reported size for these can differ from what actually crossed the network.
  • Request bodies. Uploaded data is measured differently by CDP and on the wire.
  • Retransmitted encrypted data, and TLS handshakes inside the tunnel.
These are possible reasons. We do not measure the exact split for each session. If the residual is positive and much larger than a few percent, contact support with the session_id. We investigate those cases individually.

Common counting mistakes on the customer side

These mistakes make a CDP sum too low, and the residual look larger than it really is:
  • Using decoded sizes. Network.dataReceived.dataLength counts the body after decompression, which is not what travels on the network. Use encodedDataLength from Network.loadingFinished instead.
  • Counting responses only. Request headers and POST bodies are traffic too.
  • Attaching the listener late. Requests sent before Network.enable runs are not reported to you.
  • Watching one page only. Iframes, popups and service workers can run in separate targets with their own events.
  • Skipping failed requests. A request that is cancelled or fails part way still transferred bytes. Check Network.loadingFailed too.

Reconcile your own numbers

Follow these steps to check a session yourself.
  1. Count with the same definition we use. Attach your listener before the first navigation and sum request line + request headers + POST data + status line + response headers + encodedDataLength. The example below does this.
  2. Compare your sum with session_bytes. They should be close. Small differences come from timing and from requests in other targets.
  3. Account for the rest of payload_bytes. Add before_session (warm-up) and client_hidden_bytes (requests Chrome did not announce) to session_bytes. The result should be close to payload_bytes: usually only slightly below it, and up to about 10% above it on pages with many headers. Then add overhead_bytes to get total.
  4. Positive residual much larger than a few percent ? Contact support with the session_id. We can look at the session in detail.

Example: count bytes with Puppeteer

This script counts your session’s traffic from CDP events, the way your own code would, then prints it next to the bandwidth breakdown from Browser.getBandwidth so you can compare the two.
This gives a close match, not an exact one. For billing, total is always the number that counts.

Monitor and control your bandwidth

Track cost per site

The breakdown is per session, so you can build per-site monitoring on top of it:
  1. After each session, read bandwidth_breakdown from the Session Logs API (or bandwidth from the CDP command before you close the browser).
  2. Store total, before_session and session_bytes with the target domain.
  3. Alert when a domain’s typical total per session rises sharply. A jump driven by before_session means a warm-up step started running for that site. A jump in session_breakdown shows which resource type grew, for example image or media.

Set a spending limit

In the zone’s Advanced settings, set a consumption limit. When the zone reaches it, new requests stop. This protects you from unexpected spikes across all sites in the zone.

Reduce in-session bandwidth

You control what loads during your session. Block resource types you do not need, such as images, media and fonts. See Optimizing bandwidth usage with Browser API. Use session_breakdown to see which types use the most. Be careful with traffic in other: it includes the XHR and Fetch calls that usually load the data you need, so blocking it often breaks the page.

About warm-up (before_session)

Warm-up is one of the ways the Browser API unblocks sites with a high success rate. It runs automatically, only for sites where it is needed to load the target page successfully. It is decided per site and can change over time as sites change their protection. You cannot see it in CDP and you cannot turn it off from your script. Disabling CAPTCHA solving does not turn it off either. If warm-up starts on a site that did not have it before, or before_session grows over time, that can be expected: warm-up adapts as sites change their protection.