The Hidden Costs of BigQuery That Catch Teams Off Guard
Kristóf Horváth
12 min read
This post covers the BigQuery charges that sit outside the standard query-and-storage model: BI Engine, BigQuery ML training, and streaming ingestion. It also answers a question that comes up just as often: whether views cost extra. For each one, you’ll see what actually triggers a charge and how to check whether it’s active in your project.
None of these show up if you only know about BigQuery’s on-demand-vs-capacity pricing model, and they don’t apply to every team equally. Views are the odd one out: nearly every BigQuery setup uses them, and they don’t add a charge of their own, but the way they hide scan volume makes them worth understanding first. BI Engine, BigQuery ML, and streaming ingestion only matter if a workload actually uses them. What those three share is how easy each one is to turn on without registering that a new meter just started running: a dashboard gets faster, a model trains successfully, a pipeline goes live, and the billing consequence shows up weeks later, disconnected from the change that caused it. We’ve written about a real-world version of this at an anonymized low-cost airline, where surprise BigQuery bills were the trigger for catching cost issues before release instead of after.
If your team’s core billing model (on-demand vs. capacity, storage tiers, editions) still feels unclear, start with BigQuery Pricing Explained first. The rest of this post assumes you know that part and focuses on what it leaves out.
New to BigQuery Editions and Reservations? Read our comprehensive guide:
Do views cost extra in BigQuery?
No, a view isn’t a separate line item on your bill. BigQuery uses logical views by default, which means a view stores only its query definition, not data. Every time you query one, BigQuery expands the view into its underlying SQL and runs that query against the base tables, billing you the same way it would if you’d written the query directly: by the bytes in the columns you actually reference (see Google’s introduction to logical views).
Column pruning still applies through a view. SELECT col1 FROM my_view bills only for col1 in the underlying table, even if the view’s definition selects seven columns. Run SELECT * against the same view, and you bill for all seven.
The hidden cost here is less about the billing mechanism and more about who gets to trigger it. Authorized views let you grant query access to a view without granting access to the underlying tables, a common pattern for exposing a curated dataset to a wider group of analysts without handing out raw table permissions. That’s good for governance, but it also means the person running the query has no visibility into how many tables, or how much data, sit underneath it. A “simple” self-service query through a view backed by several joined multi-terabyte tables can scan far more than its author expects.
If those base tables are partitioned, partition pruning still works through a view, as long as the underlying query, including whatever the view definition adds, leaves a filter the planner can resolve against the partition column. A view that wraps a date column in a function, or joins in a way that hides the filter, defeats pruning the same way a hand-written query would.
Learn more:
BigQuery Partitioning: How It Works, When to Use It, and What It Costs
If a view is queried often enough that the repeated compute cost adds up, a materialized view is the standard mitigation: it physically stores the precomputed result and refreshes on a schedule, trading some staleness for lower repeat-query cost (see Google’s overview of logical and materialized views).
What is BI Engine and how is it billed?
BI Engine is BigQuery’s in-memory acceleration layer for interactive dashboards (Looker Studio, Looker, Tableau, or anything connecting over JDBC/ODBC). It caches table data in memory so dashboard queries return faster, and teams typically turn it on after noticing a dashboard feels sluggish.
The billing model is where it surprises people. BI Engine is a capacity reservation, billed by the GiB-hour, independent of your query pricing model and independent of whether anyone is actually looking at the dashboard. In most regions the rate is $0.0416 per GiB-hour (check current pricing for your region on cloud.google.com). A 10 GiB BI Engine Reservation left running continuously costs roughly $300/month whether the dashboard gets one viewer or a thousand: the reservation itself is the charge, not the queries against it.
It’s not a one-sided cost, though. When BI Engine accelerates a query, the stage that reads table data is free: on-demand queries are billed for 0 scanned bytes in that stage, and on capacity pricing, that same stage consumes no reservation slots at all. Later stages of the same query (aggregation, sorting, joins) still draw from your normal on-demand bytes or slot allocation (see Google’s BI Engine pricing documentation). That’s the intended trade: pay for memory capacity, and the queries it accelerates largely stop showing up as Analysis or Reservation cost.
The catch is that the offset only holds while the workload it was sized for keeps running. If a dashboard’s query volume drops or the underlying tables change, the BI Engine reservation cost doesn’t adjust itself, and the Analysis or Reservation cost it used to displace has already disappeared from view either way. A reservation that’s stopped paying for itself can run for months without producing any signal that something’s off.
Two more details make this specifically easy to miss:
- BI Engine is a distinct service with its own SKU group, separate from the main BigQuery group that covers storage, streaming, and analysis. Its costs are excluded from both on-demand and capacity (slot-hour) pricing: Google’s Editions pricing explicitly states that flat-rate and editions compute pricing “does not apply to storage costs or BI Engine costs”, so a team that moved to Enterprise Edition and assumes everything is now bundled into slot-hours will still see a separate BI Engine line.
- If you’re on Enterprise or Enterprise Plus with slot commitments, Google bundles in a small amount of free BI Engine capacity (for example, a 100-slot commitment includes 5 GiB at no extra cost). Reserve more than the bundled amount, and the excess bills at the standard rate.
To check whether this applies to you, open the BI Engine section of the BigQuery console and look for active reservations. If nobody on the team remembers creating one, it’s worth confirming whether the dashboard it was built for is still in active use.
Learn more about BigQuery Editions:
BigQuery Editions Comparison: Standard vs Enterprise vs Enterprise Plus
How does BigQuery ML billing work?
BigQuery ML lets you train and run machine learning models with SQL, inside BigQuery. Prediction, evaluation, and inspection queries bill exactly like any other query, the same on-demand bytes-processed or slot-hour pricing you already understand, and they’re included in the same 1 TiB/month free tier as regular analysis. Training a model with CREATE MODEL is where the billing model changes underneath you (see BigQuery ML pricing).
For built-in statistical models (logistic and linear regression, k-means, PCA, ARIMA/time series, and contribution analysis), on-demand training bills at $312.50 per TiB: a full 50x the $6.25/TiB you’d pay for a standard query (US list prices; check pricing for your region on cloud.google.com, and see How Much Does a BigQuery Query Actually Cost? for that standard rate worked through). The bytes processed look identical in a dashboard that just sums total_bytes_processed; only a billing label (bqml_linear_reg_training, bqml_kmeans_training, and so on) tells you which jobs actually billed at the higher rate. On capacity pricing, these training jobs share your standard QUERY reservation slots instead, so the 50x multiplier doesn’t directly apply, but the jobs still compete with your regular workloads for slots.
For models trained via Gemini Enterprise Agent Platform passthrough (AutoML Tables, DNN, Wide-and-Deep, boosted tree, random forest, and autoencoder), on-demand pricing splits the job into two separate billing records: the BigQuery-side data processing at the standard $6.25/TiB rate, plus a passthrough charge for the actual training compute on Agent Platform. On editions or reservation pricing, these same model types instead require a dedicated ML_EXTERNAL job type assignment, which behaves differently from a normal query reservation: it isn’t preemptible and can’t borrow idle slots the way regular workloads can. Google is explicit that the slot usage for ML_EXTERNAL jobs is calculated to hold price parity with what the same training would cost running directly against Agent Platform, so routing training through BigQuery ML isn’t a cheaper backdoor around that service’s own pricing; it’s the same cost, on a different bill.
Here’s the part that actually catches teams off guard: on the on-demand path, none of this shows up as its own line item. Both the 50x built-in-model rate and the passthrough external-training cost are folded into your general analysis (query) charges. To find out what a specific CREATE MODEL job actually cost, you need the billing label attached to that job, via Cloud Logging audit logs or INFORMATION_SCHEMA.JOBS, not just the bytes-processed total. If you’re skimming the Cloud Billing console for a “BigQuery ML” line, you won’t find one on the on-demand path; the cost is hiding inside “Analysis.”
Model storage is simpler: it bills at standard BigQuery storage pricing, same as any other table.
Does streaming data into BigQuery have a separate cost?
Yes. If any pipeline writes data into BigQuery as it arrives rather than in scheduled batch loads, that ingestion is billed separately from both query compute and storage (batch loading itself is free). Two APIs currently do this, priced very differently:
| Ingestion method | Price | Free tier |
|---|---|---|
Legacy streaming inserts (tabledata.insertAll) | $0.01 per 200 mebibytes successfully inserted, 1 KB minimum per row | None |
| Storage Write API | $0.025 per gibibyte per month, per account | First 2 TiB/month free |
(US list prices. Check current rates for your region on cloud.google.com.)
The Storage Write API is Google’s current recommendation and is meaningfully cheaper at any real volume, especially once the free tier is factored in. The part that surprises most teams isn’t the rate card, it’s discovering both APIs are running at once: an older pipeline built on legacy streaming inserts that nobody’s revisited, sitting next to a newer one on the Storage Write API, each billing under a different SKU. Worth checking which ingestion method your pipelines actually use before assuming you’re already on the cheaper path.
What other BigQuery costs are easy to miss?
A few more billing surfaces worth a quick check, even though they tend to matter less for most teams:
- Cross-region reads. Querying a table stored in a different region from where the query runs adds data transfer charges on top of the query cost itself.
- BigQuery Omni. Querying data in AWS or Azure from BigQuery bills for both the cross-cloud read and the analysis, at rates separate from standard BigQuery compute.
- Cross-region exports and high-throughput reads. Batch exports to Cloud Storage in the same region are free, but cross-region extract jobs bill under general data transfer, and the BigQuery Storage Read API (used by tools like Spark connectors) bills once you’re past its monthly free allowance.
None of these are usually the largest line on a bill, but they’re the kind of charge that’s invisible until someone asks why a specific pipeline costs more than its query history suggests.
How do you find out what’s actually on your BigQuery bill?
None of the above require SQL or pipeline access to check: they’re all visible in the Cloud Billing console once you know what to look for.
- Go to Billing → Reports, filter Service: BigQuery, and group by SKU.
- Look for line items beyond the standard Analysis and Storage SKUs: BI Engine capacity, streaming inserts, Storage Write API, or BigQuery Omni.
- For BigQuery ML specifically, remember on-demand
CREATE MODELcosts won’t appear as their own SKU. Cross-checkINFORMATION_SCHEMA.JOBSforCREATE MODELstatements and their billed bytes, or pull the Cloud Logging audit trail Google’s docs describe. - If you find a SKU you can’t map to an owner or a workload, investigate that one first. An active BI Engine reservation with no current dashboard, or a streaming insert charge from a decommissioned pipeline, are both common finds.
This is an audit worth repeating quarterly: new services tend to get turned on for one project and quietly stay on long after the reason for enabling them is gone. For the wider map of what drives a BigQuery bill, from pricing model choice to commitments, see What Actually Drives BigQuery Costs? A Leader’s Guide.
How does Rabbit help catch these before they compound?
Checking the billing console by hand catches what’s already accumulated. The harder problem is catching a new charge close to when it starts, rather than three invoices later. Rabbit’s cost anomaly detection runs a forecasting model against 90 days of usage history and flags spend that breaks from the expected pattern at the project and service level. A BI Engine reservation that activates, or a BigQuery ML training job that scans far more than usual, shows up as a flagged anomaly if it’s large enough to break the expected pattern, instead of a surprise total on next month’s invoice.
Rabbit’s Insights break down cost by BigQuery job and by GCP label, so once something looks unusual, you can trace it to the actual query or pipeline behind it instead of stopping at “BigQuery costs went up”. For the repeated-scan of the views problem, Rabbit’s recommendations flag frequently-scanned tables that are strong candidates for partitioning, clustering, or a materialized view, based on how your queries actually filter.
If you want a starting estimate of how much of your current bill might be sitting in one of these overlooked surfaces, the BigQuery Savings Calculator gives you a baseline in a few minutes.


