What you pay for: bytes, slots and storage

BigQuery · Essentials · 10 min

Every design choice in BigQuery, from SELECT lists to partitioning, comes back to this model.

BigQuery bills compute and storage separately.

Compute, on-demand (the default): you pay per TiB of data processed, $6.25 per TiB in US multi-regions at the time of writing, with the first 1 TiB each month free. Bytes processed are the full sizes of the columns a query reads, in the partitions it reads. So:

  • SELECT * costs the most; listing only the columns you need is the biggest single saving.
  • LIMIT does not reduce bytes on ordinary tables, because the columns are read anyway.
  • WHERE filters reduce bytes only if they prune partitions or clustered blocks.
  • Each table referenced is billed at least 10 MB.
  • Cached results and failed queries cost nothing.

Compute, capacity (BigQuery editions): you buy slots by the slot-hour through reservations, with optional autoscaling. Bytes scanned then don't matter, only slot time. This suits predictable heavy workloads (covered at the Expert level).

Storage is billed per GiB per month. Tables or partitions not modified for 90 days automatically drop to the long-term rate, about half price, with no performance difference. Datasets can use logical (uncompressed) or physical (compressed) storage billing; physical is often cheaper for well-compressed data, but it also bills time-travel bytes.

Free operations: batch loading data, exporting, copying tables, metadata operations. Streaming inserts and the Storage Write API are billed per data volume.

Example

-- Same table, very different bills (on-demand):
SELECT * FROM `proj.web.events` LIMIT 10;           -- reads every column: e.g. 2 TB
SELECT user_id, event_name FROM `proj.web.events`;  -- reads 2 columns: e.g. 90 GB

-- On a table partitioned by DATE(event_ts), this reads one day only
SELECT user_id, event_name
FROM `proj.web.events`
WHERE DATE(event_ts) = '2025-06-01';                -- e.g. 300 MB

-- What did my recent queries bill?
SELECT job_id, total_bytes_billed / POW(1024, 4) AS tib_billed, query
FROM `region-us`.INFORMATION_SCHEMA.JOBS
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
  AND job_type = 'QUERY'
ORDER BY total_bytes_billed DESC
LIMIT 5;

Remember

  • On-demand bills bytes of the columns and partitions you read
  • LIMIT doesn't cut cost; selecting fewer columns and pruning partitions do
  • Untouched data becomes long-term storage at about half price
  • Loading, exporting and copying are free

Watch out for

  • Assuming a WHERE clause on an unpartitioned table reduces the bytes billed.

Check yourself

  1. Which change lowers the on-demand cost of SELECT * FROM big_table WHERE country = 'DE' LIMIT 100 on an unpartitioned table?
  2. When does a table's storage switch to the long-term price?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: cloud.google.com/bigquery/pricing