AFAICT the cause for the weird results is (1) the fact that your counter actually only increases once every minute, even though you collect it every 15 seconds combined with (2) Prometheus' rate() implementation discarding every 4th counter increase (in your particular setup).

More precisely, you appear to be computing a 1 minute rate, every 1 minute over a counter scraped at 15 second resolution, increasing every 1 minute (on average).

What this means essentially is that Prometheus will basically slice your 1 hour interval into disjoint 1 minute ranges and estimate the rate over each range. The first value will be the extrapolated rate of increase between points 0 and 3, the second will be the extrapolated rate between points 4 and 7 and so on. Because your counter only actually increases once a minute, you can run into 2 different situations:

  1. Your counter increases happen between point pairs 3-4, 7-8 etc. In this case Prometheus sees an increase rate of zero (because there is no increase between points 0 and 3, points 4 and 7 etc. This seems to be happening in the first half of your first graph.
  2. Your counter increases happen somewhere between points 0-3, 4-7 etc. In this case Prometheus takes the difference between the last and first points in each interval (your actual counter increase), divides it by the time difference between the 2 points (on average 45 seconds), then extrapolates that to 1 minute (essentially overestimating it by a factor of 1.(3) -- I'm eyeballing an increase of ~200k over ~50 minutes, so an average rate of about 67 QPS, whereas rate() returns something closer to 90 QPS). This is what happens in the second half of your graph.

This is also why your graph looks wildly different across refreshes. The argument for the current implementation of rate() is that it is "correct on average". Which, if you look at the whole of your graph, across refreshes, is true. </sarcasm>

Essentially graphing a Prometheus rate() or increase() over a time range R with resolution R will result in aliasing, either overestimating (1.33x in your case) or underestimating (zero in your case) on anything but a smoothly increasing counter.

You can work around it by replacing your expression with:

rate(foo[75s]) / 75  * 60

This way you'll actually get the rate of increase between data points 1 minute apart (a 75 seconds range will almost always return exactly 5 points, so 4 counter increases) and reverse the extrapolation to 75 seconds that Prometheus does. There will be some noise in edge cases (e.g. if your evaluation is aligned with scraping times it's possible to get 6 points in one range and 4 in the next due to scrape interval jitter) but you're getting that anyway with rate().

BTW, you can see the aliasing by increasing the resolution of your graph to something like 1 second (anything 15 seconds or below should show it clearly).

Answer from Alin Sînpălean on Stack Overflow
🌐
Last9
last9.io › blog › prometheus-rate-function
Prometheus Rate Function: A Practical Guide to Using It | Last9
June 15, 2026 - At its core, rate() calculates ... system performance and behavior. The scrape interval in Prometheus defines how frequently metrics are collected....
🌐
Grafana
grafana.com › blog › 2020 › 09 › 28 › new-in-grafana-7.2-__rate_interval-for-prometheus-rate-queries-that-just-work
New in Grafana 7.2: \$\_\_rate_interval for Prometheus rate queries that just work | Grafana Labs
September 29, 2020 - Grafana helpfully tells us about the value in the panel editor, as marked in the screenshot above. As you can see, the interval is only 15s. Our Prometheus server is configured with a scrape interval of 15s, so we should use a range of at least 1m ...
Discussions

Prometheus rate functions and interval selections - Stack Overflow
I am doing some monitoring with prometheus and is trying to understand how to properly use the rate functions. Premise is this; I have a counter, configuration for this is set to ingest new values... More on stackoverflow.com
🌐 stackoverflow.com
what is the default grafana setting for $__rate_interval - Stack Overflow
To get rate per minute, just multiply the rate with 60. ... Prometheus periodically fetches data from your application. Grafana periodically fetches Data from Prometheus. Grafana does not know, how often Prometheus polls your application for data. Grafana will estimate this time by looking at the configuration and assuming that every scrape gives us one data point. The $__interval ... More on stackoverflow.com
🌐 stackoverflow.com
How to set the rate interval in the PROMQL similar to $__rate_interval by grafana.
I am trying to fetch the different metrics using PROMQL and make my custom dashboard similar to grafana. But I am not sure how to dynamically set rate interval and step in query range using the given time range. I am using HTTP API provided by the Prometheus. More on github.com
🌐 github.com
3
September 21, 2023
Prometheus Error Rate alert : interval range question - Stack Overflow
How valuable is to have the interval set for 5m? Shall this range over a longer period of time, e.g. 1h. This alert goes off and it does not really inform us of a problem. What is your view? ... Save this answer. ... Show activity on this post. Buried in the mass Prometheus docs, there is a paragraph for increase function: increase should only be used with counters and native histograms where the components behave like counters. It is syntactic sugar for rate... More on stackoverflow.com
🌐 stackoverflow.com
People also ask

Can rate() be used with all types of Prometheus metrics?
No, rate() should only be used with counter-metrics. It doesn't make sense to use rate() with gauge metrics, as they don't represent cumulative values.
🌐
last9.io
last9.io › blog › prometheus-rate-function
Prometheus Rate Function: A Practical Guide to Using It | Last9
How do you calculate request rates using the Prometheus rate function?
To calculate request rates, use a query like rate(http_requests_total[5m]). This will give the per-second rate of requests over the last 5 minutes. These rates can be summed or grouped as needed, e.g., sum(rate(http_requests_total[5m])) for the total request rate across all instances.
🌐
last9.io
last9.io › blog › prometheus-rate-function
Prometheus Rate Function: A Practical Guide to Using It | Last9
How does the Prometheus rate() function differ from increase()?
While rate() calculates the per-second average rate of increase, increase() calculates the total increase in the counter's value over the time range. rate() is generally more useful for ongoing monitoring, while increase() can help understand total change over a specific period.
🌐
last9.io
last9.io › blog › prometheus-rate-function
Prometheus Rate Function: A Practical Guide to Using It | Last9
🌐
MetricFire
metricfire.com › blog › understanding-the-prometheus-rate-function
How the Prometheus rate() function works | MetricFire
March 12, 2026 - It should be at least two times the scrape interval, but the optimal range depends on the specific use case and whether detailed data or broader trends are needed. You can apply rate() to specific dimensions, making monitoring error rates for ...
Top answer
1 of 3
34

AFAICT the cause for the weird results is (1) the fact that your counter actually only increases once every minute, even though you collect it every 15 seconds combined with (2) Prometheus' rate() implementation discarding every 4th counter increase (in your particular setup).

More precisely, you appear to be computing a 1 minute rate, every 1 minute over a counter scraped at 15 second resolution, increasing every 1 minute (on average).

What this means essentially is that Prometheus will basically slice your 1 hour interval into disjoint 1 minute ranges and estimate the rate over each range. The first value will be the extrapolated rate of increase between points 0 and 3, the second will be the extrapolated rate between points 4 and 7 and so on. Because your counter only actually increases once a minute, you can run into 2 different situations:

  1. Your counter increases happen between point pairs 3-4, 7-8 etc. In this case Prometheus sees an increase rate of zero (because there is no increase between points 0 and 3, points 4 and 7 etc. This seems to be happening in the first half of your first graph.
  2. Your counter increases happen somewhere between points 0-3, 4-7 etc. In this case Prometheus takes the difference between the last and first points in each interval (your actual counter increase), divides it by the time difference between the 2 points (on average 45 seconds), then extrapolates that to 1 minute (essentially overestimating it by a factor of 1.(3) -- I'm eyeballing an increase of ~200k over ~50 minutes, so an average rate of about 67 QPS, whereas rate() returns something closer to 90 QPS). This is what happens in the second half of your graph.

This is also why your graph looks wildly different across refreshes. The argument for the current implementation of rate() is that it is "correct on average". Which, if you look at the whole of your graph, across refreshes, is true. </sarcasm>

Essentially graphing a Prometheus rate() or increase() over a time range R with resolution R will result in aliasing, either overestimating (1.33x in your case) or underestimating (zero in your case) on anything but a smoothly increasing counter.

You can work around it by replacing your expression with:

rate(foo[75s]) / 75  * 60

This way you'll actually get the rate of increase between data points 1 minute apart (a 75 seconds range will almost always return exactly 5 points, so 4 counter increases) and reverse the extrapolation to 75 seconds that Prometheus does. There will be some noise in edge cases (e.g. if your evaluation is aligned with scraping times it's possible to get 6 points in one range and 4 in the next due to scrape interval jitter) but you're getting that anyway with rate().

BTW, you can see the aliasing by increasing the resolution of your graph to something like 1 second (anything 15 seconds or below should show it clearly).

2 of 3
2

What you say doesn't line up with the data, that raw data is only going up about once a minute. Are you sure you're scraping every 15s?

🌐
Medium
medium.com › @bhupender.rawat4 › demystifying-prometheus-a-deep-dive-into-rate-and-irate-ce02745231fc
Demystifying Prometheus: A Deep Dive into rate() and irate() | by Bhupender Singh Rawat | Medium
May 7, 2025 - When you write something like ... · It takes all the samples collected during that 5-minute window (usually 20 if your scrape interval is 15s) and fits a line through them to give you a stable, averaged rat...
Top answer
1 of 2
22
  1. See New in Grafana 7.2: $__rate_interval for Prometheus rate queries that just work.
  2. Rate is always per second. See Prometheus documentation for the rate function.
  3. Click on Query options, then click on the Info-Symbol. An explanation will be displayed.
  4. To get rate per minute, just multiply the rate with 60.

Edit: ($__rate_interval and $__interval)

Prometheus periodically fetches data from your application. Grafana periodically fetches Data from Prometheus. Grafana does not know, how often Prometheus polls your application for data. Grafana will estimate this time by looking at the configuration and assuming that every scrape gives us one data point. The $__interval variable then expands to the duration between two data points in the graph (Note that this is only true for small time ranges and high resolution as the intended use case for $__interval is reducing the number of data points when the time range is wide. See Approximate Calculation of $__interval.)

If the time-distance between every two data points in each series is 15 seconds, it does not make sense to use anything less than [15s] as interval in the rate function. The rate function works best with at least 4 data points. Therefore [1m] would be much better than anything betweeen [15s] and [1m]. This is what $__rate_interval tries to achieve: guessing a minimal sensible interval for the rate function.

Personally, I think, this does not always work if your application delivers sparse data (less than one data point per scrape). I prefer using fixed intervals like 10m or even 1h or 1d in these situations. The interval need to be great enough to get you enough data points for the metric to work with the rate function.

A different approach would be to use any of $__rate_interval and $__interval but also set the Min step parameter for the query in the Grafana UI to be big enough.

2 of 2
0

Just click button "Query inspector" and you will see detailed explanation for the query (Expr: section). In my case default value for $__rate_interval in Grafana is 1m0s.

Find elsewhere
🌐
Grafana
grafana.com › docs › grafana › latest › datasources › prometheus › template-variables
Prometheus template variables | Grafana documentation
Missing or incorrect scrape interval setting: If the data source scrape interval is left at the default 15s but your actual Prometheus scrape interval is 60s, $__rate_interval calculates too small a window.
🌐
Robust Perception
robustperception.io › what-range-should-i-use-with-rate
What range should I use with rate()? – Robust Perception | Prometheus Monitoring Experts
August 12, 2019 - So to summarise, use a range that's at least 4x your scrape interval, choose one consistent range across your organisation for recording rules, and use avg_over_time if you need an average over a longer period for graphs and alerts. Not sure how to keep your recording rules maintainable?
🌐
DevOps.dev
blog.devops.dev › prometheus-theory-rate-vs-irate-20e6243a3ab8
[Prometheus Theory] rate() vs. irate() | by - DevOps.dev
October 26, 2023 - ... The rate() function would average using the first and last data points, averaged over the query interval (1m); whereas the irate() function would average using the last two data points, averaged over the scrape interval (15s).
🌐
Medium
medium.com › @pradeepsunku › mastering-prometheus-queries-in-grafana-99b3849b1e03
Mastering Prometheus Queries in Grafana | by PradeepSunku | Medium
August 10, 2024 - $__interval, $__range, and $__rate_interval are templated variables used to dynamically adjust the time range or interval for Prometheus queries based on the dashboard's settings.
🌐
Google Groups
groups.google.com › g › prometheus-users › c › txyPClxPyY4
what is the default grafana setting for $__rate_interval
May 25, 2022 - "interval" comes from the data source definition. $__rate_interval is the higher of 4 x the data source's defined rate interval, or the spacing between adjacent points in the graph.
🌐
DoiT
doit.com › home › blog › making peace with prometheus rate()
Making peace with Prometheus rate() | DoiT
February 17, 2023 - Of course, Prometheus will extrapolate it to 75 seconds but we de-extrapolate it manually back to 60 and now our charts are both precise and provide us with the data one whole-minute boundaries as well.
🌐
GitHub
github.com › samber › prometheus-query-js › issues › 35
How to set the rate interval in the PROMQL similar to $__rate_interval by grafana. · Issue #35 · samber/prometheus-query-js
September 21, 2023 - I am trying to fetch the different metrics using PROMQL and make my custom dashboard similar to grafana. But I am not sure how to dynamically set rate interval and step in query range using the given time range. I am using HTTP API provided by the Prometheus.
Author: samber
🌐
Prometheus
prometheus.io › docs › prometheus › latest › querying › functions
Query functions | Prometheus
(This implies that a change in the data labels in the conventional Prometheus view constitutes the end of one info series and the beginning of a new info series, while the “logical” view of the info function is that the same info series continues to exist, just with different “data”.) The conventional approach of adding data labels is sometimes called a “join query”, as illustrated by the following example: rate(http_server_request_duration_seconds_count[2m]) * on (job, instance) group_left (k8s_cluster_name) target_info
🌐
Hoelz
hoelz.ro › blog › use-caution-when-using-rate_interval-along-with-increase
Use caution when using $rate_interval along with increase()
One way you can get around this behavior in the case of increase() is to use rate() instead and multiply by the interval duration - so rate(detected_changes_count[$__rate_interval]) * $__interval_ms / 1000 instead of increase(detected_chang...
🌐
Medium
medium.com › @MetricFire › how-the-prometheus-rate-function-works-cc63fe90ef19
How the Prometheus rate() function works | by MetricFire | Medium
July 31, 2023 - Also, your time interval becomes tied to your query step, so if your scrape interval ever changes then you might have problems with very small time ranges. Something to remember — MetricFire is also a hosted Grafana service. Explore our free trial here — or sign up for a demo.‍ · Just like everything else, the function gets evaluated at each step. But, how does it work? ... ‍The nice thing about the rate() function is that it takes into account all of the data points, not just the first one and the last one.
Top answer
1 of 2
1

Buried in the mass Prometheus docs, there is a paragraph for increase function:

increase should only be used with counters and native histograms where the components behave like counters. It is syntactic sugar for rate(v) multiplied by the number of seconds under the specified time range window, and should be used primarily for human readability.

So answer your questions:

  1. Is there a strong reason as why I should use rate as opposed to increase?

    Yes, use the rate function.

  2. How valuable is to have the interval set for 5m?

    Not so valuable. Since your RPS/QPS is very small - less than 10 per 5m, you may get some 5m time ranges with little or zero requests and others with much more requests. The alert rule will be too sensitive or just wrong in a wider time range view. 30m or 1h range might be better.

By the way, time series on each side of division operator should have matching labels to make the alert rule work.

2 of 2
1

It looks like you have e.g. slow-changing integer counter, which may increase by less than 100 during an hour. Prometheus can return unexpected results from increase() function when applied to slow-changing integer counters because of the following issues:

  • increase(m[d]) may return fractional results over integer counter m because of extrapolation. See this issue.
  • increase(m[d]) may miss counter increase between the last raw sample just before the lookbehind window d and the first raw sample inside the lookbehind window d. See this article for more details.
  • increase(m[d]) may miss the initial counter increase if m time series starts from value other than zero.

The same issues are applied to rate() as well, since increase() is a syntactic sugar over rate() in Prometheus, e.g. increase(m[d]) = rate(m[d]) * d.

It is recommended using longer lookbehind windows for rate() and increase() functions when they are applied to slow-changing counters, in order to minimize the significance of issues mentioned above. For example, to use 1h lookbehind window in square brackets instead of 5m, so the increased window catches non-zero counter increases.

As for the original query, it is better rewriting it to the following one:

(
  sum(increase(errorMetric{service_name="someservice"}[1h]))
    /
  sum(increase(http_requests_count{service_name="someservice"}[1h]))
) > 0.05

This query contains the following changes comparing to the original query:

  • 5m lookbehind window has been changed to 1h
  • the path label filter has been removed from the http_requests_count metric selector, so the number of errorMetric time series matches the number of http_requests_count time series. On the other hand, the path label filter could be added to errorMetric metric selector instead.
🌐
Chris's Wiki
utcc.utoronto.ca › ~cks › space › blog › sysadmin › PrometheusRateVsIrate
rate() versus irate() in Prometheus (and Grafana)
November 5, 2018 - Suppose that you have a continuously updating metric that Prometheus scrapes every fifteen seconds. To do a rate() or irate() of this, you need at least two metric points and thus a range interval of thirty seconds (at least; in practice you need a somewhat larger interval).
🌐
Promlabs
promlabs.com › blog › 2021 › 01 › 29 › how-exactly-does-promql-calculate-rates
PromLabs | Blog - How Exactly Does PromQL Calculate Rates?
January 29, 2021 - The rate() and increase() functions guess that a series starts or ends under the window when the first or last sample is farther away from its respective window boundary than 1.1x the average interval between the samples under the window.
🌐
Tech Annotation
techannotation.wordpress.com › 2021 › 07 › 19 › irate-vs-rate-whatre-they-telling-you
irate() vs rate() – What're they telling you? - Tech Annotation
July 22, 2021 - As you’ve seen, the range interval play an important role in this calc. A best practice suggests us to set this value in the range of 10-60s. Take note, another good practice to avoid data misinterpretation is to set, inside Grafana, Prometheus ...