Overview
The metrics routes give the cost and usage data that the SELECT app shows, for Snowflake, BigQuery, Databricks and Tableau. Each route queries one semantic model:spend model for all examples. spend is the consolidated cost of all your platforms, for all services. Use it first for all questions about cost. Use a platform route (for example snowflake-workloads or bigquery-projects) to find which resource in the platform caused the cost.
An API key must have the usage_data:read scope to use the metrics routes.
Your first query
This request gets the daily spend for each platform for the last 30 days:day and connection_type:
Request body
Measures
A measure is a value that SELECT calculates across a group of rows, for example a sum or an average. The name of a measure usually tells you how SELECT calculates it:sum_spend is the sum of spend, and avg_daily_spend is the average spend per day.
If a request has measures and no dimensions, the response has one row: the total for all rows that match the filters.
Dimensions
A dimension is a field that you group by. The response has one row for each unique combination of the values of the dimensions, and each row contains the value of each dimension. The measures in a row are calculated from only the data in that group.dimensions is equivalent to a SQL GROUP BY. More dimensions give more rows, and each row contains less data:
A dimension that is not in
dimensions does not split the rows. For example, ["connection_type"] with a 30-day filter gives the total of the 30 days for each platform, not a row for each day.
Time dimensions
To show a trend, add a time dimension todimensions. The time dimension sets the size of each time bucket:
All dates and hours are in UTC.
Some rules apply to time buckets:
- The first and last buckets can be partial. For example, a
30Drange withweekusually starts and stops in the middle of a week. The first and last rows then contain less data than the other rows. - Filter on
day, and group by the larger bucket. To get monthly spend for the year, filterdaywithYTDand addmonthtodimensions. Do not filter onweek,monthorquarter. - Hour-level data comes from a different table. SELECT keeps spend in a daily table and in an hourly table. If your request uses
hour, or another field that only the hourly table has, SELECT reads the hourly table. Otherwise, SELECT reads the daily table, which is faster. The totals are the same. You do not select the table.
Filters
filter_expression limits the rows that SELECT uses to calculate the measures. It is equivalent to a SQL WHERE. It is a tree: each node has an operator of and or or, and a list of filters. An item in filters can be a filter or another node.
This request gets the spend on Snowflake compute or storage for the last 90 days:
Date ranges
Filter dates on theday field. To use fixed dates, use two filters. Both bounds are inclusive:
in range filter:
Measure filters
measure_filters removes groups after SELECT calculates the measures. It is equivalent to a SQL HAVING. Use it to remove small groups from the response.
This request gets the warehouses that cost more than $1,000 last month:
filter_expression changes the value of each measure. A filter in measure_filters does not change a value. It only removes rows.
Usage groups
A usage group set divides your cost into named groups, for example departments or teams. You define usage group sets in the SELECT app, or with the/v2/usage-group-sets routes. A model that has a usage_group dimension can group or filter by the groups of your sets.
To group by the groups of one set, give the name of the set in path:
path[0]is the name of the set, not its ID.- A
usage_groupdimension with nopathgroups by all your sets at the same time. - To get the cost of one group, use the same form in a filter:
{"field": {"field": "usage_group", "path": ["Department"]}, "operator": "=", "value": "Engineering"}. - To test a set before you save it, send its definition in
usage_group_sets. SELECT then uses that definition in place of your saved sets.
Sort and limit
sort is a list of fields and directions. The first item sorts first. You can sort by a dimension or a measure:
limit is the maximum number of rows in the response. The metrics routes do not paginate: page_token is always null. If a query has more rows than limit, SELECT removes the rows after the limit, and does not return an error. Always set a limit. To get the top N groups, sort by a measure and set limit to N.
Period over period
Some models have measures that compare a period to the period before it:<measure>_previous_period, change_<measure> and percent_change_<measure>. The previous period has the same number of days, and stops on the day before your range starts.
For these measures, the request must have one day range at the top level of filter_expression. Use one in range filter, or one >= filter and one <= filter.
For
QTD on 2026-09-28, the range is 2026-07-01 to 2026-09-28 (90 days). The previous period is the 90 days from 2026-04-02 to 2026-06-30. It is not the previous calendar quarter.Listing rows
To get rows with no grouping, setaggregate to false, and send only dimensions. For example, on snowflake-queries, this gives the individual queries. If the request also has measures or measure_filters, SELECT groups the rows, and does not return an error. Always set a limit when you list rows.
Teams
To query as one of your teams, send theX-Team-Id header. The response then contains only the data that the roles of the team give access to. This header may not be functional on all routes, reach out to the SELECT team if needed.
