BigQuery quota and usage caps are not enabled by default. That's the thing that surprises most people who end up with a shock bill. You set up a project, connect some data, start running queries, and assume there's some built-in protection that kicks in before anything catastrophic happens. There isn't.
In February 2024, a user ran queries against the HTTP Archive public dataset using the official GCP Python library. The library, unlike the BigQuery web UI, doesn't show cost estimates before executing. The result: a $14,000 bill from processing 2.5 petabytes of data in a matter of hours. A separate incident involved a developer receiving a $5,000+ charge for a single BigQuery query.
The common thread in every one of these stories: the cost controls existed, were opt-in, and nobody had set them up.
This guide covers exactly how to implement BigQuery quota and usage caps across five layers, so that no single query, no matter how badly written, can produce a bill that keeps you up at night.
Understanding How BigQuery Charges You
BigQuery offers two pricing models. On-demand charges per byte scanned when a query runs. The current rate is $6.25 per TiB processed. The free tier covers the first 1 TiB of queries per month and 10 GiB of storage. Capacity-based (slot reservations) charges a flat rate for committed compute capacity, which makes more sense for teams running many queries per day.
A few cost mechanics that catch people off-guard:
- LIMIT clauses do not reduce bytes scanned. Writing
SELECT * FROM my_table LIMIT 100does not mean BigQuery reads only 100 rows. It scans the entire table and returns 100 rows. You're billed for the full scan. - There is a minimum charge of 10 MiB per table per query, even if the table is tiny.
- Long-term storage saves money automatically. Tables unmodified for 90 consecutive days automatically qualify for long-term storage pricing - roughly half the active storage rate.
Layer 1: Maximum Bytes Billed (Per-Query Hard Stop)
This is the most important control to set up immediately, especially if anyone on your team uses the Python client library or any programmatic interface. Maximum bytes billed is a per-query setting that causes a query to fail before it runs if the estimated bytes to be scanned exceeds your threshold. No scan happens. No charge is incurred. You get an error instead.
Setting it in the BigQuery Console
- Open BigQuery Studio
- Click the three-dot menu next to the query editor and select "Query Settings"
- Under Advanced Options, find "Maximum bytes billed"
- Enter your threshold in bytes (10 GB = 10,000,000,000)
Setting it in Python
from google.cloud import bigquery
client = bigquery.Client()
job_config = bigquery.QueryJobConfig(
maximum_bytes_billed=10 * 1024**3 # 10 GB
)
query_job = client.query(query, job_config=job_config)
# Raises BadRequest if estimated bytes > 10 GB — no charge incurredA reasonable threshold for GSC data analysis is 10-100 GB depending on your dataset size. This is generous enough to run any sensible query but will catch runaway SELECT * on multi-year datasets.
Layer 2: Custom Daily Quotas (Project and User Level)
Maximum bytes billed protects against a single bad query. Custom quotas protect against sustained overuse across many queries over a day. BigQuery has two types of custom quota, as documented in Google's quota configuration guide:
QueryUsagePerDay- limits total bytes processed across the entire project per calendar day (resets at midnight Pacific Time)QueryUsagePerUserPerDay- limits bytes processed per individual user per day
Setting Custom Daily Quotas
- Open Google Cloud Console and navigate to IAM & Admin > Quotas & System Limits
- Filter by Service: "BigQuery API"
- Find "Query usage per day" and "Query usage per day per user"
- Select the quota, click "Edit", deselect "Unlimited", and enter your threshold in TiB
- Submit - lower quotas take effect within minutes
| Control Layer | Scope | Hard Stop? | Best For |
|---|---|---|---|
| Maximum bytes billed | Per query | Yes - query fails before running | Catastrophic single queries |
| QueryUsagePerDay | Project / day | Approximate | Total daily project spend |
| QueryUsagePerUserPerDay | User / day | Approximate | Preventing one user consuming all quota |
| Budget alerts | Billing account / month | No - notification only | Visibility and escalation |
| Table partitioning | Per query (structural) | Yes - reduces bytes scanned | Reducing baseline cost |
Layer 3: Budget Alerts
Budget alerts don't stop charges, but they give you visibility before things spiral. Combined with the controls above, they complete the safety picture.
- Open Google Cloud Console > Billing > Budgets & Alerts
- Click "Create Budget" and set the scope to your specific project
- Enter a budget amount and configure threshold rules (50%, 90%, and 100% of budget)
- Add email recipients for each threshold
For more advanced setups, you can connect budget alerts to a Pub/Sub topic and trigger automated remediation. For most teams, email alerts at multiple thresholds are sufficient when combined with maximum bytes billed and custom quotas.
Layer 4: Structural Cost Reduction with Partitioning
Partitioning reduces your baseline cost on every query, making the budget alerts and quotas less likely to trigger in the first place. The GSC BigQuery export tables are partitioned by data_date out of the box. Including a date filter means BigQuery only scans the relevant partitions. Without a date filter, it scans everything.
According to analysis from The SEO Community, adding proper partition filters to BigQuery-GSC queries can cut costs by 80% or more as datasets grow.
The habit to build: every query against your GSC data should include a WHERE data_date BETWEEN x AND y or WHERE data_date >= DATE_SUB(CURRENT_DATE(), INTERVAL N DAY) clause. No exceptions.
Layer 5: Dry Runs and Query Cost Estimation
Before running an unfamiliar query against a large dataset, estimate the bytes it will scan first.
In the BigQuery Console
The query editor automatically estimates bytes when you write a query. You'll see something like "This query will process X MB when run" in the bottom right of the editor, before you click Run.
In the command line
bq query --dry_run --use_legacy_sql=false \
'SELECT query, SUM(clicks) AS clicks
FROM `project.dataset.searchdata_site_impression`
WHERE data_date >= "2026-01-01"
GROUP BY query'In Python
job_config = bigquery.QueryJobConfig(dry_run=True, use_query_cache=False)
query_job = client.query(query, job_config=job_config)
print(f"Estimated bytes: {query_job.total_bytes_processed:,}")Monitoring Who's Spending What
The INFORMATION_SCHEMA.JOBS view shows which queries and users are driving the most cost, without any additional setup:
SELECT
user_email,
COUNT(*) AS query_count,
SUM(total_bytes_processed) / POW(1024, 4) AS total_tib_processed,
ROUND(SUM(total_bytes_processed) / POW(1024, 4) * 6.25, 2) AS estimated_cost_usd
FROM `region-us`.INFORMATION_SCHEMA.JOBS
WHERE
creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
AND job_type = 'QUERY'
AND state = 'DONE'
GROUP BY user_email
ORDER BY total_tib_processed DESC;The Recommended Setup for GSC Analytics Teams
If your team is primarily using BigQuery to analyse GSC data rather than running large-scale data warehouse workloads, this setup gives you solid protection:
- Maximum bytes billed: Set to 50 GB per query
- QueryUsagePerDay: Set to 0.2-0.5 TiB per day for the project
- QueryUsagePerUserPerDay: Set to 0.1 TiB per user per day
- Budget alert: Set to your expected monthly spend with alerts at 50% and 90%
- Always include date filters on queries against GSC tables
Conclusion
BigQuery quota and usage caps exist to prevent the kind of billing disasters that make it onto Hacker News. The controls are comprehensive and effective. The only problem is that none of them are on by default. The five layers covered in this guide give you defence in depth:
- Maximum bytes billed stops catastrophic single queries before they run
- Custom daily quotas cap sustained overuse at the project and user level
- Budget alerts give you visibility before charges compound
- Table partitioning reduces baseline cost on every query
- Dry runs let you estimate cost before committing to execution
Set these up before you run your first production query, not after. For teams analysing Google Search Console data specifically, Keyword History connects to your existing BigQuery dataset and runs cost-optimised queries automatically - so your team can access SEO insights without having to think about bytes billed.
