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.
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.
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 isbandwidth_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.
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: valuetext. 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 intotal. 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_breakdownadd up tosession_bytes, and the values inbefore_session_breakdownadd up tobefore_session. - Close, within a small residual:
payload_bytes ≈ session_bytes + before_session + client_hidden_bytes.
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:
What to expect:
- Positive residual (
payload_bytesis 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.
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.
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.dataLengthcounts the body after decompression, which is not what travels on the network. UseencodedDataLengthfromNetwork.loadingFinishedinstead. - Counting responses only. Request headers and POST bodies are traffic too.
- Attaching the listener late. Requests sent before
Network.enableruns 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.loadingFailedtoo.
Reconcile your own numbers
Follow these steps to check a session yourself.- 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. - Compare your sum with
session_bytes. They should be close. Small differences come from timing and from requests in other targets. - Account for the rest of
payload_bytes. Addbefore_session(warm-up) andclient_hidden_bytes(requests Chrome did not announce) tosession_bytes. The result should be close topayload_bytes: usually only slightly below it, and up to about 10% above it on pages with many headers. Then addoverhead_bytesto gettotal. - 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 fromBrowser.getBandwidth so you can compare the two.
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:- After each session, read
bandwidth_breakdownfrom the Session Logs API (orbandwidthfrom the CDP command before you close the browser). - Store
total,before_sessionandsession_byteswith the target domain. - Alert when a domain’s typical
totalper session rises sharply. A jump driven bybefore_sessionmeans a warm-up step started running for that site. A jump insession_breakdownshows which resource type grew, for exampleimageormedia.
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. Usesession_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.