Test result metrics
Performance testing stores test result metrics in Grafana Cloud Metrics, which is powered by Mimir. Test results work like any other metrics in your Grafana Cloud stack: you can query them with PromQL through your stack’s Prometheus data source, build custom dashboards with them, and correlate them with the rest of your observability data.
This page describes how test result metrics are stored, including their names, labels, and metric types, as well as how to query each type of metric.
Before you begin
- Test result metrics are available to any user with query access to your stack’s Prometheus data source, regardless of their project permissions in the Performance testing app. To restrict raw metric access along project boundaries, refer to Configure LBAC.
- Metrics are recorded at the time the test runs. When you query them, make sure the time range of your query covers the test run.
Query test result metrics
To query test result metrics in Grafana:
Log in to your Grafana Cloud account.
Click Explore on the main menu.
Select the
grafanacloud-<yourstackname>-promdata source.Query a test result metric. For example:
rate(k6_http_reqs_total{k6_cloud_test_name="<TEST_NAME>"}[$__rate_interval])Replace
<TEST_NAME>with the name of one of your tests.
You can use the same queries in dashboard panels and in Grafana Alerting.
Note
The
grafanacloud-<yourstackname>-promdata source queries the raw stored series. Thek6data source used by the Performance testing app provides pre-aggregated, test-oriented query types instead. Refer to Query types for details.
Range and instant queries
Each example on this page is marked as either a range query or an instant query. A range query evaluates the expression at every step of the selected time range. An instant query evaluates it once at the end of the time range.
To choose between them in Explore or in a dashboard panel, set Type in the query options. The default, Both, runs the query both ways and returns combined results. Refer to Prometheus query editor for details, including how to display instant results in a time series panel.
For test result metrics, instant queries are especially useful: counter, rate, and trend series are scoped to a single test run and accumulate from the start of the run, so the last sample of each series holds the whole-run total or distribution. The instant examples on this page wrap the metric in last_over_time(...[$__range]), which returns that last sample as long as the selected time range covers the test run. The evaluation time doesn’t need to align with the end of the run.
Query from the command line
To query test result metrics from scripts, CI pipelines, or your terminal, use the gcx CLI. Authenticate against your stack once:
gcx login mystack --server https://<yourstackname>.grafana.netFor unattended use, such as CI, authenticate with a service account token instead:
gcx login --yes ci --server https://<yourstackname>.grafana.net --token <SERVICE_ACCOUNT_TOKEN>Any query on this page works with gcx metrics query, which targets your stack’s default Prometheus data source. Run range queries with --from, --to, and --step:
gcx metrics query 'sum(rate(k6_http_reqs_total{test_run_id="<TEST_RUN_ID>"}[2m]))' \
--from <RUN_START> --to <RUN_END> --step 30sRun instant queries with --time and a window that reaches back past the run:
gcx metrics query 'sum(last_over_time(k6_http_reqs_total{test_run_id="<TEST_RUN_ID>"}[30d]))' \
--time nowQuery results are unordered. To list the runs of a test newest first, like the run list in the Performance testing app, combine a per-run query from Across the runs of a test with the --jq flag and an expression that sorts the results by run ID:
gcx metrics query 'histogram_quantile(0.95, sum by (test_run_id) (last_over_time(k6_http_req_duration{k6_cloud_test_id="<TEST_ID>"}[30d])))' \
--time now \
--jq '[.data.result[] | {run: .metric.test_run_id, p95_ms: .value[1]}] | sort_by(.run | tonumber) | reverse'Replace:
<TEST_RUN_ID>with the ID of a test run<TEST_ID>with the ID of a test<RUN_START>and<RUN_END>with the start and end times of the test run, as RFC3339 timestamps such as2026-08-12T11:28:00Z<SERVICE_ACCOUNT_TOKEN>with a token you created in your stack.
Note
The
$__rate_intervaland$__rangevariables used in the examples on this page are provided by Grafana dashboards and Explore. When you query from the command line, use fixed windows instead, such as[2m]for$__rate_intervaland a window that covers the test run, such as[30d], for$__range.
Sample rate
k6 samples metrics at a fixed interval. This interval sets the resolution of range queries and the minimum window rate() and similar functions need to return accurate results; refer to Min step and Use $__rate_interval for more on querying at a given sample interval.
The default sample rate is 12 seconds, or 5 data points per minute.
The sample rate is configurable per organization. To change your organization’s sample rate, contact support.
Metric names
Test result metrics use the k6 metric names, prefixed with k6_:
- The built-in
http_reqsmetric is stored ask6_http_reqs_total. - The built-in
http_req_durationmetric is stored ask6_http_req_duration. - A custom metric named
my_counteris stored ask6_my_counter_total.
Counter and rate metrics additionally carry a _total suffix, following the Prometheus naming convention for counters. Gauge and trend metrics don’t have a suffix.
Every series carries the original metric name, without the prefix or suffix, in the k6_metric_name label. Use it to find a metric when you know its k6 name:
{k6_metric_name="http_reqs"}Labels
Test result metrics include labels that identify the metric and the project, test, and test run that produced it:
In addition, all tags from your test script—built-in tags such as url, status, and scenario, and any custom tags—are included as labels. Tests that use multiple load generator instances or load zones produce one series per instance or zone for each metric; aggregate over instance_id and load_zone unless you’re inspecting a specific one.
Prefer the ID labels over the name labels in queries you want to keep, because projects and tests can be renamed, but their IDs never change. Refer to Project ID for details on how to find the ID of a project.
Metric types
Each k6 metric type maps to a Prometheus metric type:
Counter metrics
Counter metrics are standard Prometheus counters that accumulate over the course of a test run. To graph requests per second during a test run, use rate() in a range query:
sum(rate(k6_http_reqs_total{test_run_id="<TEST_RUN_ID>"}[$__rate_interval]))To get the total number of requests for a whole test run, read the counter’s final value with an instant query over a time range that includes the run:
sum(last_over_time(k6_http_reqs_total{test_run_id="<TEST_RUN_ID>"}[$__range]))In this and the following examples, replace <TEST_RUN_ID> with the ID of a test run, and <TEST_ID> with the ID of a test.
Gauge metrics
Gauge metrics are standard Prometheus gauges. To graph the number of active virtual users during a test run across all load generator instances, use a range query:
sum(k6_vus{test_run_id="<TEST_RUN_ID>"})Rate metrics
A k6 rate metric tracks the proportion of nonzero observations, such as the proportion of passed checks. It’s stored as two counter series distinguished by the condition label: condition="nonzero" counts the nonzero observations, and condition="zero" counts the rest.
For example, a checks metric with six passed and two failed observations is stored as:
k6_checks_total{check="my_check", condition="nonzero"} 6
k6_checks_total{check="my_check", condition="zero"} 2To get the total number of observations for a whole test run, sum over both conditions by querying without the condition label, using an instant query:
sum by (check) (last_over_time(k6_checks_total{test_run_id="<TEST_RUN_ID>"}[$__range]))To get the rate value as k6 reports it—a ratio from 0.0 to 1.0—divide the nonzero count by the total. As an instant query, this returns the whole-run ratio:
sum by (check) (last_over_time(k6_checks_total{test_run_id="<TEST_RUN_ID>", condition="nonzero"}[$__range]))
/
sum by (check) (last_over_time(k6_checks_total{test_run_id="<TEST_RUN_ID>"}[$__range]))To graph how the ratio evolves during the run instead, apply rate() to both sides in a range query:
sum by (check) (rate(k6_checks_total{test_run_id="<TEST_RUN_ID>", condition="nonzero"}[$__rate_interval]))
/
sum by (check) (rate(k6_checks_total{test_run_id="<TEST_RUN_ID>"}[$__rate_interval]))Trend metrics
Trend metrics, such as http_req_duration, are stored as native histograms: a single series that holds the full value distribution, rather than the separate _bucket, _sum, and _count series of classic histograms.
Use the histogram functions to query them. To graph the 95th percentile of the HTTP request duration as it evolves during a test run, use a range query:
histogram_quantile(0.95, sum(rate(k6_http_req_duration{test_run_id="<TEST_RUN_ID>"}[$__rate_interval])))Because the stored histogram accumulates over the run, its last sample holds the distribution of the whole run. To get the 95th percentile for a whole test run, as an instant query:
histogram_quantile(0.95, sum(last_over_time(k6_http_req_duration{test_run_id="<TEST_RUN_ID>"}[$__range])))To get the whole-run average, divide the sum of observed values by the observation count, also as an instant query:
histogram_sum(sum(last_over_time(k6_http_req_duration{test_run_id="<TEST_RUN_ID>"}[$__range])))
/
histogram_count(sum(last_over_time(k6_http_req_duration{test_run_id="<TEST_RUN_ID>"}[$__range])))Example queries
One test run
The 95th percentile of request duration, split by URL, over the course of one test run, as a range query:
histogram_quantile(0.95, sum by (url) (rate(k6_http_req_duration{test_run_id="<TEST_RUN_ID>"}[$__rate_interval])))The failed request rate over time during one test run, as a range query:
sum(rate(k6_http_req_failed_total{test_run_id="<TEST_RUN_ID>", condition="nonzero"}[$__rate_interval]))
/
sum(rate(k6_http_req_failed_total{test_run_id="<TEST_RUN_ID>"}[$__rate_interval]))All metrics produced by a specific test run, as an instant query:
group by (__name__, k6_metric_name, k6_metric_type) (last_over_time({test_run_id="<TEST_RUN_ID>"}[$__range]))Across the runs of a test
To compare runs of the same test, filter by k6_cloud_test_id and aggregate by (test_run_id), over a time range that covers all the runs you want to compare. Instant queries then return one whole-run summary value per run, the same shape as the performance-trending chart in Compare tests, and work well in table and bar chart panels.
The 95th percentile of request duration for each run, as an instant query:
histogram_quantile(0.95, sum by (test_run_id) (last_over_time(k6_http_req_duration{k6_cloud_test_id="<TEST_ID>"}[$__range])))The total number of requests in each run, as an instant query:
sum by (test_run_id) (last_over_time(k6_http_reqs_total{k6_cloud_test_id="<TEST_ID>"}[$__range]))The failed request ratio of each run, as an instant query:
sum by (test_run_id) (last_over_time(k6_http_req_failed_total{k6_cloud_test_id="<TEST_ID>", condition="nonzero"}[$__range]))
/
sum by (test_run_id) (last_over_time(k6_http_req_failed_total{k6_cloud_test_id="<TEST_ID>"}[$__range]))The peak requests per second reached in each run, as an instant query. The inner expression is evaluated as a PromQL subquery at every step of the time range, so keep the time range close to the runs you’re comparing:
max_over_time(sum by (test_run_id) (rate(k6_http_reqs_total{k6_cloud_test_id="<TEST_ID>"}[2m]))[$__range:])The 95th percentile of request duration over time, split by run, as a range query—over a time range that spans multiple runs, this graphs each run’s behavior side by side:
histogram_quantile(0.95, sum by (test_run_id) (rate(k6_http_req_duration{k6_cloud_test_id="<TEST_ID>"}[$__rate_interval])))

