Get Started with PromQL: Selecting, Rating, and Aggregating
Build up from a simple metric selector to a real query: per-path error rate, computed live.
PromQL is the query language every part of Prometheus that reads data, dashboards, alerting rules, ad hoc debugging, is built on. This guide builds up a real query step by step: given a request counter split by path, method, and status, find every path whose error rate is above 5%.
Selecting time series
The simplest PromQL expression is just a metric name, which returns the current value of every time series with that name:
http_requests_total
Add label matchers in curly braces to narrow that down to a specific subset:
http_requests_total{status="500"}
This returns every series named http_requests_total whose status label is exactly "500", regardless of path or method. Matchers can also do regex matching (status=~"5.." for any 5xx status) and negation (status!="200"), among other operators.
From a counter to a rate
http_requests_total is a counter: it only goes up, so its raw value (some large, ever-growing number since the process started) isn’t what you actually want to look at. What you want is how fast it’s increasing, and that’s what the rate() function computes: the per-second average rate of increase over a given time window.
rate(http_requests_total{status="500"}[5m])
This returns, for every matching series, the average per-second rate of 500 errors over the trailing 5 minutes. A related function, irate(), computes an instantaneous rate using only the last two data points in the range, rather than averaging across the whole window. irate() reacts faster to sudden spikes, but is also noisier and more sensitive to a single unusual scrape; rate() is the recommended default for dashboards and especially for alerting, where a smoother, less spike-prone signal is usually what you actually want. Reach for irate() specifically when you’re looking at a fast-moving counter on a short timescale and want to see instantaneous changes rather than a smoothed trend.
Aggregating away labels you don’t care about
rate() still returns one result per original time series, one line per unique combination of path, method, and status. To get a single number per path, summed across whatever methods and statuses exist, use an aggregation operator:
sum by (path) (rate(http_requests_total{status="500"}[5m]))
sum by (path) (...) sums the rates together, grouping by the path label and discarding every other label in the result. This is the general shape of PromQL aggregation: pick an operator (sum, avg, max, min, count, and others), and a by (...) clause naming which labels survive into the result.
Combining queries with binary operators
To calculate an error rate as a percentage, you need two aggregated queries, error requests and total requests, divided against each other:
sum by (path) (rate(http_requests_total{status="500"}[5m]))
/
sum by (path) (rate(http_requests_total[5m]))
* 100
PromQL matches the two sides of a binary operation by their remaining labels, path in this case, computing the division for each matching pair rather than requiring you to align them manually. Multiplying by 100 turns the resulting ratio into a percentage.
Filtering the result itself
Wrap the whole expression to keep only the results that actually cross a threshold:
(
sum by (path) (rate(http_requests_total{status="500"}[5m]))
/
sum by (path) (rate(http_requests_total[5m]))
* 100
) > 5
This returns only the paths whose computed error percentage is currently above 5, which is exactly the shape of query you’d wrap in an alerting rule to page someone when it’s true.
Turning a query into an alert
An alerting rule is a PromQL expression plus metadata describing what to do when it evaluates to something:
groups:
- name: example-alerts
rules:
- alert: HighErrorRatePerPath
expr: |
(
sum by (path) (rate(http_requests_total{status="500"}[5m]))
/
sum by (path) (rate(http_requests_total[5m]))
* 100
) > 5
for: 10m
labels:
severity: page
annotations:
summary: "Error rate above 5% on {{ $labels.path }}"
for: 10m requires the condition to stay true for 10 minutes before the alert actually fires, which avoids paging on a brief, self-resolving blip. Prometheus evaluates rules like this continuously and hands anything currently firing to Alertmanager, which is the same PromQL you’d otherwise put directly into a Grafana panel, just wrapped with a name, a duration, and routing metadata.
Where to go from here
This covers instant vectors, rate calculations, aggregation, and binary operators, enough to read and write the large majority of queries you’ll encounter in existing dashboards and alerts. PromQL has more beyond this (range vector functions beyond rate, subqueries, more aggregation operators), well documented in Prometheus’s own query reference, which is worth keeping open while you write your first few real queries.
Popular Guides
-
Prometheus Architecture and the Pull Model Why Prometheus scrapes instead of receiving, and how targets, exporters, and service discovery fit together. -
The Prometheus Data Model: Metrics, Labels, and Metric Types What a time series actually is in Prometheus, and the plain-text format targets expose them in. -
Monitor a Host and Its Containers with node_exporter and cAdvisor Run Prometheus, node_exporter, and cAdvisor together, scrape both, and confirm the targets are healthy.