﻿---
title: ES|QL PROMQL command
description: 
url: https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/esql/commands/promql
products:
  - Elasticsearch
---

# ES|QL PROMQL command
<applies-to>
  - Elastic Cloud Serverless: Generally available
  - Elastic Stack: Generally available since 9.5
  - Elastic Stack: Preview in 9.4
</applies-to>

The `PROMQL` source command queries [time series indices](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4641/manage-data/data-store/data-streams/time-series-data-stream-tsds) using [**Prometheus Query Language (PromQL)**](https://prometheus.io/docs/prometheus/latest/querying/basics/).
Like [`TS`](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/esql/commands/ts), it enables time series aggregation functions, but accepts PromQL syntax instead of ES|QL.
<note>
  `PROMQL` supports most, but not all, of PromQL. Refer to [Prometheus compatibility](#esql-prometheus-compatibility) for unsupported constructs and differences from Prometheus, and to [PromQL functions](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/functions) for the supported functions and their restrictions.
</note>


## Syntax

The `PROMQL` command accepts zero or more space-separated `<option>=<value>` pairs, followed by a PromQL expression in parentheses that can be prefixed with a result name.
```esql
PROMQL [ <option>=<value> ... ] [ <result_name>= ](<PromQL Expression>)
```


## Options

The options are inspired by the Prometheus [HTTP API](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) with some additions specific to ES|QL.
<definitions>
  <definition term="index">
    A list of indices, data streams, or aliases. Supports wildcards and date math.
    Defaults to `metrics-*` querying matching indices with [`index.mode: time_series`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4641/manage-data/data-store/data-streams/time-series-data-stream-tsds).
    Example: `PROMQL index=metrics-*.otel-* http_rate=(sum(rate(http_requests_total)))`
  </definition>
  <definition term="step">
    Query resolution step width (optional).
    Automatically determined given the number of target `buckets` and the selected time range.
    Example: `PROMQL step=1m http_rate=(sum(rate(http_requests_total)))`
  </definition>
  <definition term="buckets">
    Target number of buckets for auto-step derivation.
    Defaults to `100`. Mutually exclusive with `step`. Requires a known time range, either by setting
    `start` and `end` explicitly or implicitly through Kibana's time range filter.
    Example: `PROMQL buckets=50 start="2026-04-01T00:00:00Z" end="2026-04-01T01:00:00Z" http_rate=(sum(rate(http_requests_total)))`
  </definition>
  <definition term="start">
    Start time of the query, inclusive (optional).
    Uses the start based on Kibana's date picker if missing. Set together with `end`.
    Refer to [Time range](#esql-promql-time-range) for queries without a time range.
    Example: `PROMQL start="2026-04-01T00:00:00Z" end="2026-04-01T01:00:00Z" http_rate=(sum(rate(http_requests_total)))`
  </definition>
  <definition term="end">
    End time of the query, inclusive (optional).
    Uses the end based on Kibana's date picker if missing. Set together with `start`, and not before it.
    Refer to [Time range](#esql-promql-time-range) for queries without a time range.
    Example: `PROMQL start="2026-04-01T00:00:00Z" end="2026-04-01T02:00:00Z" http_rate=(sum(rate(http_requests_total)))`
  </definition>
  <definition term="time">
    <applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> Evaluation time of an instant query (optional).
    Evaluates the expression once, at this time, instead of at each step of a range query.
    Mutually exclusive with `start`, `end`, `step`, and `buckets`.
    Example: `PROMQL time="2026-04-01T01:00:00Z" http_rate=(sum(rate(http_requests_total)))`
  </definition>
  <definition term="scrape_interval">
    The expected metric collection interval.
    Defaults to `1m`. Used to determine implicit range selector windows as `max(step, scrape_interval)`.
    Example: `PROMQL scrape_interval=15s http_rate=(sum(rate(http_requests_total)))`
  </definition>
  <definition term="<result_name>=(<PromQL Expression>)">
    Name of the output column with the query result timeseries (optional).
    By default, the name of the output column is the PromQL expression itself.
    Example: `PROMQL http_rate=(sum by (instance) (rate(http_requests_total))) | SORT http_rate DESC`
  </definition>
</definitions>

`start`, `end`, and `time` accept an RFC 3339 timestamp with a UTC offset, such as `"2026-04-01T00:00:00Z"`,
or a Unix timestamp in seconds, which can be fractional, such as `1775001600`. Date math, such as `now-1h`,
isn't supported. In Kibana, the `?_tstart` and `?_tend` parameters reference the time range of the date picker,
such as `time=?_tend`.
`step` and `scrape_interval` accept a PromQL duration, such as `30s` or `5m`, or a number of seconds.

## Description

The `PROMQL` command takes standard PromQL parameters and a PromQL expression, runs the query, and returns the
results as regular ES|QL columns. You can continue to process the columns with other ES|QL commands.

### Time range

A range query needs either `step`, or both `start` and `end`, from which the step is derived using `buckets`.
In Kibana, the date picker provides `start` and `end`. Without a time range and without `step`, the query fails.
With `step` but no time range, the query covers all data in the index.

### Output columns

The result contains the following columns:

| Column                                                  | Type      | Description                                                                       |
|---------------------------------------------------------|-----------|-----------------------------------------------------------------------------------|
| The PromQL expression (or `<result_name>` if specified) | `double`  | The computed metric value                                                         |
| `step`                                                  | `date`    | The timestamp for each evaluation step. For an instant query, the evaluation time |
| Grouping labels (if any)                                | `keyword` | One column per grouping label from `by` clauses                                   |
| `_timeseries` (if any)                                  | `keyword` | The labels of each series as a JSON string                                        |

The label columns depend on the outermost aggregation of the PromQL expression:
- With a `by` grouping, such as `sum by (instance) (...)`, each grouping label gets its own output column.
- With an aggregation without grouping, such as `sum(...)`, there are no label columns, and the result is a single series.
- Without a cross-series aggregation, such as `rate(http_requests_total)`, or with a `without` grouping, such as
  `sum without (pod) (...)`, the remaining labels of each series are returned in a single `_timeseries` column as a JSON string.

A range query returns one row per series and evaluation step. An instant query returns one row per series.

### Index patterns

The `index` parameter accepts the same patterns as `FROM` and `TS`, including wildcards and comma-separated lists.
If omitted, it defaults to `metrics-*`, which queries matching indices configured with
[`index.mode: time_series`](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4641/manage-data/data-store/data-streams/time-series-data-stream-tsds).
The Prometheus-compatible `query` and `query_range` endpoints use the same default when the `{index}` path parameter is omitted.

### Implicit range selectors

In standard PromQL, functions like `rate` require a range selector: `rate(http_requests_total[5m])`.
The `PROMQL` command allows omitting the range selector entirely. When the range selector is absent, the window is
determined automatically as `max(step, scrape_interval)`.
For example: `PROMQL scrape_interval=15s http_rate=(sum(rate(http_requests_total)))`.
An implicit window adapts to the time range and step. A fixed window doesn't: when it's shorter than the step,
such as `[5m]` with a step of `1h`, each step only reflects the last 5 minutes and ignores the samples in between.
A fixed window is only useful when you need exactly that window, such as the rate over the last 5 minutes.

### Time range durations

<applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>
A duration, such as in a range selector, can be the difference of two RFC 3339 timestamp parameters. For example,
`[?_tend - ?_tstart]` spans the time range of the date picker in Kibana.

## Best practices

- Set `index` explicitly instead of relying on the `metrics-*` default, to narrow the data scanned.
- Omit range selectors, also when porting a Prometheus query: write `rate(http_requests_total)` instead of
  `rate(http_requests_total[5m])`, so the window adapts to the time range and step.
  Refer to [Implicit range selectors](#esql-promql-implicit-range-selectors).
  The exception is a value over the whole time range. Refer to [Single-value results](#esql-promql-single-value).
- Name the result, such as `http_rate=(...)`. Otherwise, the value column is named after the expression text,
  which changes whenever the expression is reformatted, so later commands can't reliably reference it.
- Match the function to the metric type: use `rate`, `irate`, or `increase` for counters, and functions such as
  `avg_over_time` or `max_over_time`, or the raw metric, for gauges.
  For native histograms, use `increase` instead of `rate`, and wrap the result in a histogram function, such as
  `histogram_quantile(0.99, sum by (job) (increase(http_request_duration_seconds)))`.
- In Kibana, omit `start` and `end` so the query follows the date picker. Elsewhere, set `start` and `end`
  explicitly, rather than only `step`, which covers all data in the index.
- Filter by labels in the PromQL selector, such as `network.cost{cluster!="prod"}`, rather than with `WHERE`
  after `PROMQL`. Selector filters reduce the data read, and a later `WHERE` doesn't.


### Single-value results

A range query returns a value for every step. To get a single value per series, such as for a metric or gauge chart
or a ranking, use an [instant query](#esql-promql-instant-query). In Kibana, set `time=?_tend` to evaluate the
expression at the end of the time range of the date picker
(refer to [time range parameters](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4641/explore-analyze/query-filter/languages/esql-kibana)).
- For the current value, such as the current request rate, omit the range selector:
  ```esql
  PROMQL index=metrics-generic.prometheus-* time=?_tend http_rate=(sum(rate(http_requests_total)))
  ```
- For a value over the whole time range, such as the total number of requests, use a range selector that spans the
  time range, `[?_tend - ?_tstart]` <applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>:
  ```esql
  PROMQL index=metrics-generic.prometheus-* time=?_tend
    total_requests=(sum(increase(http_requests_total[?_tend - ?_tstart])))
  ```
  This is also how to port a Grafana query that uses the `$__range` variable, or its `$__range_s` and
  `$__range_ms` variants: replace the range selector, such as `[$__range]` or `[${__range_s}s]`, with
  `[?_tend - ?_tstart]`, and set `time=?_tend`.
  Refer to [Time range durations](#esql-promql-time-range-durations).


## Examples


### Fully adaptive query

Rely on Kibana's date picker for the time range, and let `step` and range selectors be inferred automatically:
```esql
PROMQL index=metrics-generic.prometheus-* http_rate=(sum(rate(http_requests_total)))
```

This is the recommended pattern for Kibana dashboards. The query responds to the date picker, adjusts the step size
to the selected time range, and sizes the range selector window accordingly.

### Instant query

<applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>
Evaluate the expression once, for example to get the current value of a metric:
```esql
PROMQL index=metrics-generic.prometheus-*
  time="2026-04-01T01:00:00Z"
  http_rate=(sum(rate(http_requests_total)))
```


### Cross-series aggregation by label

```esql
PROMQL index=k8s step=1h result=(sum by (cluster) (network.cost))
| SORT result
```


| result:double | step:datetime            | cluster:keyword |
|---------------|--------------------------|-----------------|
| 15.875        | 2024-05-10T00:00:00.000Z | staging         |
| 18.625        | 2024-05-10T00:00:00.000Z | prod            |
| 26.5          | 2024-05-10T00:00:00.000Z | qa              |


### Label filtering with named result

```esql
PROMQL index=k8s step=1h cost=(max by (cluster) (network.total_bytes_in{cluster!="prod"}))
| SORT cluster
```


| cost:double | step:datetime            | cluster:keyword |
|-------------|--------------------------|-----------------|
| 10797.0     | 2024-05-10T00:00:00.000Z | qa              |
| 7403.0      | 2024-05-10T00:00:00.000Z | staging         |


### Post-processing with ES|QL

Pipe PromQL results into ES|QL commands for further aggregation:
```esql
PROMQL index=k8s step=1h bytes=(max by (cluster) (network.bytes_in))
| STATS max_bytes=MAX(bytes) BY cluster
| SORT cluster
```


| max_bytes:double | cluster:keyword |
|------------------|-----------------|
| 931.0            | prod            |
| 972.0            | qa              |
| 238.0            | staging         |


### Ad-hoc query with inferred step

For queries outside Kibana, set `start` and `end` explicitly. The step and range selector are still inferred
automatically from the time range and the default `buckets` count:
```esql
PROMQL index=metrics-generic.prometheus-*
  start="2026-04-01T00:00:00Z"
  end="2026-04-01T01:00:00Z"
  http_rate=(sum(rate(http_requests_total)))
```


### Enrich with a lookup

Join PromQL results with external data using ES|QL commands:
```esql
PROMQL index=metrics-generic.prometheus-*
  http_rate=(sum by (instance) (rate(http_requests_total)))
| LOOKUP JOIN instance_metadata ON instance
```


## Prometheus compatibility

This section lists the differences from Prometheus 3.x.
Sections follow [Querying basics](https://prometheus.io/docs/prometheus/latest/querying/basics/) in the Prometheus documentation.

### Histograms

In the examples on this page, `h` is a native histogram metric.
**Supported**
<applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> Native histograms, stored as `exponential_histogram` fields.
`histogram_quantile`, `histogram_count`, `histogram_sum`, `histogram_avg`, `increase`, and `sum` support them, such as `histogram_quantile(0.9, sum by (job) (increase(h[5m])))`.
<applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> `histogram_fraction` and `changes` support them too.
Classic histograms, stored as `_bucket` series with an `le` label, such as `histogram_quantile(0.9, sum by (le) (rate(x_bucket[5m])))`.
**Not supported yet**
<table>
  | #   | Construct                                                                                                                                                                   | Example                | Workaround                                                 | Issue                                                             |
  |-----|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------|------------------------------------------------------------|-------------------------------------------------------------------|
  | 1   | Native histogram as the result <applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> | `h`, `increase(h[5m])` | Wrap it in a histogram function, such as `histogram_count` | [#152263](https://github.com/elastic/elasticsearch/issues/152263) |
</table>


### Range vector

**Supported**
Range vectors as function arguments, such as `rate(x[5m])`.
In the `PROMQL` command, the range can be left out, such as `rate(x)`.
Refer to [Implicit range selectors](#esql-promql-implicit-range-selectors).
**Not supported yet**
<table>
  | #   | Construct                                      | Example | Workaround | Issue                                                             |
  |-----|------------------------------------------------|---------|------------|-------------------------------------------------------------------|
  | 2   | Range vector as the result of an instant query | `x[5m]` | -          | [#164220](https://github.com/elastic/elasticsearch/issues/164220) |
</table>


### Literals

**Supported**
String literals in single quotes, double quotes, or backticks.
Float literals, including scientific notation, hexadecimal, `Inf`, and `NaN`, such as `1e3` and `0x1f`.
Time durations, such as `1h30m`, and duration arithmetic, such as `x[5m + 1m]`.
**Not supported yet**
<table>
  | #   | Construct                                                                                                       | Example                              | Workaround           | Issue                                                             |
  |-----|-----------------------------------------------------------------------------------------------------------------|--------------------------------------|----------------------|-------------------------------------------------------------------|
  | 3   | `step()`, `range()`, `min_of()`, and `max_of()` in a duration (Prometheus: `promql-duration-expr` feature flag) | `x[step()]`, `x[min_of(5m, step())]` | Use a fixed duration | [#164221](https://github.com/elastic/elasticsearch/issues/164221) |
</table>


### Time series selectors


#### Instant vector selectors

**Supported**
A metric name with the label matchers `=`, `!=`, `=~`, and `!~`, such as `http_requests_total{job="api", code=~"5.."}`.
**Not supported yet**
<table>
  | #   | Construct                      | Example                 | Workaround                                            | Issue                                                             |
  |-----|--------------------------------|-------------------------|-------------------------------------------------------|-------------------------------------------------------------------|
  | 4   | Selector without a metric name | `{job="api"}`           | -                                                     | [#146977](https://github.com/elastic/elasticsearch/issues/146977) |
  | 5   | Regex matcher on `__name__`    | `{__name__=~"node_.*"}` | Combine the metrics with `or`: `node_cpu or node_mem` | [#146977](https://github.com/elastic/elasticsearch/issues/146977) |
</table>

**Behaves differently**
<table>
  | #   | Construct                      | Example                        | Elasticsearch | Prometheus                  | Issue                                                             |
  |-----|--------------------------------|--------------------------------|---------------|-----------------------------|-------------------------------------------------------------------|
  | 6   | Negative matcher on `__name__` | `{__name__!="up", job="node"}` | No results    | Every other matching metric | [#146977](https://github.com/elastic/elasticsearch/issues/146977) |
</table>


#### Range vector selectors

**Supported**
A range selector with a duration, such as `x[5m]` or `x[1h30m]`.
**Not supported yet**
<table>
  | #   | Construct                                                                                        | Example                    | Workaround | Issue                                                             |
  |-----|--------------------------------------------------------------------------------------------------|----------------------------|------------|-------------------------------------------------------------------|
  | 7   | `anchored` and `smoothed` modifiers (Prometheus: `promql-extended-range-selectors` feature flag) | `increase(x[5m] anchored)` | -          | [#164222](https://github.com/elastic/elasticsearch/issues/164222) |
</table>


#### Offset modifier

**Supported**
<applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> `offset` on instant and range selectors, such as `x offset 5m` and `rate(x[5m] offset 1h)`.
**Behaves differently**
<table>
  | #   | Construct                                     | Example        | Elasticsearch                                                       | Prometheus                        | Issue |
  |-----|-----------------------------------------------|----------------|---------------------------------------------------------------------|-----------------------------------|-------|
  | 8   | Negative offset at the end of the query range | `x offset -5m` | No value at the last steps. Samples after the query end aren't read | Reads samples after the query end | -     |
</table>


#### @ modifier

**Not supported yet**
<table>
  | #   | Construct    | Example                       | Workaround | Issue                                                             |
  |-----|--------------|-------------------------------|------------|-------------------------------------------------------------------|
  | 9   | `@` modifier | `x @ 1700000000`, `x @ end()` | -          | [#164139](https://github.com/elastic/elasticsearch/issues/164139) |
</table>


### Subquery

**Not supported yet**
<table>
  | #   | Construct | Example                             | Workaround | Issue                                                             |
  |-----|-----------|-------------------------------------|------------|-------------------------------------------------------------------|
  | 10  | Subquery  | `max_over_time(rate(x[5m])[1h:1m])` | -          | [#164139](https://github.com/elastic/elasticsearch/issues/164139) |
</table>


### Gotchas


#### Staleness

**Supported**
A selector without a range, such as `http_requests_total`, returns the latest sample of each series within the lookback window at each evaluation time.
**Behaves differently**
<table>
  | #   | Construct                   | Example               | Elasticsearch                                                            | Prometheus                                                  | Issue                                                             |
  |-----|-----------------------------|-----------------------|--------------------------------------------------------------------------|-------------------------------------------------------------|-------------------------------------------------------------------|
  | 11  | Lookback in instant queries | `/api/v1/query`       | Five minutes, fixed                                                      | Five minutes by default, configurable with `lookback_delta` | [#149939](https://github.com/elastic/elasticsearch/issues/149939) |
  | 12  | Lookback in range queries   | `/api/v1/query_range` | One `step`. A series disappears at the first step without its own sample | Five minutes. The last sample repeats until then            | -                                                                 |
  | 13  | Staleness markers           | -                     | Ignored. A series disappears once its samples leave the lookback window  | A series disappears at its staleness marker                 | -                                                                 |
</table>

Sections follow [Operators](https://prometheus.io/docs/prometheus/latest/querying/operators/) in the Prometheus documentation.
Each [operator reference entry](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/operators) lists further differences.

### Binary operators


#### Arithmetic binary operators

**Supported**
`+`, `-`, `*`, `/`, `%`, and `^` between scalars, between an instant vector and a scalar, such as `rate(x[5m]) * 60`, and between two instant vectors.
[Vector matching](#prometheus-compatibility-vector-matching) lists which pairs of instant vectors match.
**Not supported yet**
<table>
  | #   | Construct                                                                                                                                                              | Example            | Workaround | Issue                                                             |
  |-----|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|------------|-------------------------------------------------------------------|
  | 14  | Native histogram operands <applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> | `h * 2`, `h1 + h2` | -          | [#152263](https://github.com/elastic/elasticsearch/issues/152263) |
</table>


#### Trigonometric binary operators

**Not supported yet**
<table>
  | #   | Construct | Example     | Workaround | Issue                                                             |
  |-----|-----------|-------------|------------|-------------------------------------------------------------------|
  | 15  | `atan2`   | `x atan2 y` | -          | [#164153](https://github.com/elastic/elasticsearch/issues/164153) |
</table>


#### Comparison binary operators

**Supported**
`==`, `!=`, `>`, `<`, `>=`, and `<=` between an instant vector and a number, such as `rate(x[5m]) > 100`, with or without `bool`, such as `x > bool 100`.
**Not supported yet**
<table>
  | #   | Construct                                                 | Example                           | Workaround                                    | Issue                                                             |
  |-----|-----------------------------------------------------------|-----------------------------------|-----------------------------------------------|-------------------------------------------------------------------|
  | 16  | Filter with a scalar as the first operand                 | `100 < x`, `time() <= x`          | Swap the operands: `x > 100`                  | [#158184](https://github.com/elastic/elasticsearch/issues/158184) |
  | 17  | Non-literal scalar as the second operand                  | `x > time()`, `x > scalar(y)`     | -                                             | [#158184](https://github.com/elastic/elasticsearch/issues/158184) |
  | 18  | Two vectors, or `bool` with a scalar as the first operand | `x > y`, `100 > bool x`           | For `bool`, swap the operands: `x < bool 100` | [#158184](https://github.com/elastic/elasticsearch/issues/158184) |
  | 19  | Comparison inside another expression                      | `(x > 100) + 1`, `count(x > 100)` | -                                             | [#158184](https://github.com/elastic/elasticsearch/issues/158184) |
</table>


#### Logical/set binary operators

**Supported**
<applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> `or` between instant vectors, such as `rate(a[5m]) or rate(b[5m])`.
An `or` chain takes at most 8 operands by default.
<applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> Change the limit with the `esql.query.max_branch_count_per_merge` cluster setting.
**Not supported yet**
<table>
  | #   | Construct                              | Example                                   | Workaround | Issue                                                             |
  |-----|----------------------------------------|-------------------------------------------|------------|-------------------------------------------------------------------|
  | 20  | `and`, `unless`                        | `a and b`, `a unless b`                   | -          | [#158184](https://github.com/elastic/elasticsearch/issues/158184) |
  | 21  | `or` with `on(...)` or `ignoring(...)` | `a or on(job) b`                          | -          | [#158184](https://github.com/elastic/elasticsearch/issues/158184) |
  | 22  | `or` inside another expression         | `(sum(a) or sum(b)) * 2`, `count(a or b)` | -          | [#158184](https://github.com/elastic/elasticsearch/issues/158184) |
</table>


### Vector matching

**Supported**
Arithmetic between unaggregated metrics stored together in the same time series, such as `network.bytes_in / network.connections`, without explicit vector matching modifiers.
Arithmetic between operands aggregated without grouping, such as `sum(a) / sum(b)`.
<applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> One-to-one, many-to-one, and one-to-many matching between operands aggregated with `by(...)`, including `on`, `ignoring`, `group_left`, and `group_right`.
For example: `sum by (instance) (a) / on(instance) sum by (instance) (b)`.
**Not supported yet**
<table>
  | #   | Construct                                                                                                                                                                                                                                                       | Example                                                | Workaround                                                                                               | Issue                                                             |
  |-----|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------|----------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|
  | 23  | Different unaggregated metrics stored as separate series with a `__name__` dimension, such as data ingested with Prometheus remote write <applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> | `a / b`                                                | Aggregate both by the labels to match on: `sum by (instance) (a) / sum by (instance) (b)`                | [#158184](https://github.com/elastic/elasticsearch/issues/158184) |
  | 24  | `on`, `ignoring`, `group_left`, or `group_right` with an operand that isn't aggregated                                                                                                                                                                          | `a / on(instance) b`, `a * on(pod) group_left(node) b` | Aggregate both operands by the labels to match on                                                        | [#157669](https://github.com/elastic/elasticsearch/issues/157669) |
  | 25  | Operands aggregated differently <applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>                                                                                    | `sum(a) / b`, `b / scalar(sum(a))`, `topk(2, a) * b`   | Aggregate both operands alike: `sum(a) / sum(b)`. A number, such as `sum(a) * 2`, and `or` are supported | [#158184](https://github.com/elastic/elasticsearch/issues/158184) |
  | 26  | Nested aggregation next to an operand that reads a metric <applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>                                                          | `count(count by (pod) (a)) / sum(b)`                   | -                                                                                                        | [#158184](https://github.com/elastic/elasticsearch/issues/158184) |
  | 27  | A different `offset` per operand <applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>                                                                                   | `rate(x[5m]) / rate(x[5m] offset 1h)`                  | `or` is supported                                                                                        | [#158184](https://github.com/elastic/elasticsearch/issues/158184) |
  | 28  | `label_replace` or `label_join` as an operand                                                                                                                                                                                                                   | `label_replace(a, ...) * on(pod) group_left(node) b`   | -                                                                                                        | [#157672](https://github.com/elastic/elasticsearch/issues/157672) |
  | 29  | `fill()`, `fill_left()`, and `fill_right()` (Prometheus: `promql-binop-fill-modifiers` feature flag)                                                                                                                                                            | `a + fill(0) b`                                        | -                                                                                                        | [#164223](https://github.com/elastic/elasticsearch/issues/164223) |
</table>

**Behaves differently**
<table>
  | #   | Construct                                                                                                                                                  | Example | Elasticsearch | Prometheus                           | Issue                                                             |
  |-----|------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|---------------|--------------------------------------|-------------------------------------------------------------------|
  | 30  | Different unaggregated metrics stored as separate series <applies-to>Elastic Stack: Generally available in 9.5, Elastic Stack: Preview in 9.4</applies-to> | `a / b` | No results    | Series matched on their other labels | [#158184](https://github.com/elastic/elasticsearch/issues/158184) |
</table>


### Aggregation operators

**Supported**
- `sum`, `avg`, `min`, `max`, `count`, `quantile`, `stddev`, and `stdvar`, with `by(...)`, `without(...)`, or no grouping.
- <applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> `topk`, `bottomk`, `limitk`, and `limit_ratio`.

**Not supported yet**
<table>
  | #   | Construct                                                         | Example                                           | Workaround                                         | Issue                                                                                                                                |
  |-----|-------------------------------------------------------------------|---------------------------------------------------|----------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
  | 31  | `group`, `count_values`                                           | `group by (job) (x)`, `count_values("v", x)`      | -                                                  | [#164009](https://github.com/elastic/elasticsearch/issues/164009), [#136248](https://github.com/elastic/elasticsearch/issues/136248) |
  | 32  | `topk`, `bottomk`, `limitk`, or `limit_ratio` with `without(...)` | `topk without (instance) (1, x)`                  | Use `by(...)`                                      | [#157671](https://github.com/elastic/elasticsearch/issues/157671)                                                                    |
  | 33  | Parameter that isn't a number literal                             | `topk(scalar(a), b)`, `quantile(scalar(a), b)`    | Use a number                                       | [#164225](https://github.com/elastic/elasticsearch/issues/164225)                                                                    |
  | 34  | Nested `without(...)`                                             | `sum without (pod) (max without (container) (x))` | Use `by(...)`                                      | [#157671](https://github.com/elastic/elasticsearch/issues/157671)                                                                    |
  | 35  | `without(...)` as an operand                                      | `sum without (method) (x) * 2`                    | Use `by(...)`                                      | [#145308](https://github.com/elastic/elasticsearch/issues/145308)                                                                    |
  | 36  | Grouping by a label named `step`                                  | `sum by (step) (x)`                               | `step` is an output column of the `PROMQL` command | -                                                                                                                                    |
</table>

**Behaves differently**
<table>
  | #   | Construct                                                                                                                                      | Example            | Elasticsearch                     | Prometheus                                                                                  | Issue |
  |-----|------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|-----------------------------------|---------------------------------------------------------------------------------------------|-------|
  | 37  | `quantile`                                                                                                                                     | `quantile(0.9, x)` | Approximate, using t-digest       | Exact, with linear interpolation                                                            | -     |
  | 38  | `limitk`, `limit_ratio` <applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> | `limitk(10, x)`    | A different, equally valid subset | A deterministic pseudo-random subset, with the `promql-experimental-functions` feature flag | -     |
</table>


### Functions

Rows follow [Functions](https://prometheus.io/docs/prometheus/latest/querying/functions/) in the Prometheus documentation and are sorted by function name.
Each [function reference entry](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/functions) lists further differences.
**Supported**
The functions in the [PromQL functions](https://www.elastic.co/elastic/docs-builder/docs/4641/reference/query-languages/promql/functions) reference, such as `rate`, `increase`, `histogram_quantile`, `label_replace`, and the `<aggregation>_over_time` functions.
**Not supported yet**
<table>
  | #   | Construct                                                                                                                                                                                                                                    | Example                                                                                                                                | Workaround                                                                                                                                                                          | Issue                                                                                                                                                                                                   |
  |-----|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
  | 39  | Functions listed under [Not supported yet](/elastic/docs-builder/docs/4641/reference/query-languages/promql/functions#promql-not-supported)                                                                                                  | `absent(x)`, `resets(x[1h])`, `info(x)`                                                                                                | -                                                                                                                                                                                   | [#164001](https://github.com/elastic/elasticsearch/issues/164001), [#164003](https://github.com/elastic/elasticsearch/issues/164003), [#164002](https://github.com/elastic/elasticsearch/issues/164002) |
  | 40  | Parameter that isn't a number literal                                                                                                                                                                                                        | `clamp_min(a, scalar(b))`, `histogram_quantile(scalar(a), b)`                                                                          | Use a number                                                                                                                                                                        | [#164225](https://github.com/elastic/elasticsearch/issues/164225)                                                                                                                                       |
  | 41  | `histogram_quantile` or `histogram_fraction` over `topk`, `bottomk`, `limitk`, or `limit_ratio` <applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>                       | `histogram_quantile(0.9, topk(5, a))`                                                                                                  | Aggregate with `by(...)` first: `histogram_quantile(0.9, topk(5, sum by (job, le) (a)))`                                                                                            | [#164226](https://github.com/elastic/elasticsearch/issues/164226)                                                                                                                                       |
  | 42  | `histogram_quantile` or `histogram_fraction` over `without(...)` <applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>                                                      | `histogram_quantile(0.9, sum without (instance) (rate(x[5m])))`                                                                        | Use `by(...)`                                                                                                                                                                       | [#157671](https://github.com/elastic/elasticsearch/issues/157671)                                                                                                                                       |
  | 43  | `histogram_stddev`, `histogram_stdvar` <applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>                                                          | `histogram_stddev(h)`                                                                                                                  | -                                                                                                                                                                                   | [#164152](https://github.com/elastic/elasticsearch/issues/164152)                                                                                                                                       |
  | 44  | `irate`, `delta` on native histograms <applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>                                                           | `irate(h[5m])`                                                                                                                         | -                                                                                                                                                                                   | [#152263](https://github.com/elastic/elasticsearch/issues/152263)                                                                                                                                       |
  | 45  | `label_replace` or `label_join` outside `by(...)`                                                                                                                                                                                            | `label_replace(x, "dst", "$1", "instance", "(.*):.*")`                                                                                 | <applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> Use it inside `by(...)`: `sum by (dst) (label_replace(...))` | [#157672](https://github.com/elastic/elasticsearch/issues/157672)                                                                                                                                       |
  | 46  | `label_replace` or `label_join` over an aggregation <applies-to>Elastic Stack: Planned</applies-to>                                                                                                                                          | `label_replace(sum by (job) (x), ...)`                                                                                                 | -                                                                                                                                                                                   | [#157672](https://github.com/elastic/elasticsearch/issues/157672)                                                                                                                                       |
  | 47  | `label_replace` or `label_join` over anything other than a `by(...)` aggregation, or that overwrites a grouping label <applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to> | `label_replace(sum without (instance) (x), "dst", "$1", "job", "(.*)")`, `label_replace(sum by (job) (x), "job", "$1", "job", "(.*)")` | Aggregate with `by(...)` and write a new label: `label_replace(sum by (job) (x), "dst", "$1", "job", "(.*)")`                                                                       | [#157672](https://github.com/elastic/elasticsearch/issues/157672)                                                                                                                                       |
  | 48  | `label_replace` or `label_join` that writes a label named `step` <applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>                                                      | `label_replace(x, "step", ...)`                                                                                                        | `step` is an output column of the `PROMQL` command                                                                                                                                  | -                                                                                                                                                                                                       |
  | 49  | `predict_linear` with a horizon computed by a function <applies-to>Elastic Stack: Planned</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>                                                                | `predict_linear(x[1h], time())`, `predict_linear(x[1h], scalar(y))`                                                                    | Use a number, a duration literal, or arithmetic on literals                                                                                                                         | [#164225](https://github.com/elastic/elasticsearch/issues/164225)                                                                                                                                       |
  | 50  | `rate` on native histograms <applies-to>Elastic Stack: Generally available since 9.5</applies-to> <applies-to>Elastic Cloud Serverless: Generally available</applies-to>                                                                     | `rate(h[5m])`                                                                                                                          | Use `increase`: `histogram_quantile(0.99, sum by (job) (increase(h[5m])))`                                                                                                          | [#152263](https://github.com/elastic/elasticsearch/issues/152263)                                                                                                                                       |
  | 51  | `sort` or `sort_desc` below the top level                                                                                                                                                                                                    | `sum(sort(x))`                                                                                                                         | Remove it. Sorting has no effect there                                                                                                                                              | [#164227](https://github.com/elastic/elasticsearch/issues/164227)                                                                                                                                       |
  | 52  | `step()`, `range()`, `start()`, `end()` (Prometheus: `promql-experimental-functions` feature flag)                                                                                                                                           | `time() - start()`                                                                                                                     | -                                                                                                                                                                                   | [#164228](https://github.com/elastic/elasticsearch/issues/164228)                                                                                                                                       |
</table>

**Behaves differently**
<table>
  | #   | Construct                                                                 | Example                               | Elasticsearch                                                                                    | Prometheus                                               | Issue |
  |-----|---------------------------------------------------------------------------|---------------------------------------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------|-------|
  | 53  | `absent_over_time` with no data or over a gap                             | `absent_over_time(x{job="none"}[5m])` | No results                                                                                       | `1`, with the labels of the selector's equality matchers | -     |
  | 54  | `histogram_count`, `histogram_sum`, or `histogram_avg` on classic buckets | `histogram_count(rate(x_bucket[5m]))` | An error. They accept only native histograms                                                     | No results                                               | -     |
  | 55  | `quantile_over_time`                                                      | `quantile_over_time(0.9, x[5m])`      | Approximate, using t-digest                                                                      | Exact, with linear interpolation                         | -     |
  | 56  | `rate`, `increase`                                                        | `rate(x[5m])`                         | Computed over fixed time buckets, interpolated at bucket boundaries. Results can differ slightly | Extrapolated within each range window                    | -     |
</table>