The expression browser and HTTP API

Prometheus · Essentials · 10 min

The UI is where you develop every query, and the API is how Grafana, scripts and tests run them.

The Prometheus server ships a web UI on port 9090 (rewritten in React for 3.0). The Query page is the expression browser: type PromQL, press Enter, and switch between Table (an instant query at one moment) and Graph (a range query over a window). The metrics explorer button lists every metric name with its help text, and the Explain tab breaks a query into its steps. The Status menu shows targets, service discovery, rules, configuration, flags and TSDB statistics.

Everything the UI does goes through the HTTP API under /api/v1/. Two endpoints matter most:

  • GET /api/v1/query?query=<expr>&time=<rfc3339 or unix>: an instant query, result type vector, scalar or string. Omit time for now.
  • GET /api/v1/query_range?query=<expr>&start=..&end=..&step=15s: evaluates the expression at every step and returns a matrix with values: [[ts, "value"], ...] per series. Values are strings to preserve precision.

Both accept POST with form parameters, which avoids URL-length limits for long queries. Responses are JSON with status: "success" or "error" plus errorType and error.

Other useful endpoints: /api/v1/series?match[]=up (which series exist), /api/v1/labels and /api/v1/label/job/values (for building dropdowns), /api/v1/targets, /api/v1/rules, /api/v1/alerts and /api/v1/status/tsdb. Liveness and readiness are /-/healthy and /-/ready.

The API is read-only unless you enable the admin API, and it has no authentication of its own.

Example

# Instant query (now)
curl -s 'http://localhost:9090/api/v1/query' --data-urlencode 'query=sum(up) by (job)'
# {"status":"success","data":{"resultType":"vector",
#   "result":[{"metric":{"job":"api"},"value":[1760700000.123,"2"]}]}}

# Range query: last hour, one point per minute
curl -s 'http://localhost:9090/api/v1/query_range' \
  --data-urlencode 'query=rate(http_requests_total{job="api"}[5m])' \
  --data-urlencode "start=$(date -u -d '-1 hour' +%s)" \
  --data-urlencode "end=$(date -u +%s)" --data-urlencode 'step=60s' | jq '.data.result[0].values[:2]'
# [[1760696400,"12.4"],[1760696460,"12.9"]]

# Which label values exist?
curl -s 'http://localhost:9090/api/v1/label/job/values'
# {"status":"success","data":["api","node","prometheus"]}

Remember

  • Table = instant query, Graph = range query in the UI
  • /api/v1/query returns a vector; /query_range returns a matrix with step
  • POST form data for long queries; values come back as strings

Watch out for

  • Using a step much smaller than the scrape interval in query_range, which only repeats the same samples.

Check yourself

  1. A dashboard needs one value per minute over the last 6 hours. Which call fits?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: prometheus.io/docs/prometheus/latest/querying/api/