HTTP Interface

You can receive information about the progress of a query in the `X-ClickHouse-Progress` response headers. To do this, enable [`send_progress_in_http_headers`](/docs/operations/settings/settings#send_progress_in_http_headers).

Below is an example of the header sequence:

```
X-ClickHouse-Progress: {"read_rows":"261636","read_bytes":"2093088","total_rows_to_read":"1000000","elapsed_ns":"14050417","memory_usage":"22205975"}
X-ClickHouse-Progress: {"read_rows":"654090","read_bytes":"5232720","total_rows_to_read":"1000000","elapsed_ns":"27948667","memory_usage":"83400279"}
X-ClickHouse-Progress: {"read_rows":"1000000","read_bytes":"8000000","total_rows_to_read":"1000000","elapsed_ns":"38002417","memory_usage":"80715679"}
```

The possible header fields are:

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Header field Description|  |  |  |  |  |  |  |  |  |  |  |  |  |  | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | `read_rows` Number of rows read.|  |  |  |  |  |  |  |  |  |  |  |  | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | `read_bytes` Volume of data read in bytes.|  |  |  |  |  |  |  |  |  |  | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | `total_rows_to_read` Total number of rows to be read.|  |  |  |  |  |  |  |  | | --- | --- | --- | --- | --- | --- | --- | --- | | `written_rows` Number of rows written.|  |  |  |  |  |  | | --- | --- | --- | --- | --- | --- | | `written_bytes` Volume of data written in bytes.|  |  |  |  | | --- | --- | --- | --- | | `elapsed_ns` Query runtime in nanoseconds.|  |  | | --- | --- | | `memory_usage` Memory in bytes used by the query. (**Available from v25.11**) | | | | | | | | | | | | | | | |

Running requests don't stop automatically if the HTTP connection is lost. Parsing and data formatting are performed on the server-side, and using the network might be ineffective.

The following optional parameters exist:

|  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- |
| Parameters Description|  |  |  |  | | --- | --- | --- | --- | | `query_id` (optional) Can be passed as the query ID (any string). [`replace_running_query`](/docs/operations/settings/settings#replace_running_query)| `quota_key` (optional) Can be passed as the quota key (any string). ["Quotas"](/docs/operations/quotas) | | | | | |

The HTTP interface allows passing external data (external temporary tables) for querying. For more information, see ["External data for query processing"](/docs/engines/table-engines/special/external-data).

## Response buffering[​](#response-buffering "Direct link to Response buffering")

Response buffering can be enabled on the server-side. The following URL parameters are provided for this purpose:

- `buffer_size`
- `wait_end_of_query`

The following settings can be used:

- [`http_response_buffer_size`](/docs/operations/settings/settings#http_response_buffer_size)
- [`http_wait_end_of_query`](/docs/operations/settings/settings#http_wait_end_of_query)

`buffer_size` determines the number of bytes in the result to buffer in the server memory. If a result body is larger than this threshold, the buffer is written to the HTTP channel, and the remaining data is sent directly to the HTTP channel.

To ensure that the entire response is buffered, set `wait_end_of_query=1`. In this case, the data that isn't stored in memory will be buffered in a temporary server file.

For example:

```
curl -sS 'http://localhost:8123/?max_result_bytes=4000000&buffer_size=3000000&wait_end_of_query=1' -d 'SELECT toUInt8(number) FROM system.numbers LIMIT 9000000 FORMAT RowBinary'
```

Tip

Use buffering to avoid situations where a query processing error occurred after the response code and HTTP headers were sent to the client. In this situation, an error message is written at the end of the response body, and on the client-side, the error can only be detected at the parsing stage.

## Setting a role with query parameters[​](#setting-role-with-query-parameters "Direct link to Setting a role with query parameters")

This feature was added in ClickHouse 24.4.

In specific scenarios, setting the granted role first might be required before executing the statement itself.
However, it isn't possible to send `SET ROLE` and the statement together, as multi-statements aren't allowed:

```
curl -sS "http://localhost:8123" --data-binary "SET ROLE my_role;SELECT * FROM my_table;"
```

The command above results in an error:

```
Code: 62. DB::Exception: Syntax error (Multi-statements are not allowed)
```

To overcome this limitation, use the `role` query parameter instead:

```
curl -sS "http://localhost:8123?role=my_role" --data-binary "SELECT * FROM my_table;"
```

This is the equivalent of executing `SET ROLE my_role` before the statement.

Additionally, it is possible to specify multiple `role` query parameters:

```
curl -sS "http://localhost:8123?role=my_role&role=my_other_role" --data-binary "SELECT * FROM my_table;"
```

In this case, `?role=my_role&role=my_other_role` works similarly to executing `SET ROLE my_role, my_other_role` before the statement.

## HTTP response codes caveats[​](#http_response_codes_caveats "Direct link to HTTP response codes caveats")

Because of limitations of the HTTP protocol, a HTTP 200 response code doesn't guarantee that a query was successful.

Here is an example:

```
curl -v -Ss "http://localhost:8123/?max_block_size=1&query=select+sleepEachRow(0.001),throwIf(number=2)from+numbers(5)"
*   Trying 127.0.0.1:8123...
...
< HTTP/1.1 200 OK
...
Code: 395. DB::Exception: Value passed to 'throwIf' function is non-zero: while executing 'FUNCTION throwIf(equals(number, 2) :: 1) -> throwIf(equals(number, 2))
```

The reason for this behavior is the nature of the HTTP protocol. The HTTP header is sent first with an HTTP code of 200, followed by the HTTP body, and then the error is injected into the body as plain text.

This behavior is independent of the format used, whether it's `Native`, `TSV`, or `JSON`; the error message will always be in the middle of the response stream.

You can mitigate this problem by enabling `wait_end_of_query=1` ([Response Buffering](#response-buffering)). In this case, sending of the HTTP header is delayed until the entire query is resolved. This however, doesn't completely solve the problem because the result must still fit within the [`http_response_buffer_size`](/docs/operations/settings/settings#http_response_buffer_size), and other settings like [`send_progress_in_http_headers`](/docs/operations/settings/settings#send_progress_in_http_headers) can interfere with the delay of the header.

Tip

The only way to catch all errors is to analyze the HTTP body before parsing it using the required format.

Such exceptions in ClickHouse have consistent exception format as below irrespective of which format used (eg. `Native`, `TSV`, `JSON`, etc) when `http_write_exception_in_output_format=0` (default) . Which makes it easy to parse and extract error messages on the client side.

```
\r\n
__exception__\r\n
<TAG>\r\n
<error message>\r\n
<message_length> <TAG>\r\n
__exception__\r\n
```

Where `<TAG>` is a 16 byte random tag, which is the same tag sent in the `X-ClickHouse-Exception-Tag` response header.
The `<error message>` is the actual exception message (exact length can be found in `<message_length>`). The whole exception block described above can be up to 16 KiB.

Here is an example in `JSON` format

```
$ curl -v -Ss "http://localhost:8123/?max_block_size=1&query=select+sleepEachRow(0.001),throwIf(number=2)from+numbers(5)+FORMAT+JSON"
...
{
    "meta":
    [
        {
            "name": "sleepEachRow(0.001)",
            "type": "UInt8"
        },
        {
            "name": "throwIf(equals(number, 2))",
            "type": "UInt8"
        }
    ],