# Broker Query API

Pinot exposes broker endpoints for single-stage execution, multi-stage execution, and query fingerprint generation. Cursor-based pagination is available through query parameters on the SQL endpoint, and the response-store lifecycle is managed through broker endpoints on the same broker that executed the query.

For the broker `POST` query endpoints on this page, malformed JSON request bodies and payloads that omit required fields now return HTTP `400 Bad Request` instead of HTTP `500`.

## Endpoints

| Method   | Endpoint                             | Purpose                                                           |
| -------- | ------------------------------------ | ----------------------------------------------------------------- |
| `POST`   | `/query/sql`                         | Submit SQL to the broker query endpoint                           |
| `POST`   | `/query`                             | Submit SQL through the multi-stage endpoint                       |
| `POST`   | `/query/sql/queryFingerprint`        | Generate a normalized fingerprint and stable hash for a DQL query |
| `POST`   | `/query/sql?getCursor=true`          | Submit a query and return a cursor-backed first page              |
| `GET`    | `/responseStore/{requestId}/results` | Fetch additional cursor pages                                     |
| `GET`    | `/responseStore/{requestId}`         | Fetch cursor metadata                                             |
| `GET`    | `/responseStore`                     | List active cursor stores                                         |
| `DELETE` | `/responseStore/{requestId}`         | Delete a cursor response store                                    |

## Query Submission

```bash
curl -H "Content-Type: application/json" -X POST \
  -d '{"sql":"select foo, count(*) from myTable group by foo limit 100"}' \
  http://localhost:8099/query/sql
```

Use `/query` when the statement requires multi-stage features such as joins or window functions:

```bash
curl -H "Content-Type: application/json" -X POST \
  -d '{"sql":"select count(*) from a JOIN b ON a.x = b.x"}' \
  http://localhost:8099/query
```

Both `POST /query/sql` and `POST /query` require a JSON request body with a top-level `sql` field. If the request body is malformed JSON or the `sql` field is missing, the broker returns HTTP `400 Bad Request`.

## Query Fingerprints

Use `POST /query/sql/queryFingerprint` to generate a normalized fingerprint for a DQL query without executing it. Pinot returns a small JSON object with:

* `queryHash`: a stable hash of the normalized fingerprint
* `fingerprint`: the normalized SQL shape

The request body must include `sql`:

```bash
curl -H "Content-Type: application/json" -X POST \
  -d '{"sql":"SELECT * FROM myTable WHERE id IN (1, 2, 3)"}' \
  http://localhost:8099/query/sql/queryFingerprint
```

Pinot normalizes literals into placeholders, so the returned fingerprint for the example above is:

```
SELECT * FROM `myTable` WHERE `id` IN (?)
```

The same endpoint also accepts multi-stage queries. For example, a query with `SET useMultistageEngine=true;` still returns a normalized fingerprint instead of executing the statement.

This endpoint is DQL-only. Malformed JSON, a missing `sql` field, invalid SQL, or a non-DQL statement all return HTTP `400 Bad Request`. Unlike the broker config `pinot.broker.enable.query.fingerprinting`, this helper endpoint can generate fingerprints on demand without enabling automatic fingerprinting for normal query execution.

## Cursor Pagination

Cursor-backed queries return the first page together with metadata that the client must reuse for later fetches. The most important fields are `requestId`, `brokerHost`, `brokerPort`, `offset`, `numRows`, `numRowsResultSet`, and `expirationTimeMs`.

```bash
curl --request POST http://localhost:8099/query/sql?getCursor=true&numRows=1 \
  --data '{"sql":"SELECT * FROM nation limit 100"}' | jq
```

If `numRows` is omitted or set to `0`, Pinot uses `pinot.broker.cursor.fetch.rows` (default `10000`). Fetch the next page with:

```bash
curl -X GET http://localhost:8099/responseStore/236490978000000006/results?offset=1&numRows=1 | jq
```

Read cursor metadata without returning the row slice:

```bash
curl -X GET http://localhost:8099/responseStore/236490978000000006 | jq
```

## Operational Notes

* Cursors are broker-affine; follow-up requests must go back to the same broker.
* Cursor results expire according to `pinot.broker.cursor.response.store.expiration` and are eventually cleaned up by the controller.
* `GET /responseStore` and `DELETE /responseStore/{requestId}` are operator-oriented response-store endpoints, not the normal client pagination flow.

## What this page covered

* The broker query endpoints and their intended use.
* Cursor-based pagination and response-store lifecycle basics.
* The main operational constraint: follow-up requests must hit the same broker.

## Next step

If you need SQL semantics rather than transport semantics, jump to the SQL syntax page; if you need endpoint details beyond query submission, move to the controller or gRPC reference.

## Related pages

* [API Reference](/reference/api-reference.md)
* [Query Response Format](/reference/api-reference/query-response-format.md)
* [Controller Admin API](/reference/api-reference/controller-admin-api.md)
* [Broker gRPC API](/reference/api-reference/broker-grpc-api.md)
* [Querying Pinot](/build-with-pinot/querying-and-sql/querying-pinot.md)
* [Query using Cursors](/build-with-pinot/querying-and-sql/query-execution-controls/query-using-cursors.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.pinot.apache.org/reference/api-reference/query-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
