Saved Queries
Save any SQL query and it becomes an instant REST API endpoint with auth, caching, and parameters.
Quick Start
Create a saved query, then call it via curl or the CLI:
# Run a saved querycurl -X POST /api/v1/workspaces/{workspace_id}/saved-queries/{name}/run \ -H "Authorization: Bearer rq_your_api_key"
# With parameterscurl -X POST /api/v1/workspaces/{workspace_id}/saved-queries/{name}/run \ -H "Authorization: Bearer rq_your_api_key" \ -H "Content-Type: application/json" \ -d '{"parameters": {"min_revenue": 1000}}'# Run a saved querycurl -X POST /api/v1/workspaces/{workspace_id}/saved-queries/{name}/run \ -H "Authorization: Bearer rq_your_api_key"
# With parameterscurl -X POST /api/v1/workspaces/{workspace_id}/saved-queries/{name}/run \ -H "Authorization: Bearer rq_your_api_key" \ -H "Content-Type: application/json" \ -d '{"parameters": {"min_revenue": 1000}}'Creating a Saved Query
From the Dashboard
- Write your SQL in the Query Editor
- Click Save in the toolbar
- Give it a name, optional description, and cache TTL
- Parameters are auto-detected from
:param_nameplaceholders in the SQL
Via the API
curl -X POST /api/v1/workspaces/{workspace_id}/saved-queries \ -H "Authorization: Bearer rq_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "name": "top-customers", "sql": "SELECT name, SUM(amount) as revenue FROM my_stripe.charges GROUP BY name ORDER BY revenue DESC LIMIT 10", "description": "Top 10 customers by revenue", "cache_ttl_seconds": 300 }'curl -X POST /api/v1/workspaces/{workspace_id}/saved-queries \ -H "Authorization: Bearer rq_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "name": "top-customers", "sql": "SELECT name, SUM(amount) as revenue FROM my_stripe.charges GROUP BY name ORDER BY revenue DESC LIMIT 10", "description": "Top 10 customers by revenue", "cache_ttl_seconds": 300 }'Parameters
Saved queries support typed parameters. Define them when creating the query, then pass values at runtime.
{ "name": "revenue-by-country", "sql": "SELECT country, SUM(amount) as total FROM my_stripe.charges WHERE country = :country AND amount > :min_amount GROUP BY country", "parameters": [ { "name": "country", "type": "string", "required": true, "default": null }, { "name": "min_amount", "type": "integer", "required": false, "default": 0 } ]}{ "name": "revenue-by-country", "sql": "SELECT country, SUM(amount) as total FROM my_stripe.charges WHERE country = :country AND amount > :min_amount GROUP BY country", "parameters": [ { "name": "country", "type": "string", "required": true, "default": null }, { "name": "min_amount", "type": "integer", "required": false, "default": 0 } ]}| Type | Description |
|---|---|
| string | Text value |
| integer | Whole number |
| float | Decimal number |
| boolean | true / false |
Pass parameters when running:
curl -X POST /api/v1/workspaces/{workspace_id}/saved-queries/revenue-by-country/run \ -H "Authorization: Bearer rq_your_api_key" \ -H "Content-Type: application/json" \ -d '{"parameters": {"country": "FR", "min_amount": 5000}}'curl -X POST /api/v1/workspaces/{workspace_id}/saved-queries/revenue-by-country/run \ -H "Authorization: Bearer rq_your_api_key" \ -H "Content-Type: application/json" \ -d '{"parameters": {"country": "FR", "min_amount": 5000}}'Caching
Set cache_ttl_seconds to
cache results. Cached responses include "cached": true in
the response.
{ "columns": [{ "name": "revenue", "type": "DECIMAL" }], "rows": [[12580.50]], "row_count": 1, "execution_time_ms": 2, "cached": true, "cached_at": "2026-02-14T12:00:00+00:00"}{ "columns": [{ "name": "revenue", "type": "DECIMAL" }], "rows": [[12580.50]], "row_count": 1, "execution_time_ms": 2, "cached": true, "cached_at": "2026-02-14T12:00:00+00:00"}Cache is keyed on query name + parameter values. Different parameter combinations are cached separately.
API Reference
GET /api/v1/workspaces/:workspace_id/saved-queries
List all saved queries in a workspace.
[ { "id": "uuid", "name": "top-customers", "description": "Top 10 by revenue", "parameter_count": 0, "created_at": "2026-02-13T12:00:00Z" }][ { "id": "uuid", "name": "top-customers", "description": "Top 10 by revenue", "parameter_count": 0, "created_at": "2026-02-13T12:00:00Z" }]GET /api/v1/workspaces/:workspace_id/saved-queries/:name
Get a saved query by name, including its SQL and parameter definitions.
POST /api/v1/workspaces/:workspace_id/saved-queries
Create a new saved query.
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | URL-safe name (used in the endpoint path) |
| sql | string | Yes | SQL query (DuckDB syntax) |
| description | string | No | Human-readable description |
| parameters | array | No | Parameter definitions (name, type, required, default) |
| cache_ttl_seconds | integer | No | Cache lifetime in seconds (null = no cache) |
PATCH /api/v1/workspaces/:workspace_id/saved-queries/:name
Update a saved query. All fields optional - only provided fields are changed.
DELETE /api/v1/workspaces/:workspace_id/saved-queries/:name
Delete a saved query. Returns 204 on success.
POST /api/v1/workspaces/:workspace_id/saved-queries/:name/run
Execute a saved query and return results.
Request Body (optional)
{ "parameters": { "country": "FR", "min_amount": 5000 }}{ "parameters": { "country": "FR", "min_amount": 5000 }}Response
{ "columns": [ { "name": "country", "type": "VARCHAR" }, { "name": "total", "type": "DECIMAL" } ], "rows": [["FR", 48250.00]], "row_count": 1, "execution_time_ms": 42, "cached": false}{ "columns": [ { "name": "country", "type": "VARCHAR" }, { "name": "total", "type": "DECIMAL" } ], "rows": [["FR", 48250.00]], "row_count": 1, "execution_time_ms": 42, "cached": false}CLI
# List saved queriesrq queries
# Run a saved queryrq queries run top-customers
# Run with parametersrq queries run revenue-by-country --param country=FR --param min_amount=5000# List saved queriesrq queries
# Run a saved queryrq queries run top-customers
# Run with parametersrq queries run revenue-by-country --param country=FR --param min_amount=5000