Snowflake AI budgets warn you about Cortex spend, but they do not stop it. Three other controls can stop it: per-user quotas with blocking turned on, daily limits for Cortex Code, and access grants that decide which roles can call Cortex. A budget can also run your own stored procedure at a threshold. Alerts you write can warn you sooner. Every fact and figure in this guide was checked against Snowflake’s documentation.
This article is part of our guide to Snowflake Cortex AI costs.
Which Snowflake guardrail catches which Cortex spend?
Budgets watch the month and notify you. Per-user quotas stop users within minutes, but only if you turn blocking on. Cortex Code daily limits block one surface per user. Access grants stop new calls, not work already running. Stored procedures you write can stop spend. An agent orchestration budget caps one response. Alerts you write notify you, and stop spend only if you code a cancel into the action.
| Guardrail | Scope | Action at the limit | Delay |
|---|---|---|---|
| Budget notification | Account, tag, or AI object | Soft: email, queue or webhook | Refresh up to 6.5 hours, or 1 hour |
| Budget custom action | Same as the budget | Hard, if your procedure revokes or suspends | Up to 8 hours, or 2 hours |
| Per-user quota, blocking on | Each user, AI domains only | Hard: blocks new AI requests | Minutes |
| Cortex Code daily limit | Each user, each surface | Hard: blocks that surface | Not stated |
| Access grants | Role, function or model | Hard for removed roles, no spend cap | Not stated |
| Your own alert | Any usage view | Soft: email, hard only if you code a cancel | Schedule plus view lag |
However, no guardrail refunds credits already billed, and a blocked user can overshoot a quota limit before the block lands. An agent orchestration budget caps one response by time or by tokens. The token cap counts orchestration only, not tokens used by Cortex Analyst, Cortex Search or other tools.
Which Cortex services can a Snowflake budget track?
The account budget counts all credit usage in the account. A custom budget can also track the AI_SERVICES and AI_INFERENCE service types. In practice, you add Cortex Agents, Snowflake Intelligence and the AI Gateway as objects, or count AI functions, Cortex Code and agents by tagged user. Cortex Analyst has no dedicated budget feature.
Snowflake’s AI cost governance page says budget features are not supported, and not planned, for Cortex Analyst. The account budget still counts its credits. Snowflake’s docs now call Cortex Code “Snowflake CoCo” and Snowflake Intelligence “Snowflake CoWork”. This guide keeps the older names because the budget domains and the daily-limit parameters still use them. The combined usage view already uses the new name, so the queries below name SNOWFLAKE_COCO_USAGE_HISTORY.
Two ways to scope an AI budget
- Shared-resource budget (by team). Tag your users, then add AI domains. It counts only what tagged users consume. Snowflake released it as generally available on April 10, 2026. The AI Gateway budget and quota domain became generally available on November 3, 2026.
- Resource budget (by object). Tag one object, then attach stored procedures at thresholds. Snowflake documents this for Cortex Agents, Snowflake Intelligence and Cortex Search.
For the basics, see our guide to Snowflake budgets vs resource monitors.
How do you build a budget for AI spend?
Create a custom budget, set a monthly limit in credits, tag the users it should count, and add the AI domains as shared resources. Then set notifications. For enforcement, attach a custom action that calls a stored procedure you write. The budget only watches. Your procedure does the stopping.
This follows Snowflake’s page on budgets for AI features. The custom action below revokes access at 100% of actual spend. It fires at most once a month, so you must pair it with an action that restores access at the start of the cycle, as the last lines do. Otherwise the role stays locked out. Test both in a non-production account first.
-- Role: holds the database roles SNOWFLAKE.BUDGET_CREATOR and SNOWFLAKE.USAGE_VIEWER, CREATE SNOWFLAKE.CORE.BUDGET and USAGE on the schema, and APPLYBUDGET on the tag.
-- Creates a budget that counts AI use by tagged users only. Returns confirmations, not usage.
USE SCHEMA budgets_db.budgets_schema;
CREATE SNOWFLAKE.CORE.BUDGET ai_budget();
CALL ai_budget!SET_SPENDING_LIMIT(5000); -- illustrative monthly limit in credits
CALL ai_budget!SET_USER_TAGS(
[[(SELECT SYSTEM$REFERENCE('TAG', 'cost_mgmt_db.tags.cost_center', 'SESSION', 'APPLYBUDGET')), 'ANALYTICS']],
'UNION');
CALL ai_budget!ADD_SHARED_RESOURCE('AI FUNCTION');
CALL ai_budget!ADD_SHARED_RESOURCE('CORTEX CODE');
CALL ai_budget!ADD_SHARED_RESOURCE('CORTEX AGENT');
-- Assumes budgets_notification_integration exists and the addresses are verified.
GRANT USAGE ON INTEGRATION budgets_notification_integration TO APPLICATION SNOWFLAKE;
CALL ai_budget!SET_EMAIL_NOTIFICATIONS('budgets_notification_integration', 'costadmin@example.com');
CALL ai_budget!SET_NOTIFICATION_THRESHOLD(80);
-- Needs revoke_ai_access(role_name STRING), EXECUTE AS OWNER, created first. It removes ai_users_role access at 100% of actual spend.
GRANT USAGE ON DATABASE budgets_db TO APPLICATION SNOWFLAKE;
GRANT USAGE ON SCHEMA budgets_db.budgets_schema TO APPLICATION SNOWFLAKE;
GRANT USAGE ON PROCEDURE budgets_db.budgets_schema.revoke_ai_access(STRING) TO APPLICATION SNOWFLAKE;
CALL ai_budget!ADD_CUSTOM_ACTION(
SYSTEM$REFERENCE('PROCEDURE', 'budgets_db.budgets_schema.revoke_ai_access(string)'),
ARRAY_CONSTRUCT('ai_users_role'), 'ACTUAL', 100);
-- Needs restore_ai_access(role_name STRING), EXECUTE AS OWNER, created first. It gives ai_users_role its access back at the start of each budget month.
GRANT USAGE ON PROCEDURE budgets_db.budgets_schema.restore_ai_access(STRING) TO APPLICATION SNOWFLAKE;
CALL ai_budget!SET_CYCLE_START_ACTION(
SYSTEM$REFERENCE('PROCEDURE', 'budgets_db.budgets_schema.restore_ai_access(string)'),
ARRAY_CONSTRUCT('ai_users_role'));
Custom action rules and delays
Snowflake’s custom actions page sets these rules:
- The procedure runs with owner’s rights, finishes within 30 minutes, and is retried once if it fails. A budget holds up to 10 custom actions.
- An actual-spend action runs at most once a month. A projected-spend action runs at most once a day.
Therefore a revoke at 100% will not fire again after you restore access. Snowflake’s agent budget page adds a second revoke at 200% for that case, and allows thresholds up to 500%. Access stays revoked until you restore it. A cycle-start action can restore it on the first of the month.
The budget refreshes up to every 6.5 hours, or hourly on the low latency tier, which multiplies its compute cost by 12. Snowflake says an action can take up to eight hours to take effect after a threshold is exceeded, or up to two hours with the latency-optimized option.
How do per-user quotas block Cortex spend?
A per-user quota gives users in scope the same credit limit per day, week or month. With blocking turned on, it blocks a user who reaches it. Blocking covers AI functions, Cortex Agents, Snowflake Intelligence, Cortex Code and the AI Gateway, not warehouses. Snowflake calls quotas the only budget-feature control with built-in enforcement. Cortex Code daily limits also block.
Per-user quotas reached general availability in the 10.29 release. The per-user quotas page lists what to expect:
- A user is blocked at the first limit reached, until the next cycle reset at 00:00 UTC.
- For AI functions, calls in progress are terminated. For the AI Gateway, running requests finish and are billed.
- Blocks land within minutes, and scope or limit changes take 5 to 10 minutes to propagate.
- One quota watches warehouses or AI domains, never both.
Scope the quota before you turn blocking on. A new quota covers all users, including service and pipeline users. With blocking on, any user who passes the limit is blocked, and that user’s in-progress AI function calls are ended.
-- Role: ACCOUNTADMIN, or a role with SNOWFLAKE.QUOTA_CREATOR and CREATE SNOWFLAKE.CORE.QUOTA on the schema, and APPLYBUDGET on the tag.
-- Creates a quota that blocks users in scope at 100 AI credits a day. Applies to ALL users until you scope it. Returns confirmations, not usage.
USE SCHEMA cost_mgmt_db.quota_schema;
CREATE SNOWFLAKE.CORE.QUOTA ai_user_quota();
CALL ai_user_quota!ADD_SHARED_RESOURCE('AI FUNCTION');
CALL ai_user_quota!ADD_SHARED_RESOURCE('CORTEX AGENT');
CALL ai_user_quota!ADD_SHARED_RESOURCE('CORTEX CODE');
-- A new quota covers ALL users, including service users. Scope it first.
CALL ai_user_quota!SET_USER_TAGS(
[[(SELECT SYSTEM$REFERENCE('TAG', 'cost_mgmt_db.tags.cost_center', 'SESSION', 'APPLYBUDGET')), 'ANALYTICS']],
'UNION');
-- or keep all users and exclude service users:
-- CALL ai_user_quota!EXCLUDE_USERS('USER', ['ETL_SERVICE_USER']);
CALL ai_user_quota!SET_PER_USER_LIMIT(100, 'DAILY');
-- Snowflake lists 'DAILY' here, but Snowsight offers alert notifications only with a monthly limit. Test this call alone.
CALL ai_user_quota!ADD_NOTIFICATION_THRESHOLD(80, 'ACTUAL', TRUE, 'DAILY');
-- Wait about 10 minutes after the scope is set, then turn blocking on.
CALL ai_user_quota!SET_BLOCK_ENFORCEMENT_ENABLED(TRUE, TRUE);
A per-user limit is not a budget
Limits are per user and never pooled. Snowflake’s example: a 100-credit quota across 10 users permits up to 1,000 credits in total. Illustratively, with 40 users and a 25-credit daily limit, the nominal ceiling is 1,000 credits a day, or 30,000 over 30 days. That is three times a 10,000-credit monthly budget, so pair the two. Overshoot can push real spend higher.
How do Cortex Code daily limits work?
Three account or user parameters cap estimated Cortex Code credits over a rolling 24 hours: one each for the CLI, Desktop and Snowsight. The default is -1, which means no limit. A value of 0 blocks the surface. A user-level value overrides the account value.
Snowflake’s daily credit usage limits page documents them. See our Cortex Code guide for the product and how to manage Cortex Code cost for a ledger.
-- Role: ACCOUNTADMIN, or a role that can modify the account and the user.
-- Applies to every user in the account. A user above 20 estimated credits in 24 hours is blocked on that surface. Try ALTER USER on one user first.
ALTER ACCOUNT SET CORTEX_CODE_CLI_DAILY_EST_CREDIT_LIMIT_PER_USER = 20;
ALTER ACCOUNT SET CORTEX_CODE_DESKTOP_DAILY_EST_CREDIT_LIMIT_PER_USER = 20;
ALTER ACCOUNT SET CORTEX_CODE_SNOWSIGHT_DAILY_EST_CREDIT_LIMIT_PER_USER = 20;
ALTER USER power_user SET CORTEX_CODE_CLI_DAILY_EST_CREDIT_LIMIT_PER_USER = 50;
These limits count estimates, and Snowflake states no delay. A user also needs CORTEX_USER or CORTEX_AGENT_USER.
Which access controls limit who can spend on Cortex?
Create the replacement role first and grant it to every user and service role that needs Cortex. Test in a non-production account, as a role other than ACCOUNTADMIN. Only then revoke CORTEX_USER from PUBLIC, and keep the rollback grant ready. That role also opens Analyst, Agents, Cortex Code and Search, so a careless revoke can break all of them.
CORTEX_USER also opens fine-tuning, so removing it from PUBLIC can break all of these features for roles that hold no narrower role. Embeddings need a separate CORTEX_EMBED_USER grant once CORTEX_USER is revoked, or embedding functions and managed-embedding Search services break. AI functions have a second gate, USE AI FUNCTIONS, which PUBLIC holds by default.
-- Role: ACCOUNTADMIN. Run this whole block in a non-production account first and test after the revokes, then repeat in production.
-- CORTEX_USER also opens Analyst, Agents, Cortex Code, Cortex Search and fine-tuning, so the revoke affects all of them.
CREATE ROLE cortex_user_role;
GRANT DATABASE ROLE SNOWFLAKE.CORTEX_USER TO ROLE cortex_user_role;
GRANT USE AI FUNCTIONS ON ACCOUNT TO ROLE cortex_user_role;
GRANT ROLE cortex_user_role TO USER some_user;
-- Grant cortex_user_role to every role that still needs Cortex (pipelines, services) before you continue.
-- Embeddings: grant SNOWFLAKE.CORTEX_EMBED_USER to those roles too (to roles, not to users).
REVOKE DATABASE ROLE SNOWFLAKE.CORTEX_USER FROM ROLE PUBLIC;
REVOKE USE AI FUNCTIONS ON ACCOUNT FROM ROLE PUBLIC;
-- Test now, as a role other than ACCOUNTADMIN (USE SECONDARY ROLES NONE): a role that holds cortex_user_role must still work, and a role without it must fail.
-- Call an AI function, Cortex Analyst, an agent and Cortex Code. If anything breaks, run the undo below.
-- To undo:
-- GRANT DATABASE ROLE SNOWFLAKE.CORTEX_USER TO ROLE PUBLIC;
-- GRANT USE AI FUNCTIONS ON ACCOUNT TO ROLE PUBLIC;
- Narrower roles exist. AI_FUNCTIONS_USER covers scalar AI functions only, CORTEX_ANALYST_USER covers Cortex Analyst only, and CORTEX_AGENT_USER covers agents and Cortex Code.
- PUBLIC has a second path. The SNOWFLAKE.PUBLIC application role is granted to PUBLIC, and a plain REVOKE does not remove what it grants. That can include USE AI FUNCTIONS and the all-models role. Check with SHOW GRANTS TO APPLICATION ROLE SNOWFLAKE.PUBLIC. To remove an item, grant it directly to the roles that need it first, then call SNOWFLAKE.LOCAL.REVOKE_FROM_PUBLIC_APPLICATION_ROLE. Re-run the check after the revokes to confirm.
- A second revoke needs care. Snowflake’s page adds
REVOKE IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE FROM ROLE PUBLIC. The usage queries below rely on imported privileges on that database, so run SHOW GRANTS TO ROLE PUBLIC first to see what it would remove. - Cross-region inference is a routing choice. Cortex Code needs it enabled. The Consumption Table lists a higher on-demand AI Credit price for Regional than for Global (Table 2(b)).
Which usage views show Cortex spend, and how late?
Each Cortex service writes to its own Account Usage view. AI functions are the freshest, at 2 to 5 minutes. Most others lag up to an hour, and METERING_HISTORY lags up to three hours. Run the queries below as a role with imported privileges on the SNOWFLAKE database. The USAGE_VIEWER database role covers the usage views, but the per-user query also reads the USERS view, which needs SECURITY_VIEWER.
| View | Covers | Latency |
|---|---|---|
| CORTEX_AI_FUNCTIONS_USAGE_HISTORY | AI functions | 2 to 5 minutes |
| CORTEX_AGENT_USAGE_HISTORY | Cortex Agents | Up to 1 hour |
| SNOWFLAKE_COCO_USAGE_HISTORY | Cortex Code | Up to 1 hour |
| CORTEX_ANALYST_USAGE_HISTORY | Cortex Analyst | 1 hour |
| CORTEX_SEARCH_SERVING_USAGE_HISTORY | Search serving | 1 hour |
| CORTEX_SEARCH_DAILY_USAGE_HISTORY | Search totals | 3 hours |
| SNOWFLAKE_COWORK_USAGE_HISTORY | Snowflake Intelligence | 1 hour |
| METERING_HISTORY | All services, hourly | Up to 3 hours |
Do not add the views together, because Snowflake says Search embedding credits also appear in the AI functions view.
Snowflake’s pages disagree in two places. First, they are inconsistent about which METERING_HISTORY service type holds each Cortex service. The budgets page ties AI_SERVICES to Snowflake Intelligence and Cortex Agent. The METERING_HISTORY page says AI_SERVICES covers AI functions and Cortex Analyst. The governance page labels the AI functions view AI_FUNCTIONS. Second, they are inconsistent about whether the Search serving view includes embeddings. Its page lists serving credits only, while the governance page says the Search total includes embedding costs and names the daily view as the total. So check the service types in your own account before you filter.
Avoid the older CORTEX_FUNCTIONS_USAGE_HISTORY and CORTEX_AISQL_USAGE_HISTORY. Snowflake’s governance page slates them for deprecation on November 22, 2026 and January 15, 2027, respectively. The queries below use the replacement, CORTEX_AI_FUNCTIONS_USAGE_HISTORY. For agents, see Cortex agent token-level costs.
-- Role: imported privileges on the SNOWFLAKE database, or USAGE_VIEWER (plus SECURITY_VIEWER for the per-user query, which reads USERS).
-- Credits per day by service type, 30 days. Up to 3 hours behind.
-- Snowflake's pages disagree on which Cortex service sits under AI_SERVICES, so do not filter yet.
SELECT DATE_TRUNC('day', start_time) AS usage_day, service_type, SUM(credits_used) AS credits
FROM SNOWFLAKE.ACCOUNT_USAGE.METERING_HISTORY
WHERE start_time >= DATEADD('day', -30, CURRENT_TIMESTAMP())
GROUP BY usage_day, service_type
ORDER BY usage_day DESC, credits DESC;
-- AI function credits per user, last 24 hours (every hourly window that overlaps them). 2 to 5 minutes behind.
SELECT u.name AS user_name, SUM(h.credits) AS credits
FROM SNOWFLAKE.ACCOUNT_USAGE.CORTEX_AI_FUNCTIONS_USAGE_HISTORY h
JOIN SNOWFLAKE.ACCOUNT_USAGE.USERS u ON h.user_id = u.user_id
WHERE h.start_time >= DATEADD('hour', -25, CURRENT_TIMESTAMP())
GROUP BY u.name
ORDER BY credits DESC;
-- Cortex Code credits per user and surface, last 7 days. Up to 1 hour behind.
SELECT user_name, interface, SUM(token_credits) AS credits
FROM SNOWFLAKE.ACCOUNT_USAGE.SNOWFLAKE_COCO_USAGE_HISTORY
WHERE usage_time >= DATEADD('day', -7, CURRENT_TIMESTAMP())
GROUP BY user_name, interface
ORDER BY credits DESC;
How do you build a do-it-yourself alert for Cortex spend?
Write a Snowflake alert: a schedule, a condition query on one of the usage views, and an action such as SYSTEM$SEND_EMAIL. The alert runs on a warehouse you name or on serverless compute, and email needs a notification integration. An alert tells you. It only stops spend if you code a cancel.
This is a fast native signal, because the AI functions view is only minutes behind. The alert checks on the schedule you set, here hourly. It sums all queries, so it detects a jump in spend, not one query. The example follows Snowflake’s alerts guide. Test it with a low threshold in a non-production account first.
-- Role: CREATE ALERT and USAGE on the schema, EXECUTE ALERT and EXECUTE MANAGED ALERT (this alert is serverless),
-- CREATE INTEGRATION, and imported privileges on the SNOWFLAKE database.
-- Emails when AI function credits in the previous and current hourly windows pass 200 (illustrative). Checks hourly.
-- One spike can email twice, because two runs see the same window.
-- Windows are UTC hours, so the filter counts elapsed hours, not DATE_TRUNC (which follows the session time zone).
CREATE NOTIFICATION INTEGRATION ai_cost_alerts
TYPE = EMAIL
ENABLED = TRUE
ALLOWED_RECIPIENTS = ('finops@example.com');
CREATE OR REPLACE ALERT ai_functions_hourly_alert
SCHEDULE = '60 MINUTE' -- no WAREHOUSE line, so this is a serverless alert
IF (EXISTS (
SELECT 1
FROM SNOWFLAKE.ACCOUNT_USAGE.CORTEX_AI_FUNCTIONS_USAGE_HISTORY
WHERE start_time >= DATEADD('hour', -2, CURRENT_TIMESTAMP())
HAVING SUM(credits) > 200
))
THEN CALL SYSTEM$SEND_EMAIL('ai_cost_alerts', 'finops@example.com',
'Cortex AI function spend', 'AI function credits passed 200 in the last two hourly windows.');
ALTER ALERT ai_functions_hourly_alert RESUME; -- alerts start suspended
- Email. Each recipient must be in ALLOWED_RECIPIENTS, in the SYSTEM$SEND_EMAIL call, and on a user with a verified email.
- Cancelling. For a single runaway query, Snowflake’s cost guide shows a task that calls SYSTEM$CANCEL_QUERY on running queries above a credit threshold.
How Seemore helps with Cortex spend
A head of data still asks a wider question: which pipeline, dashboard, cost domain, or business unit drove the spike? Seemore connects spend to upstream jobs, downstream dashboards, ownership, and business usage across the wider data stack, which is the gap many teams still have after native Snowflake setup. Its focus is end-to-end visibility, owner-level cost attribution, lineage, and anomaly detection, with cross-stack cost context from ingestion through BI.
Two Seemore features map directly to the controls above. Once Seemore’s helper procedures are installed, Cortex Code Cost Control sets the three Cortex Code daily limit parameters for you, as an account default or per user and separately for Snowsight, the CLI and Desktop, and shows Cortex Code cost by user with each user’s average daily and projected annual cost. Budget Groups can track AI Services spend over a daily, weekly or monthly window and alert a Slack or Microsoft Teams channel or an email address at the percentages you set. Like a Snowflake budget, a Budget Group warns rather than stops spend.
Frequently asked questions
How do Snowflake AI budgets work for Cortex spend?
A Snowflake AI budget sets a monthly credit limit for an account, a tagged object or tagged AI users. It notifies you when spend is projected to pass the limit. It does not stop spend, but a custom action can run your stored procedure.
Can Snowflake block a user who overspends on Cortex?
Yes, once you turn blocking on. A per-user quota then stops new AI requests from a user who reaches a daily, weekly or monthly credit limit. A new quota covers all users, so scope it first. Blocking covers AI functions, agents, Snowflake Intelligence, Cortex Code and the AI Gateway, not warehouses.
Does Snowflake have a spending limit for Cortex Code?
Yes. Three parameters, one each for the CLI, Desktop and Snowsight, cap estimated credits per user over a rolling 24 hours. The default is -1, which means no limit, and 0 blocks the surface.
How do you restrict who can use Cortex in Snowflake?
Grant CORTEX_USER to named roles first, test in a non-production account, then revoke it from PUBLIC. It also opens Analyst, agents, Cortex Code and Search. Check SNOWFLAKE.PUBLIC, because a plain REVOKE does not remove what that role grants. For models, use role grants, because the model allowlist is being deprecated.
Are Snowflake AI budgets enough to catch a runaway Cortex query?
Not alone. Budgets refresh up to every 6.5 hours, and AI budget actions can take up to eight hours. A per-user quota with blocking ends AI function calls in progress once it blocks the user. The AI functions view lists running queries within 2 to 5 minutes, so an alert on a short schedule can see them.
How late is Cortex usage data in Snowflake?
AI function usage appears within 2 to 5 minutes. The agent, Cortex Code, Analyst and Search serving views run up to an hour behind. The hourly METERING_HISTORY view lags up to three hours.