A single dashboard query scanned 4 TB, and the monthly BigQuery bill has a new line item nobody can explain. With on-demand pricing, you pay for the bytes a query reads, not the rows it returns or the time it takes. Most runaway costs come from queries that read far more of a table than they need to.
How on-demand billing works
- You're billed per TiB of data scanned by the query (the first 1 TiB per month is free).
- BigQuery is columnar: it reads only the columns the query references, but all rows of those columns unless partitions or clusters let it skip them.
- There's a minimum of 10 MB billed per table referenced.
That leads to the two most common surprises:
SELECT * FROM events LIMIT 10;
-- scans EVERY column of EVERY row. LIMIT doesn't reduce bytes billed.
SELECT user_id FROM events WHERE DATE(event_ts) = '2026-09-26';
-- scans the whole user_id and event_ts columns if the table isn't partitioned.
Check the cost before you run
The query editor shows "This query will process X GB" before you run it. From the CLI:
bq query --dry_run --use_legacy_sql=false 'SELECT ...'
And put a hard cap on any query, especially from scheduled jobs and BI tools:
bq query --maximum_bytes_billed=10000000000 ... # fail instead of scanning > 10 GB
Fix 1: read only the columns you need
Never use SELECT * on wide tables. List the columns. To look at sample rows, use the table preview in the console or bq head, which is free, instead of SELECT * … LIMIT.
