← Back to Blog

BigQuery for a small business: how we keep the analytics bill predictable

  • Google Cloud
  • Sheets
BigQuery for a small business: how we keep the analytics bill predictable

Say a 12-person distribution company moved its sales reporting into BigQuery two years ago. Orders land there every night from the ERP, a Looker Studio dashboard shows yesterday's revenue by customer, and the owner opens it with the first coffee of the day. For a long time the bill was small enough that nobody looked. Then one month it was not, and the uncomfortable part is that nothing had changed. No new table, no new report. The data simply got bigger, and a dashboard eight people now open every morning kept doing what it had always done.

This is the most common Google Cloud invoice we are asked to explain, and it is usually fixable in an afternoon. The fix is not a rewrite. It is knowing which meter is running, putting a ceiling on it, and making one table stop being read in full. Here is the order we work through it on a client project. Prices and behaviour below come from Google's own documentation, checked 6 October 2026.

A BigQuery bill has two meters, and only one of them moves fast

Storage is the slow meter. Compute is the fast one, and under the default on-demand model you pay for the bytes each query reads, not for how long it runs or how clever it is.

On the on-demand model, the first 1 TiB of query data processed per month is free, and above that the list price is USD 6.25 per TiB (BigQuery pricing page, checked 6 October 2026). The page shows the rate for the location you pick, so confirm the one you are actually billed in before you do any arithmetic.

Storage is billed by the gibibyte-hour, with the first 10 GiB per month free. The pricing page's own worked example puts 1 TiB of active logical storage held for a full month in us-central1 at USD 23.552 (checked 6 October 2026). There is a second discount most small teams never notice: a table or partition not modified for 90 consecutive days moves to long-term storage and the rate drops by about half. Querying it, exporting it, copying it or creating a view over it does not reset that clock. Only modifying the data does.

Put those two numbers next to each other and the shape of a small analytics bill is obvious. A terabyte sitting still for a month costs roughly what four terabytes cost to read once. When the invoice jumps at a company of fifteen or twenty people, it is nearly always compute, and nearly always one query that runs over and over.

What we check first on a client's project

Three things, in this order, before touching a single table.

What is the bill actually made of. In Cloud Billing reports, filter to the project and group by SKU. BigQuery analysis and BigQuery storage are separate lines. If analysis dwarfs storage, the problem is a query. If storage dwarfs analysis, the problem is that somebody is keeping ten years of raw exports they have never opened.

Which queries read the most bytes. BigQuery records every job in INFORMATION_SCHEMA, and the view is free to query. Replace the region with yours:

SELECT
  user_email,
  COUNT(*) AS jobs,
  SUM(total_bytes_billed) / POW(1024, 4) AS tib_billed
FROM `region-us`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
  AND job_type = 'QUERY'
  AND state = 'DONE'
GROUP BY user_email
ORDER BY tib_billed DESC

Who is running them. The user_email column is the whole diagnosis. A person's address at the top means somebody is exploring data by hand and can be taught in ten minutes. A service account at the top means a dashboard or a scheduled query is doing it unattended, which is the expensive case: it never gets bored and stops.

On-demand or Editions: how we decide for a small business

BigQuery has two ways to charge for compute. On-demand bills the bytes your queries read. Editions sells processing capacity measured in slots, with a baseline you always hold and a maximum the autoscaler can reach, billed per slot-hour, across the Standard, Enterprise and Enterprise Plus tiers. The rates sit on the same pricing page; check them for your region rather than trusting a figure from a blog, because they differ by tier and by commitment.

For almost every small business we work with, on-demand wins and the question is not close. Editions earns its keep on sustained, concurrent load that keeps capacity busy for hours at a time. A nightly import and a dashboard eight people open in the morning is not that. One detail matters more than it looks: the custom query quotas described below apply only to the on-demand model, so moving to Editions to control costs quietly removes the hard stop you were relying on.

The dashboard that reads the whole table every morning

This is the single most common cause of a surprise BigQuery bill in a company that is not a data company. The mechanics are worth understanding because the fix takes minutes.

A Looker Studio report is not one query. Each chart issues its own, and every chart not answered from cache goes back to BigQuery. If the report points straight at a raw table and the charts carry no date filter BigQuery can use, each refresh reads the whole table. Eight people opening that report before nine in the morning, on a table that grows every night, is a bill that rises on its own.

Three changes, in the order we make them:

  • Lengthen data freshness. Looker Studio keeps query results in memory and serves repeat queries from there within the freshness window, which both speeds the report up and avoids the BigQuery charge. The default for a BigQuery data source is 12 hours. For a report built on data that only lands overnight, a long window costs the reader nothing and saves most of the queries.
  • Use one reusable data source with owner's credentials. Reports built on embedded data sources, each with viewer credentials, multiply refreshes. One shared data source means one set of refreshes for everyone.
  • Point the report at a summary, not at the raw table. A scheduled query that rolls yesterday's orders into a small aggregate table, or a materialized view the dashboard reads instead, turns a full scan into something trivial. This is usually the change that moves the invoice, and it is an hour of work.

Partition and cluster once, pay less every day after

A partitioned table is split into segments, usually by date. When a query filters on the partitioning column, BigQuery reads only the segments it needs. Clustering then sorts rows inside each partition by up to four columns, so filters on those columns skip blocks as well. On a table with two years of orders, the difference between a filtered read and a full scan is not a few percent.

The part most teams miss is the option that makes it stick:

CREATE TABLE analytics.orders (
  order_id   STRING,
  customer_id STRING,
  order_ts   TIMESTAMP,
  total      NUMERIC
)
PARTITION BY DATE(order_ts)
CLUSTER BY customer_id
OPTIONS (
  require_partition_filter = TRUE
);

With require_partition_filter set, a query that does not filter on the partitioning column is rejected before any data is read, and you are not charged for it. On an existing table:

ALTER TABLE analytics.orders
SET OPTIONS (require_partition_filter = TRUE);

Turn that on and the accidental full scan becomes an error somebody has to fix instead of a line on the invoice. Warn the team first: reports and scripts that relied on the full scan break the same morning. That is the point, but it should not be a surprise.

While you are in there: SELECT * is the most expensive way to ask any question, because it scans every column including the ones nobody reads. Naming the columns you need is often the second biggest saving after partitioning.

Two ceilings we set before we hand a project back

Maximum bytes billed, on the queries that matter. BigQuery estimates the bytes a query will read before it runs, and if the estimate is over the limit the query fails without a charge. Google's own guidance is to start small and raise it as needed. From the command line:

bq query --use_legacy_sql=false --maximum_bytes_billed=2000000000 \
  'SELECT order_id, total FROM analytics.orders WHERE DATE(order_ts) = CURRENT_DATE()'

A daily quota on the project. In the Cloud console under IAM and Admin, Quotas and System Limits, filter the service to the BigQuery API. Two entries matter: query usage per day, which caps everyone in the project together and defaults to 200 TiB, and query usage per day per user, which applies separately to each user and service account and defaults to unlimited. Daily quotas reset at midnight Pacific time. Set the project ceiling at a number honest work would never reach, which for a small business is a few TiB a day.

Budget alerts tell you. They do not stop you

Every small business we audit has a Cloud Billing budget, and most owners believe it is a cap. It is not. An alerts-only budget does not cap usage or spending; it sends email to the recipients you choose when actual or forecasted costs cross the thresholds you set. The spending continues.

A real cap does exist, just not for BigQuery. Spend cap budgets, which pause usage of a service once costs pass 100 percent of the budget, have been available in Preview since 27 July 2026 for a limited set of services, among them the Gemini API, the Gemini Enterprise Agent Platform, Cloud Run and Cloud Run functions. BigQuery is not on that list as of 6 October 2026, and a feature in Preview is not something we would build a client's cost control on in any case. For BigQuery, the custom query quota above is the stop. The budget is the smoke alarm.

Set both. Point the alert at a group address rather than one person's inbox, and add a forecasted-spend threshold as well as an actual-spend one, so the warning arrives while the month can still change.

What we watch in the first month

Run the INFORMATION_SCHEMA query above once a week for the first month and read it as a trend. Three signs tell you the fix held: bytes billed per day flatten instead of climbing, the service accounts that used to top the list fall below the humans, and no new scheduled query appears that nobody remembers creating. Check once that the tables you expect to be idle really crossed into long-term storage. If one has not, something is still touching it every night.

How Guanacos Tech helps

Most of the BigQuery bills we are asked to look at are not a design problem. They are four settings nobody was told about, on a project set up to answer one question and then left alone. We read the billing export and the job history, find the query doing the damage, partition and summarise the table it reads, and put the ceilings in place so the next surprise is a rejected query instead of an invoice. If that sounds like your project, our Google Cloud consulting for small teams starts with a 30-minute call where we look at what your bill is made of before anyone proposes anything.

Sources

Next step

Would you rather we did this for you?

Thirty minutes on Google Meet, free. We look at your domain or project with you, tell you what is wrong and what we would do first. If you can fix it yourself, we say so.

Book a 30-minute call or read about our Google Cloud consulting

Frequently asked questions

Why did my BigQuery bill go up when nothing changed?

Under on-demand pricing you pay for the bytes each query reads, so the same query costs more every month as the table it reads grows. A dashboard or a scheduled query that scans a full table is the usual cause, because it runs on a schedule and nobody has to do anything for the cost to rise. Check the job history in INFORMATION_SCHEMA and look at which account is reading the most bytes.

Does a Cloud Billing budget stop BigQuery from spending more?

No. An alerts-only budget sends email when actual or forecasted costs cross your thresholds, and it does not cap usage or spending. Spend cap budgets, which do pause a service, were announced as a Preview on 27 July 2026 for a limited set of services, and BigQuery is not among them as of 6 October 2026. For BigQuery the hard stop is a custom query quota on the project, set under Quotas and System Limits for the BigQuery API.

Is BigQuery Editions cheaper than on-demand for a small company?

Usually not. Editions bills reserved and autoscaled capacity by the slot-hour, which pays off when load is sustained and concurrent for hours at a time. A nightly data load and a morning dashboard leave that capacity idle. There is also a trade-off that is easy to miss: custom query quotas, the only hard daily ceiling on query spend, apply to the on-demand model only.