Saved Queries
Save, version, and execute named SQL queries. Each update creates a new version, preserving the full history. Saved queries are automatically classified by category (e.g., aggregation, join, filtered scan) and can be executed by ID.
List saved queries
/v1/queriesQuery parameters
limitinteger— Maximum number of resultsoffsetinteger— Pagination offset
Response 200 — List of saved queries
countinteger— required. Min:0has_moreboolean— requiredlimitinteger— required. Min:0offsetinteger— required. Min:0queriesSavedQuerySummary[] — requiredcreated_atstring— requireddescriptionstring— requiredidstring— requiredlatest_versioninteger— requirednamestring— requiredtagsstring[] — requiredupdated_atstring— required
{
"count": 0,
"has_more": true,
"limit": 0,
"offset": 0,
"queries": [
{
"created_at": "2026-01-01T00:00:00Z",
"description": "string",
"id": "string",
"latest_version": 0,
"name": "string",
"tags": [
"string"
],
"updated_at": "2026-01-01T00:00:00Z"
}
]
}
Create saved query
/v1/queriesSave a named SQL query. The SQL is stored as version 1 and automatically analyzed for classification metadata (category, table count, predicate/join/aggregation flags).
Request body
descriptionstring,nullnamestring— requiredsqlstring— requiredtagsstring[] |null
{
"description": "Ten highest-spending customers by order total",
"name": "top-customers",
"sql": "SELECT customer_id, sum(amount) AS total FROM orders GROUP BY customer_id ORDER BY total DESC LIMIT 10",
"tags": [
"sales",
"weekly"
]
}
Response 201 — Saved query created
categorystring,nullcreated_atstring— requireddescriptionstring— requiredhas_aggregationboolean,nullhas_group_byboolean,nullhas_joinboolean,nullhas_limitboolean,nullhas_order_byboolean,nullhas_predicateboolean,nullidstring— requiredlatest_versioninteger— requirednamestring— requirednum_tablesinteger,nullsqlstring— requiredsql_hashstring— requiredtable_sizestring,nulltagsstring[] — requiredupdated_atstring— required
{
"category": "string",
"created_at": "2026-01-01T00:00:00Z",
"description": "string",
"has_aggregation": true,
"has_group_by": true,
"has_join": true,
"has_limit": true,
"has_order_by": true,
"has_predicate": true,
"id": "string",
"latest_version": 0,
"name": "string",
"num_tables": 0,
"sql": "string",
"sql_hash": "string",
"table_size": "string",
"tags": [
"string"
],
"updated_at": "2026-01-01T00:00:00Z"
}
Errors
| Status | Description |
|---|---|
400 | Invalid request |
Get saved query
/v1/queries/{id}Path parameters
idstring— required. Saved query ID
Response 200 — Saved query details
categorystring,nullcreated_atstring— requireddescriptionstring— requiredhas_aggregationboolean,nullhas_group_byboolean,nullhas_joinboolean,nullhas_limitboolean,nullhas_order_byboolean,nullhas_predicateboolean,nullidstring— requiredlatest_versioninteger— requirednamestring— requirednum_tablesinteger,nullsqlstring— requiredsql_hashstring— requiredtable_sizestring,nulltagsstring[] — requiredupdated_atstring— required
{
"category": "string",
"created_at": "2026-01-01T00:00:00Z",
"description": "string",
"has_aggregation": true,
"has_group_by": true,
"has_join": true,
"has_limit": true,
"has_order_by": true,
"has_predicate": true,
"id": "string",
"latest_version": 0,
"name": "string",
"num_tables": 0,
"sql": "string",
"sql_hash": "string",
"table_size": "string",
"tags": [
"string"
],
"updated_at": "2026-01-01T00:00:00Z"
}
Errors
| Status | Description |
|---|---|
404 | Saved query not found |
Update saved query
/v1/queries/{id}Update a saved query. If the SQL changes, a new version is created (previous versions are preserved). Name, tags, description, and classification overrides can also be updated.
Path parameters
idstring— required. Saved query ID
Request body
category_overridestring,null— Override the auto-detected category. Sendnullto clear (revert to auto).descriptionstring,nullnamestring,null— Optional new name. When omitted the existing name is preserved.sqlstring,null— Optional new SQL. When omitted the existing SQL is preserved.table_size_overridestring,null— User annotation for table size. Sendnullto clear.tagsstring[] |null
All fields are optional. Send only the ones you want to set.
Response 200 — Saved query updated
categorystring,nullcreated_atstring— requireddescriptionstring— requiredhas_aggregationboolean,nullhas_group_byboolean,nullhas_joinboolean,nullhas_limitboolean,nullhas_order_byboolean,nullhas_predicateboolean,nullidstring— requiredlatest_versioninteger— requirednamestring— requirednum_tablesinteger,nullsqlstring— requiredsql_hashstring— requiredtable_sizestring,nulltagsstring[] — requiredupdated_atstring— required
{
"category": "string",
"created_at": "2026-01-01T00:00:00Z",
"description": "string",
"has_aggregation": true,
"has_group_by": true,
"has_join": true,
"has_limit": true,
"has_order_by": true,
"has_predicate": true,
"id": "string",
"latest_version": 0,
"name": "string",
"num_tables": 0,
"sql": "string",
"sql_hash": "string",
"table_size": "string",
"tags": [
"string"
],
"updated_at": "2026-01-01T00:00:00Z"
}
Errors
| Status | Description |
|---|---|
400 | Invalid request |
404 | Saved query not found |
Delete saved query
/v1/queries/{id}Path parameters
idstring— required. Saved query ID
Response 204 — Saved query deleted
Errors
| Status | Description |
|---|---|
404 | Saved query not found |
Execute saved query
/v1/queries/{id}/executeExecute a saved query, scoped to a database (required X-Database-Id header). By default runs the latest version. Optionally specify a version number to execute a previous version. The SQL runs inside the given database scope, the same way POST /v1/query does. Returns the same response format as POST /v1/query.
Path parameters
idstring— required. Saved query ID
Headers
X-Database-Idstring— required. Required. Scope execution to this database (its id). A missing or malformed value is a 400; an unknown database id is a 404.
Request body
versioninteger,null
All fields are optional. Send only the ones you want to set.
Response 200 — Query executed
columnsstring[] — requiredexecution_time_msinteger— required. Min:0nullableboolean[] — required. Nullable flags for each column (parallel to columns vec). True if the column allows NULL values, false if NOT NULL.preview_row_countinteger— required. Number of rows in this response body. Always present. For a large result this is a bounded preview, not the grand total — seetotal_row_countandtruncated.query_run_idstring— required. Unique identifier for the query run record (qrun...).result_idstring,null— Unique identifier for retrieving this result via GET /results/{id}. When non-null, the result is being persisted asynchronously. Null only when the result fit entirely in this response (truncated: false) but could not be persisted for later retrieval — see thewarningfield. Atruncated: trueresponse ALWAYS carries a non-null, resolvableresult_id: a truncated result that cannot be persisted fails the request with a retryable HTTP 503 (PERSISTENCE_UNAVAILABLE, with aRetry-Afterheader) rather than returning a partial body with a dead ticket.row_countinteger— required. Deprecated — usepreview_row_count(rows in this body) andtotal_row_count(grand total) instead. Retained as a back-compat alias and always equal topreview_row_count; for a truncated result it is the preview count, not the grand total — readtotal_row_countfor that. Will be removed in a future release once clients migrate. Min:0rowsany[][] — required. Array of rows, where each row is an array of column values. Values can be strings, numbers, booleans, or null.total_row_countinteger,null— Grand total rows in the full result. Present (and equal topreview_row_count) when the whole result fit in this response;nullwhile a truncated result is still being persisted. Whennull, read the authoritative total fromGET /v1/query-runs/{id}(row_count) or theX-Total-Row-Countheader onGET /v1/results/{id}.truncatedboolean— required. True whenrowsis a bounded preview of a larger result. Fetch the full result viaresult_id.warningstring,null— Warning message if result persistence could not be initiated. Present only when the full result is returned inline (truncated: false) but could not be persisted:result_idis then null and the result cannot be re-fetched later, though every row is in this response. A truncated result never carries a warning — if it cannot be persisted the request fails with a retryable HTTP 503 (PERSISTENCE_UNAVAILABLE, with aRetry-Afterheader) instead.
{
"columns": [
"string"
],
"execution_time_ms": 0,
"nullable": [
true
],
"preview_row_count": 0,
"query_run_id": "string",
"result_id": "string",
"row_count": 0,
"rows": [
[
null
]
],
"total_row_count": 0,
"truncated": true,
"warning": "string"
}
Errors
| Status | Description |
|---|---|
400 | Invalid request (including a missing X-Database-Id header) |
404 | Saved query or database not found |
List saved query versions
/v1/queries/{id}/versionsPath parameters
idstring— required. Saved query ID
Query parameters
limitinteger— Maximum number of versionsoffsetinteger— Pagination offset
Response 200 — List of versions
countinteger— required. Min:0has_moreboolean— requiredlimitinteger— required. Min:0offsetinteger— required. Min:0saved_query_idstring— requiredversionsSavedQueryVersionInfo[] — requiredcategorystring,nullcreated_atstring— requiredhas_aggregationboolean,nullhas_group_byboolean,nullhas_joinboolean,nullhas_limitboolean,nullhas_order_byboolean,nullhas_predicateboolean,nullnum_tablesinteger,nullsqlstring— requiredsql_hashstring— requiredtable_sizestring,nullversioninteger— required
{
"count": 0,
"has_more": true,
"limit": 0,
"offset": 0,
"saved_query_id": "string",
"versions": [
{
"category": "string",
"created_at": "2026-01-01T00:00:00Z",
"has_aggregation": true,
"has_group_by": true,
"has_join": true,
"has_limit": true,
"has_order_by": true,
"has_predicate": true,
"num_tables": 0,
"sql": "string",
"sql_hash": "string",
"table_size": "string",
"version": 0
}
]
}
Errors
| Status | Description |
|---|---|
404 | Saved query not found |