At a glance
- First API call:
POST /query/sql?getCursor=true&numRows=Nruns the query and returns page 1 plus arequestId. - Subsequent calls:
GET /responseStore/{requestId}/results?offset=X&numRows=Nreturns any page you ask for. - Cleanup: stored results expire and are deleted automatically.
Overview
Without cursors, paging through a large result usually means sending the same query again and again with a differentLIMIT and OFFSET. Each of those calls makes Pinot do the full query work again. With cursors, the broker runs the query one time, saves the full result, and hands you pages of it on request.
Use cursor pagination when:
- A query returns more rows than you want to send to a client in one response.
- You are building a UI with “next page” or “load more” controls.
- You are exporting or processing a large result in batches.
- You want every page to come from the same snapshot of results.
How it works
The broker runs your query once, saves the whole result in its response store, and then serves pages from that saved copy. Servers are only involved in the first API call.- Your app sends the query with
getCursor=true. The broker runs it on the servers like any other query. - The broker saves the full result to its response store and gives it an ID (
requestId). - The broker returns the first page, the
requestId, and the total row count. - For each later page, your app asks the same broker for rows starting at an
offset. The broker reads them from the stored result. The query is not run again. - After the expiration time, the broker deletes the stored result automatically.
Prerequisites
Cursor pagination must be enabled on your environment’s brokers. Ask StarTree Support to enable it. Support can also tune the page size and how long stored results are kept. See Broker configuration for the settings involved.How expiration and the cleaner work together. A stored result expires after the
expiration time, and the cleaner deletes expired results on its next run. For example, with a 15-minute expiration and a cleaner that runs every 10 minutes, a result is removed between 15 and 25 minutes after the query. Ask for an expiration longer than your clients need to page through a result.Paging through results
A first API call opens the cursor, and subsequent calls fetch the remaining pages. In all examples, replacexxx.startree.cloud with your broker URL, and <YOUR_TOKEN>, <your_table> and <REQUEST_ID> with your own values.
The examples use a result of 17 rows read with a page size of 10. Page 1 returns rows 0 to 9 and page 2 returns the remaining 7.
Run the query and open a cursor
Send the query withPOST /query/sql, adding getCursor=true and your page size numRows as URL parameters. Put the SQL in the JSON body as usual.
resultTable, plus the cursor fields:
A first-page response with a page size of 3 looks like this (trimmed):
Fetch the next pages
CallGET /responseStore/{requestId}/results with the offset of the first row you want and the page size in numRows.
offset by your page size each time (0, 10, 20, …) until offset reaches numRowsResultSet. The last page may hold fewer rows than you asked for, and its numRows field shows the actual count.
Stop at the total
Anoffset equal to or larger than numRowsResultSet returns an error, not an empty page. With 17 stored rows, valid offsets are 0 to 16.
offset=17, because it equals the total, and the broker returns it as an HTTP 500 error. Stop paging once offset reaches numRowsResultSet. An offset inside the range with a large numRows is fine: it simply returns the rows that are left.
Clean up (optional)
Stored results are deleted automatically after they expire. To free the space right away when you are done, delete the cursor:Parameters
There are two kinds of settings: request parameters you send with each call, and broker configuration that sets the page size and storage behavior.Request parameters
Broker configuration
These are the cursor settings a broker reads. To change any of them, contact StarTree Support.
Durations accept the units
d, h, m and s, and combinations such as 1h30m.
Good to know
- Results expire. A stored result lives for the
expirationtime, and may stay up to one more cleaner cycle before it is deleted. After that, run the query again to get a new cursor. - Check broker disk size. If your cursor QPS is very high, check the broker disk size. Stored results stay on the broker’s disk until they expire, so a high volume of cursor queries could fill the disk.
- Pick a sensible page size. Each page fetch re-reads the stored result, so very small pages over a very large result are slower. Pages of hundreds or thousands of rows work better than pages of 10.
- The query still runs in full. Cursors page the delivery of results, not the query itself. The query’s
LIMITand the broker’s memory limits still apply, and only read (SELECT) queries can open a cursor. - Results are a snapshot. Every page comes from the result saved when the query ran, so new data ingested later does not appear until you run the query again.

