ALL POSTS FinOps

Snowflake Cost Monitoring: From QUERY_HISTORY to a Bill You Can Explain

How to build real cost visibility from ACCOUNT_USAGE, where resource monitors and budgets fit, and why monitoring explains the bill but sits outside the query path.

DK Dan KowalskiSystems engineering, OSO Jun 14, 2026 9 min read
team=growthteam=ml team=bizopsteam=core

Snowflake cost monitoring means turning the account’s own telemetry into a bill you can explain. Everything you need is already in the SNOWFLAKE.ACCOUNT_USAGE schema: WAREHOUSE_METERING_HISTORY for credits per warehouse, QUERY_HISTORY for per-query attribution, and METERING_DAILY_HISTORY for the daily account total. Add resource monitors and budgets for alerting, and you can see — to the credit — where the money goes. What monitoring can’t do is spend less for you.

Most teams discover their Snowflake bill is too high long before they can say why. The fix is not another invoice line item; it’s a habit of querying the account’s metering views, attributing spend, and watching trends with alerts. This post walks the views that matter, the difference between resource monitors and budgets, a set of starter cost queries you can paste in today, and the honest limit of dashboards — they explain the bill, but they sit outside the query path and never act on it.

01 / THE VIEWS THAT MATTERWhich ACCOUNT_USAGE views should I monitor?

Snowflake bills you for compute (credits burned by warehouses) and a smaller amount for storage and cloud services. For cost monitoring, three ACCOUNT_USAGE views carry almost all the signal:

ViewGrainWhat it answers
WAREHOUSE_METERING_HISTORYHourly, per warehouseWhich warehouses burn the most credits, and when
QUERY_HISTORYPer queryWho ran what, on which warehouse, scanning how much, for how long
METERING_DAILY_HISTORYDaily, whole accountTotal credits/day across compute, storage, and cloud services

A practical split: METERING_DAILY_HISTORY is your top-line trend line, WAREHOUSE_METERING_HISTORY tells you which warehouse moved it, and QUERY_HISTORY lets you drill all the way to the user, app, BI tool, or dbt model that owns the spend. (For chargeback mechanics, see Snowflake cost attribution and chargeback.)

Mind the latency. ACCOUNT_USAGE views have ingestion lag — typically up to ~45 minutes for metering and ~45 minutes for query history, with up to 24 hours of total data freshness. They’re authoritative for trends and chargeback, not for real-time interception. That latency is exactly why monitoring observes but can’t act in flight.

02 / RESOURCE MONITOR VS BUDGETResource monitor vs budget — what’s the difference?

These two controls get conflated constantly, but they do different jobs.

A resource monitor is an enforcement control. You give it a credit quota over an interval (e.g. monthly) and attach triggers: at 75% of quota, notify; at 90%, notify; at 100%, suspend the warehouse after running queries finish; at 110%, suspend immediately and cancel running queries. It can actually stop compute. The cost: it’s blunt — it kills the warehouse, not the wasteful workload.

A budget is a tracking and alerting control. You set a spend target, attach the objects (warehouses, databases, etc.) you want to watch, and Snowflake notifies account admins when projected or actual spend crosses the target. A budget never suspends anything. It’s the early-warning system; the resource monitor is the circuit breaker.

Use a resource monitor to stop runaway compute. Use a budget to see the runaway coming. Neither makes the underlying workload cheaper.

— rule of thumb
Resource monitorBudget
Primary jobCap & suspend computeTrack spend vs target
Can suspend warehouses?Yes (at thresholds)No
GranularityAccount or per-warehouseGroup of objects
Best forHard guardrailsTrend alerts, forecasting

In practice you want both: budgets so finance gets warned early, resource monitors so a runaway dashboard or a misfiring ELT job can’t drain a month of credits overnight.

03 / STARTER COST QUERIESWhat SQL should I run first?

Two queries cover 80% of the first investigation: which warehouses cost the most, and which queries are the most expensive to run. Both read straight from ACCOUNT_USAGE.

-- Top warehouses by credits, last 30 days
SELECT
    warehouse_name,
    ROUND(SUM(credits_used), 1)              AS credits,
    ROUND(SUM(credits_used) * 3.00, 0)       AS approx_usd   -- swap in your $/credit
FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY
WHERE start_time >= DATEADD('day', -30, CURRENT_TIMESTAMP())
GROUP BY warehouse_name
ORDER BY credits DESC;
-- Most expensive query shapes, last 30 days (by total execution time)
SELECT
    LEFT(query_text, 80)                                  AS query_preview,
    COUNT(*)                                              AS runs,
    ROUND(SUM(execution_time) / 1000 / 60, 1)            AS total_exec_minutes,
    ROUND(AVG(bytes_scanned) / POWER(1024, 3), 2)        AS avg_gb_scanned,
    SUM(IFF(warehouse_size IS NOT NULL, 1, 0))           AS warehouse_runs
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE start_time >= DATEADD('day', -30, CURRENT_TIMESTAMP())
  AND execution_status = 'SUCCESS'
GROUP BY query_preview
ORDER BY total_exec_minutes DESC
LIMIT 50;

The warehouse query gives you the credit leaderboard; the query-shape query tells you whether spend is concentrated in a few monster transforms or — far more common on BI-heavy accounts — spread across thousands of cheap, repeated reads. That distinction decides what to do next, and it’s where how much Snowflake actually costs becomes a question with a real answer.

Monitoring path: ACCOUNT_USAGE views feed a dashboard that explains the bill. The dashboard reads telemetry after the fact — it never sits in the connection between driver and warehouse.

04 / DASHBOARDS EXPLAIN, THEY DON’T ACTWhy does monitoring not reduce my bill?

This is the structural limit every cost dashboard shares — whether it’s a homegrown Looker board, dbt-snowflake-monitoring, or a hosted analytics tool. They read ACCOUNT_USAGE after the credits are spent. By design they sit outside the query path: they can show you that the same dashboard query ran 40,000 times last month, but they can’t serve any of those 40,000 from a cache, can’t suspend the warehouse a few minutes earlier, and can’t rewrite an expensive query shape in flight. Observation and action are different layers.

That’s not a knock on monitoring — you can’t fix what you can’t see, and these queries are the right first move. It’s just the honest ceiling: a report explains the bill; it doesn’t shrink it. To shrink it you need something in the path that can make a decision per query, before the warehouse wakes up.

05 / FROM MONITORING TO IN-PATH ACTIONHow do you turn visibility into savings?

This is the bridge from observing spend to cutting it. chukei is an open source, Apache-2.0, self-hosted Snowflake cost optimization engine — a transparent wire-protocol proxy that runs in your own VPC. Drivers change one hostname and nothing else; your SQL and credentials stay exactly where they are. Because it sits in the query path, it can act on the very patterns your monitoring surfaced:

  • Verified result caching serves deterministic repeated reads from a cache that is continuously double-checked against live Snowflake (soak run: ~120k queries, ~60k cache hits, 0 mismatches).
  • Warehouse auto-suspend uses a Poisson idle model to recommend safe early suspends — it captured ~94% of modelled savings in simulation.
  • Deterministic SQL rewriting applies equivalence-tested rules inline. There is no LLM on the hot path — the decision is deterministic, with ~2 ms p99 added overhead.
  • Per-team cost attribution stamps every query at the wire with the user, app, team, or dbt model that owns it — the same chargeback your QUERY_HISTORY drill-downs were approximating, but live.

Fail open, always. Parse errors, cache misses, non-deterministic SQL, writes, and unsafe result shapes all degrade to a byte-identical passthrough to Snowflake. The cache is false-positive-intolerant — when in doubt, it misses. chukei never breaks a query.

You don’t have to take the savings range on faith. The replay simulator reads a CSV export of your QUERY_HISTORY, simulates every lever offline, and emits an Ed25519-signed evidence file — nothing installed in the path, nothing leaving your machine.

# export a month of history from Snowflake, then:
chukei replay --query-history queries.csv --evidence report.json

 parsed 4,210,773 queries        (31 days)
 cache       deterministic repeats identified
 suspend     idle windows modelled (Poisson)
 projected   savings within 15–30% target band
 wrote signed report.json        · Ed25519

The 15–30% figure is a target to validate via replay, never a guarantee — the real number depends on your workload mix and how well your warehouses are already tuned. Monitoring tells you where the money goes; replay tells you how much of it is recoverable before you change a thing. For the full picture of levers and rollout, start with the cornerstone guide to Snowflake cost optimization.

Key takeaways

  • Monitor from ACCOUNT_USAGE: WAREHOUSE_METERING_HISTORY (credits by warehouse), QUERY_HISTORY (per-query attribution), METERING_DAILY_HISTORY (daily totals).
  • Resource monitor vs budget: a resource monitor caps and suspends compute; a budget tracks and alerts but never suspends. Use both.
  • Start with two queries: top warehouses by credits, and most expensive query shapes — they reveal whether spend is a few monsters or a thousand repeats.
  • Dashboards explain, they don’t act: they read telemetry after the fact, outside the query path, so they can’t cache, suspend, or rewrite in flight.
  • Bridge to action: an in-path engine like chukei turns the patterns you monitored into savings — validated first with a signed replay, never promised.

Build the visibility first; the queries above are a complete starting point. When you’re ready to act on what you find, run the replay against your own history and read the receipts — it’s all in the repository.

Frequently asked questions

How do I monitor Snowflake costs?
Query the SNOWFLAKE.ACCOUNT_USAGE views — WAREHOUSE_METERING_HISTORY for credits by warehouse, QUERY_HISTORY for per-query attribution, and METERING_DAILY_HISTORY for daily account totals. Layer resource monitors and budgets on top for alerting, and surface the results in a BI dashboard.
What is QUERY_HISTORY used for?
QUERY_HISTORY records every query run on the account — text, user, warehouse, bytes scanned, execution time, and queuing. It's the source of truth for attributing cost to users, teams, BI tools, and dbt models, and for finding the most expensive query shapes.
Resource monitor vs budget — what's the difference?
A resource monitor caps warehouse credit consumption and can notify, suspend, or hard-suspend warehouses when a quota is hit — it's an enforcement control. A budget tracks spend against a target across a group of objects and alerts you, but never suspends anything. Use resource monitors to stop runaway compute; use budgets to watch trends and get warned early.
How do I set a Snowflake spend alert?
Create a resource monitor with a credit quota and a NOTIFY trigger at a percentage threshold, or create a budget and attach it to the objects you want to watch. Both can email account admins; resource monitors can additionally suspend warehouses at higher thresholds.
Does monitoring reduce my Snowflake bill on its own?
No. Monitoring and dashboards observe spend — they explain where credits go but sit outside the query path, so they can't serve a result, suspend an idle warehouse, or rewrite a query in flight. Reducing the bill needs an action layer; monitoring tells you where to point it.
DK
Dan Kowalski

Builds the Rust wire-protocol core of chukei. Spends his time making sure the proxy adds milliseconds, never breakage.

SnowflakeFinOpsMonitoringCost