# Pinned queries > Pin a SELECT so its result is always ready in the result cache - computed when the database opens, never pushed out, computed again after a write. - Category: HTTP API - Keywords: pin, pinned query, pinned statement, pins, keep a result ready, warm cache, preload, dashboard slow query, GET /api/pins, POST /api/pins, DELETE /api/pins, \_pinned\_queries, cache\_pinned - See also: [Performance tips (result-cache)](performance-tips.md#result-cache), [Run SQL over HTTP (/exec, /exp)](http-exec.md), [Timers](timers.md), [Indexes (warm)](indexes.md#warm), [Monitor panel (memory, processor, caches)](console-monitor.md) A **pinned query** is a `SELECT` the server keeps ready. Pin the slow question a dashboard asks on every load, and from then on every request for it is answered from the [result cache](performance-tips.md#result-cache): - It is computed **when the database opens**, right after the indexes are [built at open](indexes.md#warm), before anyone asks. - It is **never pushed out** of the result cache by other results, and it may take up to **three quarters** of the cache (an ordinary result is kept only up to a quarter). - After a **write** to a table it reads, the server computes it again by itself once the database has been quiet for half a second, so the next request finds it fresh. On a database that is written all the time and is never quiet, it does so at the latest two seconds after the first write that made the result old. A request that comes before that computes it, as any query would. - Pins are saved in the table `_pinned_queries` of the hidden `_system` database (like [timers](timers.md)), or of the served database when `kdb serve` runs on one folder, so they survive restarts and are part of every backup. The rules of the cache still apply: the statement must be one cacheable `SELECT` of this database (no `now()`, no random functions, no other database), and the **row limit is part of the key**: pin it with the `max_rows` the requests use (`/exec` sends 10,000 unless `limit=` says otherwise; the console sends 10,000). A pin is saved code that later runs by itself, without a rights check: only a server **admin** can pin or unpin, and every change is written to the [audit log](audit-log.md) (event `pin`). Pins are a feature of `kdb serve`. A server started without them answers `/api/pins` with 404. ## GET /api/pins Syntax: ```sql GET /api/pins[?db=] ``` The pins of one database (the default database without `db=`). Needs read rights on the database. | Field | Meaning | |---|---| | `database`, `sql`, `max_rows`, `enabled` | the pin as saved | | `created_ms`, `created_by` | when it was saved and by whom; empty = the token | | `open` | the database is open now; the fields below are live only then | | `cached`, `cached_bytes` | its result is in the cache right now, and its size | | `last_run_ms`, `last_ok`, `last_error` | the last time the server computed it by itself (at open, or after a write); `last_ok` is `null` before the first | | `error` | why the database refused the pin when it was applied (a column that no longer exists, for example); empty when it took it | `now_ms` at the top is the server's clock. `GET /api/stats` shows `cache_pinned` per database: how many pinned results are in the cache now. ## POST /api/pins Syntax: ```sql POST /api/pins[?db=] Content-Type: application/json {"sql": "SELECT ...", "max_rows": 10000, "enabled": true} ``` Pins a statement, or replaces the pin with the same SQL. Returns the pin. | Field | Meaning | |---|---| | `sql` | one `SELECT`, at most 64 KB | | `max_rows` | optional, 1 to 1,000,000 (default 10,000): the row limit the requests use | | `enabled` | optional (default `true`); `false` keeps the pin saved without applying it | A statement that is not one `SELECT`, does not parse, or reads another database is a 400 that says so. So is a `max_rows` out of range. **HTTP: Pin the totals a dashboard shows on every load** Setup: ```text CREATE TABLE trades (id LONG PRIMARY KEY, symbol SYMBOL, qty LONG, price DOUBLE); INSERT INTO trades VALUES (1, 'CITY', 100, 510.5), (2, 'CITY', 50, 512), (3, 'VOLT', 200, 301); ``` ```http POST /api/pins Content-Type: application/json {"sql": "SELECT symbol, sum(qty) AS qty, sum(qty * price) AS amount FROM trades GROUP BY symbol ORDER BY symbol"} ### GET /api/pins ``` Response: `POST /api/pins` HTTP 200 (application/json) ```text { "database": "demo", "sql": "SELECT symbol, sum(qty) AS qty, sum(qty * price) AS amount FROM trades GROUP BY symbol ORDER BY symbol", "max_rows": 10000, "enabled": true, "created_ms": 1792647859769, "created_by": "", "open": true, "cached": false, "cached_bytes": 0, "last_run_ms": 1792647859769, "last_ok": true, "last_error": "", "error": "" } ``` `GET /api/pins` HTTP 200 (application/json) ```text { "now_ms": 1792647859811, "pins": [ { "database": "demo", "sql": "SELECT symbol, sum(qty) AS qty, sum(qty * price) AS amount FROM trades GROUP BY symbol ORDER BY symbol", "max_rows": 10000, "enabled": true, "created_ms": 1792647859769, "created_by": "", "open": true, "cached": false, "cached_bytes": 0, "last_run_ms": 1792647859769, "last_ok": true, "last_error": "", "error": "" } ] } ``` **HTTP: Only one SELECT can be pinned** ```http POST /api/pins Content-Type: application/json {"sql": "DELETE FROM trades"} ``` Response: HTTP 400 (application/json) ```text {"error": "a pinned statement is one SELECT"} ``` ## DELETE /api/pins Syntax: ```sql DELETE /api/pins[?db=] Content-Type: application/json {"sql": "SELECT ..."} ``` Removes the pin with that SQL (also `?sql=`). Returns `{"deleted": true}`. Its cached result stays until the cache needs the room, like any other. An unknown pin is a 404; a request without the SQL is a 400. **HTTP: Unpin it** Setup: ```text CREATE TABLE trades (id LONG PRIMARY KEY, symbol SYMBOL, qty LONG); ``` ```http POST /api/pins Content-Type: application/json {"sql": "SELECT symbol, sum(qty) FROM trades GROUP BY symbol"} ### DELETE /api/pins Content-Type: application/json {"sql": "SELECT symbol, sum(qty) FROM trades GROUP BY symbol"} ``` Response: `POST /api/pins` HTTP 200 (application/json) ```text { "database": "demo", "sql": "SELECT symbol, sum(qty) FROM trades GROUP BY symbol", "max_rows": 10000, "enabled": true, "created_ms": 1792647859798, "created_by": "", "open": true, "cached": false, "cached_bytes": 0, "last_run_ms": 1792647859799, "last_ok": true, "last_error": "", "error": "" } ``` `DELETE /api/pins` HTTP 200 (application/json) ```text {"deleted": true} ```