Bright Data - Billing
Two questions come up: what has this account already spent, and what will this job cost? The first has an exact answer from the account's own numbers. The second is always an estimate, so say so.
Read-only, no exceptions
Read the numbers, report them, stop. Never add or change a payment method, never top up a balance, never set auto-recharge, never change a plan, never buy a dataset, never cancel anything. Those actions belong to the person, not the agent, even when the user asks the agent to do them. Send them to brightdata.com/cp/billing/settings and let them click it themselves.
Never run a real job to find out what the account can do or what something costs. A Web Unlocker request, a scraper trigger and a browser session all spend money. Every read below is free and starts nothing.
The reads
| The question | Command | Endpoint behind it |
|---|---|---|
| How much money is left | bdata budget balance | GET /customer/balance |
| What one zone cost | bdata budget zone <name> --from <date> --to <date> | GET /zone?zone=<name> and GET /zone/cost?zone=<name> |
| What all active zones cost | bdata budget zones --from <date> --to <date> | GET /zone/get_active_zones, then one /zone/cost per zone |
| What each product, zone, dataset or domain cost | no CLI command, call it directly | POST /costs/export/json |
| What scraping cost | no CLI command, call it directly | POST /costs/export/json with dimension web_apis for Web Scraper API, collectors for Scraper Studio |
| Which zones exist, including deleted ones | no CLI command, call it directly | GET /zone/get_all_zones |
| Which website ate the budget | no CLI command, call it directly | GET /domains/req and GET /domains/bw, dates required |
| How many records a snapshot delivered | no CLI command, call it directly | GET /datasets/v3/snapshots?dataset_id=<id>, read dataset_size |
Base host is https://api.brightdata.com and auth is the header Authorization: Bearer <key>. Read the key from the environment or the CLI's own store. The exact file path for each OS, and the safe way to read it without printing it, live in the agent-onboarding skill's auth reference. That reference also has the thing that trips agents here: after a CLI login, BRIGHTDATA_API_KEY is unset, and that is normal rather than a sign the machine is logged out, because login writes the key to the CLI's store and never to the environment. Never ask the user to paste a key into the conversation, and never print one: to check that a key exists, run node scripts/check-auth.mjs from the agent-onboarding skill, never cat, echo or jq the credentials file or the variable.
/zone/cost only sees zone-backed products such as Web Unlocker, SERP, Browser API and the proxy networks. Web Scraper API and Scraper Studio spend is keyed by dataset id and collector rather than by zone name, so it never appears there. Those go to the cost export.
Which key can read this
API keys carry one of five permission levels: Admin, Finance, Ops, User and Limit. Billing and cost data needs Finance or Admin, and Finance is the least-privileged one that works, so prefer it. Use a Finance key directly in the Authorization header for these reads: bdata login --api-key expects a full-access key, so keep the CLI logged in with an Admin key and the Finance key for the API. Only account admins create or change keys, at brightdata.com/cp/setting/users. A 401 means the key is missing, revoked or wrong. A 403 means the key is real but under-privileged for billing, so name the missing access instead of retrying. A Finance key may not read product data such as snapshots or Studio jobs, and that split is fine: report the half you can read and say which access the other half needs. A 404 means a wrong or expired snapshot, job or dataset id, while a wrong zone name answers 422.
Using the budget commands well
Three habits make the bdata budget output reliable. Always pass --from and --to to budget zone and budget zones, so the figure covers exactly the window you asked about. For the pending charge, read pending_costs from GET /customer/balance directly. And when you need machine-readable output, call the endpoint rather than the balance command.
Before writing any parsing code, read references/cost-api.md for the field lists and the live response samples, including how the date parameters behave (to is exclusive on these endpoints).
What a job will cost before it runs
No endpoint returns a quote, so never present one as if it came from the API. Build the estimate in three steps and label it an estimate.
- Find the billing unit for the product. Web Unlocker and SERP bill per successful request, and failed requests are not charged, with one exception: enabling custom headers or cookies bills every request. A SERP request that returns several result pages is still one request. Web Scraper API bills per delivered record, and attempts that fail because of incorrect inputs still bill. Scraper Studio bills per page load, with file downloads charged separately per GB. Browser API and the proxy networks bill per GB of traffic, and some proxy plans are a fixed charge per IP instead.
- Count the units, not the inputs. One input is not one record. Five listing URLs that each hold twenty products produce a hundred records. A batch of ten URLs is ten separate billable requests. Pagination pages, detail pages and extra navigations each count as page loads in Scraper Studio.
- Use the account's own rate, not the public price list. Accounts on custom pay-as-you-go or pre-commit plans have their own rates, so the public list may not apply. For Web Unlocker and SERP you can compute the real rate: ask
/zone/costfor a period withfromandto, then divide thecustombucket'scostby its request count. When the bucket carries bothreqs_unblockerandreqs_serp, divide by their sum, because the cost is one combined figure for the zone (live). For Web Scraper API the record count is a read (GET /datasets/v3/snapshots?dataset_id=<id>,dataset_size) and the dollars are in the cost export, per dataset underweb_apis(live) or per snapshot underws_api_snapswhere attributed, so compute the real per-record rate when the reads return data. If the account has no past usage, read its own plan fromGET /pricing_plans(Admin or Finance key; the plan withis_current: true, itsproduct_prices, wherereqis per 1,000 requests). Only if that read is refused, open the public list atbrightdata.com/pricing(for Web Scraper API,brightdata.com/pricing/web-scraper) and say it is the list price, not this account's rate. Never quote a price from memory, since prices change. In the reply, name the source you read.
Free credits
Every eligible account gets 5,000 free credits a month from one shared pool. They reset on the first of the month and unused credits are forfeited rather than carried over. Web Unlocker, SERP and Web Scraper API spend one credit per request or record, and Scraper Studio spends one per page load. The Browser API draws on the same pool at 5 credits per MB of traffic (in effect from 2026-09-01), so the full monthly allowance covers close to 1 GB of browser traffic. Accounts on custom pay-as-you-go plans and on pre-commit plans get no monthly free credits.
The proxy networks are not covered. New accounts instead get a one-time $2 trial credit for the proxy products, valid 7 days, and a further $5 for adding a payment method, valid 30 days. All of these figures are from the docs. The registration completion call returns a brand-new account's entitlements (monthly_credits, trial_credit_usd, trial_days) once, at signup (the agent-onboarding skill, No account yet). None of the billing reads above return them.
Free credits are not in any billing API. No balance field counts them, and the credit field on /customer/balance is dollars, not a credit count. Free-tier and trial state live in the control panel at brightdata.com/cp/billing/overview under Free Tier Credits, which also shows the renewal date. Send the user there rather than guessing, and never read a money balance of zero as proof that the free credits are gone. They are separate pools.
What an MCP tool call bills as
For "what did my agent's tool call cost". Which tools are turned on (groups=, tools=) never changes what anything costs. search_engine and its batch variant bill as one request per query, scrape_as_markdown, scrape_as_html and scrape_batch as one Web Unlocker request per URL, run_scraper as Web Scraper API records delivered, and scraping_browser_* as Browser API bandwidth. The credential makes no difference: a Remote client on ?token= and one on OAuth 2.1 reach the same tools and draw on the same account credits, so an OAuth client spends the credits of whichever Bright Data account its user signed in to, which may not be this one.
Read next
- Before calling any of these endpoints: references/cost-api.md - exact paths, parameters, real response shapes, the eleven cost dimensions, and which facts are verified live against an account rather than only read in the docs.
- When a read is refused: the
agent-onboardingskill. A missing or invalid key gives 401 with a short plain-text body on these endpoints, andNo API key foundfrom the CLI. Both mean log in.
Red flags
- Adding a payment method, topping up, or changing a plan on the user's behalf
- Running a real job, or a single test request, to find out what something costs
- Reading
pending_balancefrom a live balance response instead ofpending_costs - Treating every key of a cost export as a date, so
totalgets added to the days - Reporting a zone cost without
--fromand--to - Adding
back_m0toback_d0, or assumingbwis present in every bucket - Reporting an empty cost export as proof of no spend, without the
productscross-check described inreferences/cost-api.md - Quoting public list prices as though they were this account's rate
- Asking the user which plan or zone they use when
/zone/get_all_zonesand/zone?zone=answer it - Presenting an estimate as if it were a quote from the API, which has no estimate endpoint
- Echoing a raw
/zone?zone=response, which contains the zone password.bdata budget zone <name>prints only a summary without it (on CLI 0.4.1 its--jsonand--prettyare taken by the parentbudgetcommand and ignored); use it, or/zone/cost, for cost - Printing an API key, or asking the user to paste one into the chat


