> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-parallel-read-in-order-multi-part.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Prometheus HTTP API support in ClickHouse: remote write, remote read, and PromQL queries over a TimeSeries table.

# Prometheus HTTP API and PromQL

export const PrivatePreviewBadge = () => {
  return <div className="privatePreviewBadge">
            <div className="privatePreviewIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M5.33301 6.66667V4.66667V4.66667C5.33301 3.194 6.52701 2 7.99967 2V2C9.47234 2 10.6663 3.194 10.6663 4.66667V4.66667V6.66667" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path d="M8.00033 9.33337V11.3334" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path fillRule="evenodd" clipRule="evenodd" d="M11.333 14H4.66634C3.92967 14 3.33301 13.4033 3.33301 12.6666V7.99996C3.33301 7.26329 3.92967 6.66663 4.66634 6.66663H11.333C12.0697 6.66663 12.6663 7.26329 12.6663 7.99996V12.6666C12.6663 13.4033 12.0697 14 11.333 14Z" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
        </div>
            {'Private preview'}
        </div>;
};

<PrivatePreviewBadge />

ClickHouse implements the Prometheus HTTP API over a [`TimeSeries`](/reference/engines/table-engines/integrations/time-series) table. One handler serves remote write, remote read, instant PromQL queries, and range PromQL queries.

To expose ClickHouse's own metrics for a Prometheus server to scrape, see the [Prometheus metrics endpoint](/concepts/features/interfaces/prometheus-metrics).

<h2 id="prerequisites">
  Prerequisites
</h2>

The setup steps differ between ClickHouse Cloud and self-managed ClickHouse. Follow the section for your deployment.

<h3 id="prerequisites-cloud">
  ClickHouse Cloud
</h3>

<Note>
  PromQL support in ClickHouse Cloud is in private preview. The services that take part in the private preview already have the `enable_time_series_table` setting and the Prometheus API endpoints configured. Other ClickHouse Cloud services do not have this configuration, and you cannot enable the feature yourself on such a service. The `SET enable_time_series_table` statement and the `http_handlers` configuration in the next sections apply to self-managed deployments.
</Note>

On a service that takes part in the private preview, continue at [Create a TimeSeries table](#create-a-timeseries-table). The service serves the endpoint paths listed in the [endpoint table](#configure-prometheus-api).

<h3 id="enable-the-timeseries-setting">
  Self-managed: enable the TimeSeries setting
</h3>

Enable the `enable_time_series_table` setting for the user that creates and accesses the table:

```sql theme={null}
SET enable_time_series_table = 1;
```

For HTTP API requests, enable `enable_time_series_table` in the profile of the API user.

<h3 id="configure-prometheus-api">
  Self-managed: configure the Prometheus API endpoints
</h3>

Configure one prefix-routed handler on the main ClickHouse HTTP port:

```xml theme={null}
<http_handlers>
    <defaults/>
    <rule>
        <url_prefix>/prometheus/api/v1</url_prefix>
        <handler>
            <type>prometheus_api_v1</type>
        </handler>
    </rule>
</http_handlers>
```

`<defaults/>` preserves the built-in handlers for endpoints such as `/ping` and for SQL requests. The prefix above exposes these endpoints through one handler:

| Endpoint | Purpose |
| - | - |
| `/prometheus/api/v1/write` | Prometheus remote write |
| `/prometheus/api/v1/read` | Prometheus remote read |
| `/prometheus/api/v1/query` | Instant PromQL queries |
| `/prometheus/api/v1/query_range` | Range PromQL queries |
| `/prometheus/api/v1/format_query` | PromQL expression formatting |
| `/prometheus/api/v1/series` | Series metadata |
| `/prometheus/api/v1/metadata` | Metric-family metadata |

The example omits `database` and `table` from the handler. Each request must provide the `table` query parameter (except for `/format_query`, which only parses the given PromQL expression and doesn't need a table). It can also provide `database`, use a qualified table name such as `prometheus.metrics`, or omit the database to use `default`. This allows one handler to serve multiple `TimeSeries` tables.

To use one fixed table for every request, configure it in the handler:

```xml theme={null}
<handler>
    <type>prometheus_api_v1</type>
    <database>prometheus</database>
    <table>metrics</table>
</handler>
```

A table configured in the handler cannot be overridden by request parameters.

Routing and handler settings:

| Name | Default | Description |
| - | - | - |
| `url_prefix` | none | Rule filter that matches every request path that starts with the configured prefix. |
| `table` | none | The name of a `TimeSeries` table. When omitted, the request must provide the `table` query parameter. The configured name can include a database. |
| `database` | none | The database containing the table. A request can provide it as a query parameter. When omitted, ClickHouse uses a database from a qualified `table` value or falls back to `default`. |

<h3 id="create-a-timeseries-table">
  Create a TimeSeries table
</h3>

Create a database and a `TimeSeries` table:

```sql theme={null}
CREATE DATABASE prometheus;
CREATE TABLE prometheus.metrics ENGINE = TimeSeries;
```

<h2 id="remote-write">
  Ingest metrics with remote write
</h2>

ClickHouse supports the [Prometheus remote-write protocol](https://prometheus.io/docs/specs/remote_write_spec/). Configure Prometheus to write to the handler:

```yaml theme={null}
remote_write:
  - url: https://clickhouse.example.com:8443/prometheus/api/v1/write?database=prometheus&table=metrics
    basic_auth:
      username: default
      password: <password>
```

Prometheus sends samples to the `prometheus.metrics` table.

To batch data from many concurrent remote-write requests into fewer parts, enable [asynchronous inserts](/reference/settings/session-settings/async-insert#async_insert) by adding the `async_insert` setting to the URL (or by enabling it in the user profile):

```yaml theme={null}
remote_write:
  - url: https://clickhouse.example.com:8443/prometheus/api/v1/write?database=prometheus&table=metrics&async_insert=1
```

ClickHouse acknowledges an asynchronous remote-write request only after the data is flushed to all inner tables of the `TimeSeries` table, regardless of the [`wait_for_async_insert`](/reference/settings/session-settings/wait-for#wait_for_async_insert) setting: the remote-write protocol treats an acknowledged write as durable. If the flush fails, the request returns an error and Prometheus retries it.

<h2 id="promql-query-support">
  Query with PromQL
</h2>

Use the instant-query endpoint to evaluate a PromQL expression at one point in time:

```bash theme={null}
curl --user default:<password> --get \
  "https://clickhouse.example.com:8443/prometheus/api/v1/query" \
  --data-urlencode "query=rate(http_requests_total[5m])" \
  --data-urlencode "database=prometheus" \
  --data-urlencode "table=metrics"
```

Use the range-query endpoint to evaluate an expression over a time range:

```bash theme={null}
curl --user default:<password> --get \
  "https://clickhouse.example.com:8443/prometheus/api/v1/query_range" \
  --data-urlencode "query=rate(http_requests_total[5m])" \
  --data-urlencode "start=2026-08-15T12:00:00Z" \
  --data-urlencode "end=2026-08-15T13:00:00Z" \
  --data-urlencode "step=60s" \
  --data-urlencode "database=prometheus" \
  --data-urlencode "table=metrics"
```

The query endpoints also accept the parameters in a form body. Without `--get`, curl sends the parameters as `application/x-www-form-urlencoded` over `POST`:

```bash theme={null}
curl --user default:<password> \
  "https://clickhouse.example.com:8443/prometheus/api/v1/query_range" \
  --data-urlencode "query=rate(http_requests_total[5m])" \
  --data-urlencode "start=2026-08-15T12:00:00Z" \
  --data-urlencode "end=2026-08-15T13:00:00Z" \
  --data-urlencode "step=60s" \
  --data-urlencode "database=prometheus" \
  --data-urlencode "table=metrics"
```

Use the format-query endpoint to parse and format a PromQL expression without evaluating it:

```bash theme={null}
curl --user default:<password> --get \
  "https://clickhouse.example.com:8443/prometheus/api/v1/format_query" \
  --data-urlencode "query=sum by(job)(http_requests_total{code=\"200\"})/2"
```

The expression is returned serialized from the parsed query, with the whitespace normalized, the comments removed, the redundant parentheses dropped, and the durations printed in the units the parser accepts: `sum by (job) (http_requests_total{code="200"}) / 2`. A duration keeps its units rather than becoming a number of seconds, so `rate(x[5m])` comes back as `rate(x[5m])`, and one that is not a whole number of a single unit is spelled with each unit it needs, so `rate(x[90s])` comes back as `rate(x[1m30s])`. This endpoint doesn't evaluate the expression, so it doesn't need the `database` and `table` parameters.

See the [supported PromQL features](/reference/functions/table-functions/prometheusQueryRange#supported-promql-features) for the function and aggregation operator list used by the HTTP API, the `promql` dialect, and the table functions.

<h3 id="grafana">
  Grafana
</h3>

Configure a Prometheus data source with the base URL ending before `/api/v1`:

```yaml theme={null}
apiVersion: 1
datasources:
  - name: ClickHouse Prometheus
    type: prometheus
    access: proxy
    url: https://clickhouse.example.com:8443/prometheus
    basicAuth: true
    basicAuthUser: default
    jsonData:
      httpMethod: POST
      customQueryParameters: database=prometheus&table=metrics
    secureJsonData:
      basicAuthPassword: <password>
```

Grafana appends `/api/v1/query` or `/api/v1/query_range` to this base URL and adds `customQueryParameters` to each request.

With `httpMethod: POST`, Grafana sends the query parameters in the request body. ClickHouse reads the request body and the URL query string, so `customQueryParameters` still applies. Use `POST` for long PromQL expressions, because a URL has a length limit.

<Note>
  Only the query endpoints `/api/v1/query`, `/api/v1/query_range`, and `/api/v1/format_query` and the metadata endpoints `/api/v1/series`, `/api/v1/labels`, `/api/v1/label/<name>/values`, and `/api/v1/metadata` are implemented. `/api/v1/series` requires at least one `match[]` series selector, supports the optional `start`, `end`, and `limit` parameters, and returns the union of the series matched by each selector. `/api/v1/labels` accepts the same parameters, with `match[]` being optional, and returns the sorted label names of the matched series (or of all series when no selectors are given). `/api/v1/label/<name>/values` accepts the same parameters as `/api/v1/labels` and returns the sorted values of one label, with `<name>` optionally using the Prometheus `U__...` escaping of label names that contain characters outside `[a-zA-Z0-9_]`. These endpoints cover what a Grafana Prometheus datasource uses for label browsing, template variables, and query-builder autocomplete.
</Note>

<h3 id="sql-entry-points">
  SQL entry points
</h3>

ClickHouse uses the same PromQL converter for the HTTP API, the `promql` dialect, and the [`prometheusQuery`](/reference/functions/table-functions/prometheusQuery) and [`prometheusQueryRange`](/reference/functions/table-functions/prometheusQueryRange) table functions.

Run PromQL directly with `clickhouse-client`:

```bash theme={null}
clickhouse-client \
  --dialect promql \
  --promql_database prometheus \
  --promql_table metrics \
  --query 'rate(http_requests_total[5m])'
```

Use the table functions to embed PromQL in a SQL query:

```sql theme={null}
SELECT *
FROM prometheusQuery(
    prometheus.metrics,
    'rate(http_requests_total[5m])',
    now()
);
```

<h2 id="metadata">
  Query metric metadata
</h2>

The `/prometheus/api/v1/metadata` endpoint returns the metric metadata stored in the `Metrics` target table of the `TimeSeries` table: the type, help text, and unit of each metric family. It supports the following Prometheus parameters in the URL query string:

| Parameter | Description |
| - | - |
| `metric` | Return metadata only for this metric family. |
| `limit` | Limit the number of returned metric families. A negative value means no limit; zero returns no metric families. |
| `limit_per_metric` | Limit the number of metadata objects returned for each metric family. Zero and negative values mean no limit. |

The default `Metrics` target table is a `ReplacingMergeTree` ordered by the metric family name: it keeps the most recently written metadata entry for each metric family. Several entries per family are returned only while the target table stores them — before its parts are merged, or when the table is defined with an engine that preserves them.

```bash theme={null}
curl --user default:<password> --get \
  "https://clickhouse.example.com:8443/prometheus/api/v1/metadata" \
  --data-urlencode "metric=http_requests_total" \
  --data-urlencode "database=prometheus" \
  --data-urlencode "table=metrics"
```

<h2 id="remote-read">
  Read metrics with remote read
</h2>

ClickHouse supports the [Prometheus remote-read protocol](https://prometheus.io/docs/prometheus/latest/querying/remote_read_api/) at `/prometheus/api/v1/read`.

Configure a Prometheus server to read from the same `TimeSeries` table:

```yaml theme={null}
remote_read:
  - url: https://clickhouse.example.com:8443/prometheus/api/v1/read?database=prometheus&table=metrics
    basic_auth:
      username: default
      password: <password>
```
