A Snowflake bill that grows 30% a quarter ends the year at 2.86 times its starting size. Snowflake's own filing gives a baseline for comparison. Existing customers spend about 26% more over a full year, which is about 6% a quarter.
Growth at five times that pace usually comes from several small drivers at once. Check six drivers. The first two are idle warehouses and oversized warehouses. The next two are dbt models rebuilt in full and repeated scans. The last two are serverless and AI service lines, and Gen2 pricing. Each one shows up in a SNOWFLAKE.ACCOUNT_USAGE view, and each section below gives the query that finds it.
Your Snowflake bill went up again this quarter, and finance wants a reason. Suppose it grew 30%. We found no source that says a typical account grows at that rate, so treat 30% as your scenario. The point of this post is to find which part of the bill moved.
Start with the arithmetic, because 30% a quarter compounds fast. A $100,000 quarter becomes $130,000, then $169,000, then $219,700, then $285,610. The bill passes double its starting size in the third quarter. After four quarters it is 2.86 times the start.
Now compare that with a measured baseline. Snowflake reported a net revenue retention rate of 126% for the second quarter of fiscal 2027, in its SEC earnings exhibit. Net revenue retention compares what the same customers spend now with what they spent a year earlier. So existing customers spend about 26% more per year, which compounds to about 6% a quarter.
Snowflake counts only customers on capacity contracts in that rate, so treat it as a proxy. Some of that growth is new work that somebody approved. The job is to split the approved growth from the drift, and the six sections below each find one kind of drift.
A Snowflake bill growing 30% a quarter pulls away from the pace Snowflake's existing customers show, and the gap widens every quarter.
A Snowflake Bill Growing 30% a Quarter Outpaces Snowflake's Own Customer Base
| Quarter | Your bill at 30% a quarter | Existing-customer pace, 126% a year | Snowflake product revenue pace, 37% a year |
|---|---|---|---|
| Start | 100 | 100 | 100 |
| Q1 | 130 | 106.0 | 108.2 |
| Q2 | 169 | 112.2 | 117.0 |
| Q3 | 219.7 | 118.9 | 126.6 |
| Q4 | 285.6 | 126.0 | 137.0 |
The same filing reports product revenue of $1.49 billion, up 37% year over year. That figure includes new customers, so it sits above the retention pace. It also reports 828 customers with more than $1 million in trailing 12-month product revenue.
The two Snowflake columns are our own quarterly conversions of the annual figures. Snowflake publishes only the annual rates. A bill on the 30% path is not normal for the customer base, so it is worth an hour of diagnosis.
Split the Snowflake Bill by Service Type Before You Blame a Warehouse
Warehouse compute is one line of the Snowflake bill among many. The METERING_HISTORY view splits credits by SERVICE_TYPE. The values include WAREHOUSE_METERING, AUTO_CLUSTERING, SEARCH_OPTIMIZATION, MATERIALIZED_VIEW, PIPE, SERVERLESS_TASK, QUERY_ACCELERATION and AI_SERVICES.
Run this first. It shows which line grew, month by month:
SELECT
DATE_TRUNC('month', start_time) AS usage_month,
service_type,
ROUND(SUM(credits_used), 1) AS credits
FROM SNOWFLAKE.ACCOUNT_USAGE.METERING_HISTORY
WHERE start_time >= DATEADD(month, -6, DATE_TRUNC('month', CURRENT_DATE()))
GROUP BY usage_month, service_type
ORDER BY usage_month, credits DESC;
The view has up to three hours of latency. Its cloud services column can lag by up to six hours.
Cloud services credits need one more step. Snowflake charges cloud services only when daily cloud services use exceeds 10% of daily warehouse use. The raw CREDITS_USED_CLOUD_SERVICES number is therefore larger than what you pay on most days.
If WAREHOUSE_METERING grew fastest, read the next four sections. If a serverless line or AI_SERVICES grew fastest, skip to the serverless section.
Idle Warehouses Bill Every Second Until AUTO_SUSPEND Runs
An idle warehouse bills as if it were working. The default AUTO_SUSPEND is 600 seconds, per the CREATE WAREHOUSE reference. A background process checks for idle warehouses about every 30 seconds. A setting of 0 or NULL means the warehouse never suspends.
Short settings have their own cost. Snowflake bills per second with a 60-second minimum, and every resume starts the minimum again. The worked example in our 12 Snowflake cost optimization techniques shows the effect. A warehouse that wakes 200 times a day for 20-second queries does 67 minutes of work and bills 200 minutes.
Snowflake documents an idle-cost query. This version groups idle cost by month to show the trend:
SELECT
DATE_TRUNC('month', start_time) AS usage_month,
warehouse_name,
ROUND(SUM(credits_used_compute), 1) AS compute_credits,
ROUND(SUM(credits_used_compute)
- SUM(credits_attributed_compute_queries), 1) AS idle_credits
FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY
WHERE start_time >= DATEADD(month, -3, DATE_TRUNC('month', CURRENT_DATE()))
AND end_time < CURRENT_DATE()
GROUP BY usage_month, warehouse_name
ORDER BY idle_credits DESC;
Idle credits that grow faster than compute credits point at new warehouses, or at warehouses with more idle gaps. Then check the setting with SHOW WAREHOUSES and read the auto_suspend column. Altimate Lite for Snowflake suspends enabled warehouses when they go idle, and the write-up on idle warehouse spend with Altimate Lite shows the per-warehouse view.
Oversized Warehouses Stay Oversized After the Incident Ends
Each size step doubles the credit rate. The warehouse overview lists Gen1 rates of 1 credit an hour at X-Small, 8 at Large and 16 at X-Large. A team that sizes up for a migration or an incident rarely sizes back down.
A warehouse is a candidate to size down when its queries do not queue, do not spill and finish fast. QUERY_HISTORY carries all three signals:
SELECT
warehouse_name,
warehouse_size,
COUNT(*) AS queries,
APPROX_PERCENTILE(execution_time, 0.95) / 1000 AS p95_exec_seconds,
SUM(queued_overload_time) / 1000 AS queued_seconds,
SUM(bytes_spilled_to_local_storage) / POWER(1024, 3) AS spilled_gb
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE start_time >= DATEADD(day, -14, CURRENT_DATE())
AND warehouse_name IS NOT NULL
GROUP BY warehouse_name, warehouse_size
ORDER BY queries DESC;
Look for a large warehouse with zero queued seconds, zero spill and a short p95. Drop it one size and rerun the same workload. The size-down test is technique 2 in the same techniques post.
Full Table Rebuilds Repeat Last Night's Work on Every Run
A dbt model with the table materialization rebuilds the whole table on every run. The table grows every day, so the rebuild costs a little more every day. That growth is invisible until someone lists the rebuilds.
dbt adds a JSON comment to every query it runs, per the dbt query-comment reference. The comment carries the node_id of the model. On Snowflake, dbt puts the comment at the end of the query. This query ranks rebuilt models by credits over the last 30 days:
SELECT
REGEXP_SUBSTR(q.query_text, '"node_id": "([^"]+)"', 1, 1, 'e') AS dbt_node,
COUNT(*) AS rebuilds,
ROUND(SUM(a.credits_attributed_compute), 2) AS credits,
ROUND(AVG(q.bytes_scanned) / POWER(1024, 3), 1) AS avg_gb_scanned
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY q
JOIN SNOWFLAKE.ACCOUNT_USAGE.QUERY_ATTRIBUTION_HISTORY a
ON a.query_id = q.query_id
WHERE q.start_time >= DATEADD(day, -30, CURRENT_DATE())
AND q.query_type = 'CREATE_TABLE_AS_SELECT'
AND q.query_text ILIKE '%"app": "dbt"%'
GROUP BY dbt_node
ORDER BY credits DESC
LIMIT 25;
A model near the top that appends new rows each day is a candidate for the incremental materialization. QUERY_ATTRIBUTION_HISTORY has up to eight hours of latency. It leaves out idle time and queries of about 100 milliseconds or less, so these credits are a lower bound.
Repeated Scans Pay for the Same Answer Many Times
A short query that runs thousands of times a day can cost more than a slow report that runs once. A list sorted by duration hides that query. Group by query_parameterized_hash instead, so one query with different filter values counts as one shape:
SELECT
q.query_parameterized_hash,
ANY_VALUE(q.warehouse_name) AS warehouse,
COUNT(*) AS runs,
ROUND(SUM(a.credits_attributed_compute), 2) AS credits,
ROUND(SUM(q.partitions_scanned)
/ NULLIF(SUM(q.partitions_total), 0), 2) AS share_of_partitions_read
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY q
JOIN SNOWFLAKE.ACCOUNT_USAGE.QUERY_ATTRIBUTION_HISTORY a
ON a.query_id = q.query_id
WHERE q.start_time >= DATEADD(day, -7, CURRENT_DATE())
GROUP BY q.query_parameterized_hash
ORDER BY credits DESC
LIMIT 25;
Two patterns show up at the top. The first is a shape with many runs, which often means a dashboard refreshes more often than anyone reads it. The second is a share of partitions read close to 1.0, which means the filter does not prune. The Enterprise Platform tracks query cost run by run, with idle time included.
Serverless and AI Service Lines Grow With No Warehouse Attached
Several Snowflake features bill without a warehouse, so the warehouse queries above miss them. Resource monitors work for warehouses only, so they miss these lines too.
Three lines are worth a check each month:
- Query acceleration. New Gen2 warehouses have the Query Acceleration Service turned on by default, with a scale factor of 2. The service bills per second while it runs. Snowflake's own example puts a Medium warehouse at scale factor 5 at up to 20 extra credits an hour.
- Clustering and search optimization. Both run in the background as the table changes. A key set during an evaluation keeps billing after the evaluation ends.
- AI services. Cortex functions bill on the AI_SERVICES line. The breakdown of Cortex AI cost by service splits that line by service, user and model.
This query lists the serverless objects that cost the most last month:
SELECT service_type, database_name, schema_name, name,
ROUND(SUM(credits_used), 2) AS credits
FROM SNOWFLAKE.ACCOUNT_USAGE.METERING_HISTORY
WHERE service_type NOT IN ('WAREHOUSE_METERING', 'WAREHOUSE_METERING_READER')
AND start_time >= DATEADD(month, -1, CURRENT_DATE())
GROUP BY service_type, database_name, schema_name, name
ORDER BY credits DESC
LIMIT 25;
Check each row against a consumer that still exists. Snowflake Budgets reach serverless features that resource monitors miss, and technique 9 in the techniques post covers them.
Gen2 Defaults Raise the Price of Each Warehouse Hour
A new standard warehouse is Gen2 unless you say otherwise. The Gen2 guide says Snowflake defaults to GENERATION = '2' when the statement omits the clause. A warehouse created by a script last quarter may be Gen2 without anyone choosing it.
Gen2 costs more per hour. The Snowflake Service Consumption Table lists 1.35 credits an hour at X-Small on AWS and GCP, and 1.25 on Azure. A Gen1 Large at 8 credits becomes 10.8 credits on AWS. Gen2 saves money only when the same work finishes at least 26% faster.
Find the Gen2 warehouses and their creation dates:
SHOW WAREHOUSES;
SELECT "name", "size", "generation", "created_on", "enable_query_acceleration"
FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()))
WHERE "generation" = '2'
ORDER BY "created_on" DESC;
Compare credits per query before and after each switch. For the choice between tuning a standard warehouse and converting it, read the comparison of Snowflake Adaptive Compute and auto-tuning.
Match Each Driver to the View That Shows It
| Driver | Where it shows | Signal to look for | Fix in the techniques post |
|---|---|---|---|
| Idle warehouses | WAREHOUSE_METERING_HISTORY | idle credits growing month over month | technique 1 |
| Oversized warehouses | QUERY_HISTORY | no queueing, no spill, short p95 | technique 2 |
| dbt full refreshes | QUERY_HISTORY plus QUERY_ATTRIBUTION_HISTORY | the same node rebuilt every run | technique 6 |
| Repeated scans | QUERY_HISTORY plus QUERY_ATTRIBUTION_HISTORY | many runs, partitions read near 1.0 | techniques 5 and 6 |
| Serverless and AI lines | METERING_HISTORY | a non-warehouse service growing fastest | techniques 9 and 10 |
| Gen2 pricing | SHOW WAREHOUSES | generation 2 on recently created warehouses | the Gen2 section |
Each driver needs a person who can act on it. A bill split by warehouse reaches nobody in particular. A bill split by team reaches the person who owns the query. The Enterprise Platform shows the team that owns each credit.
Run the Service Split This Week and Give Each Line an Owner
Run the METERING_HISTORY query for the last six months. Name the one service line that grew fastest, and run the matching query from this post. Write down one owner for each of the top five rows.
Some fixes are one statement. Setting AUTO_SUSPEND from measured idle gaps is one of them. Others need tooling. Altimate Lite for Snowflake suspends idle warehouses and lowers the maximum cluster count on the warehouses you enable. It does not resize warehouses and it does not show per-query cost. A spike that the queries above cannot explain calls for the root cause of a workload spike. Our page on Snowflake query optimization lists what Altimate does for Snowflake cost optimization.
Frequently Asked Questions
We found no source that says so. Snowflake reports a 126% net revenue retention rate, which is about 6% a quarter for existing customers. A bill growing 30% a quarter moves about five times faster.
Start with METERING_HISTORY, grouped by month and SERVICE_TYPE. It separates warehouse compute from clustering, search optimization, query acceleration, serverless tasks and AI services. Then use WAREHOUSE_METERING_HISTORY and QUERY_HISTORY for the warehouse line.
Subtract CREDITS_ATTRIBUTED_COMPUTE_QUERIES from CREDITS_USED_COMPUTE in WAREHOUSE_METERING_HISTORY. Snowflake's docs give this query. The column is NULL for Adaptive Warehouses, so the method works on standard warehouses only.
A model with the table materialization rebuilds the whole table on every run. As the table grows, each rebuild reads and writes more data. The incremental materialization processes only new rows.
No. Snowflake says resource monitors work for warehouses only. Use Snowflake Budgets for serverless features, and check the METERING_HISTORY service lines each month.
