SaturnSQL API

24 endpoints across 7 groups, served from https://saturnsql.com/api.

HTTP API for the SaturnSQL database query platform. SaturnSQL connects to your PostgreSQL, MySQL, MSSQL, Amazon Redshift, ClickHouse, DynamoDB and BigQuery databases and lets people — and AI agents — explore their schema and run queries through one authenticated surface, without ever handling raw database credentials.

Authentication

Two bearer credentials are accepted on the Authorization header:

  • API key (recommended for scripts, integrations, and AI agents) — a long-lived, scoped, revocable key in the sat_live_... format, created in Settings → Profile → API keys.
  • JWT session token — obtained from POST /auth/login; expires after 24 hours and requires the TOTP step when MFA is enrolled. This is what the web app uses.
Authorization: Bearer sat_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

API key scopes

A key carries one or more scopes. Everything not in the list below fails closed — a key can never manage other keys, users, billing, or connections.

ScopeGrants
readGET /connections, GET /schema/{connectionId}, GET /schema/context, GET /queries, GET /queries/{id}, GET /queries/history
executePOST /queries/execute (run SQL), POST /queries/{id}/run (run a saved query from values)
writePOST /queries, PUT /queries (save / update saved queries)

Give an agent the narrowest scope it needs: read alone to explore schema and saved queries, add execute only when it must run SQL, add write only when it should persist queries. Name the key after the agent and set an expiry.

Building queries and insights with an AI agent

The intended agent loop is discover → understand → query → (optionally) save. Every call is scoped to the company the key belongs to, and only connections the key's owner can see are reachable.

1. Discover connections

List the databases available to the key and pick a connectionId:

curl -s https://saturnsql.com/api/connections \
  -H 'Authorization: Bearer sat_live_...'

Each connection reports its id, name, database type and whether it is read-only.

2. Understand the schema

Pull the tables, columns and types so the agent writes SQL against the real structure — never guess table or column names:

# Full schema for a connection
curl -s 'https://saturnsql.com/api/schema/CONNECTION_ID' \
  -H 'Authorization: Bearer sat_live_...'

# Relevance-ranked subset for a goal (best for large databases)
curl -s 'https://saturnsql.com/api/schema/context?connection_id=CONNECTION_ID&intent=monthly%20revenue%20by%20plan' \
  -H 'Authorization: Bearer sat_live_...'

GET /schema/context accepts connection_id (required), intent (a natural-language description of what you're after), tables (comma-separated allowlist) and current_sql, and returns only the slice of schema relevant to that intent. Feed it straight into your SQL-generation prompt to keep context small and accurate.

3. Run read-only queries

Execute generated SQL and get rows back (execute scope required):

curl -s https://saturnsql.com/api/queries/execute \
  -H 'Authorization: Bearer sat_live_...' \
  -H 'Content-Type: application/json' \
  -d '{"connectionId":"CONNECTION_ID","sql":"SELECT plan, count(*) AS n FROM companies GROUP BY plan ORDER BY n DESC","limit":100}'

limit defaults to 1000 and caps at 10000. The response returns the result rows plus column metadata.

Read-only enforcement. Execution is guarded so an agent cannot mutate data: only SELECT, WITH (CTEs) and EXPLAIN are accepted, one statement at a time, and write / DDL / admin verbs (INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, TRUNCATE, GRANT, EXEC, SELECT … INTO, …) are rejected. Connections flagged read-only are additionally enforced at the driver level. Design agent prompts around exploration and reporting, not mutation.

Insight-query patterns

Once the schema is known, these read-only patterns surface useful insights on almost any database — adapt table and column names from step 2:

  • Shape & volumeSELECT count(*) FROM <table>; row counts per table to find the big / active ones.
  • DistributionsSELECT <dim>, count(*) n FROM <table> GROUP BY <dim> ORDER BY n DESC LIMIT 20 for top categories, statuses, plans.
  • Trends over timeSELECT date_trunc('month', <ts>) m, count(*) FROM <table> GROUP BY 1 ORDER BY 1 for growth and seasonality.
  • Data-quality audit — null / blank counts per column, duplicate keys, orphaned foreign keys.
  • Cohorts & funnels — first-seen vs last-seen per entity, step-to-step conversion with WITH CTEs.

Run EXPLAIN first on unfamiliar or large tables, and keep a LIMIT on ad-hoc SELECTs.

4. Save the good ones (optional, write scope)

Persist a query the user will want again, optionally filed into a folder with folderPath (null = root):

curl -s https://saturnsql.com/api/queries \
  -H 'Authorization: Bearer sat_live_...' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Signups by month","sql":"SELECT ...","connectionId":"CONNECTION_ID","folderPath":"Growth"}'

Browse queries the team already trusts with GET /queries, and recent runs with GET /queries/history, to reuse rather than reinvent. (Sharing a query company-wide is a plan-gated action available in the web app, not over an API key.)

5. A saved query is also a report page

Every saved query is readable at https://saturnsql.com/app/report/{id} — the result as a table, with a chart above it when the shape allows one. With no chart pinned (the only case an API key can produce), the shape decides:

  • 1 date/timestamp column + 1–3 numeric columns → line / area
  • no date column + exactly 2 numeric columns → scatter
  • no date column + 1 text column (2–12 values) + 1 numeric → bar
  • anything else, including a 4th numeric column → table only

So keep the numeric columns to three and the date columns to one; emit the rest as text. Rows plot in result order, so a time series needs ORDER BY <time> ASC.

Pinning a chart (PUT /queries/{id}/chart) is session-token, owner-only. Save the query, hand over the link, and leave that to the web app.

Limits & errors

  • Query quota — each company has a monthly execution quota; exceeding it (or hitting the rate limit) returns 429 with usage / limit details.
  • Errors — non-2xx responses return { "error": "..." }: 400 validation, 401 bad or missing credential, 403 insufficient scope, 404 unknown connection or query.

Guide: Using SaturnSQL with AI coding agents.

Endpoints

Authentication

Log in and refresh session tokens

POST/auth/login

Log in (get a session token)

Exchanges email + password for a 24h JWT session token and a refresh token. If MFA is enrolled, returns `{ mfaRequired: true, mfaToken }` instead — complete the login with POST /auth/mfa/login. For scripts and AI agents prefer a `sat_live_` API key over this flow.

Request body

NameTypeRequiredDescription
emailstringYes
passwordstringYes
Responses
  • 200Authenticated (or MFA challenge)
  • 400Validation failed
  • 401Bad or missing credentials
POST/auth/mfa/login

Complete MFA login

Completes a login challenged by MFA: submit the short-lived `mfaToken` from POST /auth/login plus the 6-digit TOTP `code` (or a backup code).

Request body

NameTypeRequiredDescription
mfaTokenstringYes
codestringYes
Responses
  • 200Authenticated
  • 400Validation failed
  • 401Bad or missing credentials
POST/auth/refresh

Refresh a session token

Exchanges a valid refresh token for a fresh 24h session token. Optionally switch the active company with `selectedCompanyId`.

Request body

NameTypeRequiredDescription
refreshTokenstringYes
selectedCompanyIdstringNo
Responses
  • 200New session token
  • 400Validation failed
  • 401Bad or missing credentials

API Keys

Create and manage scoped sat_live_ keys for scripts and agents

DELETE/api-keys/{id}

Revoke an API key

Revokes a key by id. Takes effect immediately; the key can no longer authenticate. Requires a session token.

Parameters

NameTypeRequiredDescription
idstringYes
Responses
  • 200Revoked
  • 401Bad or missing credentials
  • 404Key not found
GET/api-keys

List API keys

Lists the current company's active (non-revoked) API keys. Only the display prefix is shown — the secret is never returned after creation. Requires a session token.

Responses
  • 200Keys
  • 401Bad or missing credentials
POST/api-keys

Create an API key

Creates a scoped `sat_live_` key. The plaintext `key` is returned ONCE in the response — store it immediately, it cannot be retrieved again. Requires a session token.

Parameters

NameTypeRequiredDescription
namestringYesDisplay name for the key
expirystringYesOne of 24h, 30d, 1y, never
scopesstringYesSubset of ["read", "execute", "write"], defaults to ["read"]

Request body

NameTypeRequiredDescription
namestringYesDisplay name, e.g. the agent it is for
expirystringYes
scopesstring[]NoDefaults to ["read"]
Responses
  • 201Created (secret shown once)
  • 400Validation failed
  • 401Bad or missing credentials

Connections

Discover the databases you can query

GET/connections

List connections

Lists the database connections available to the caller (own + company-shared). Use an `id` as the `connectionId` for schema and query calls. Available to API keys with the `read` scope.

Responses
  • 200Connections
  • 401Bad or missing credentials

Schema

Inspect tables and columns before writing SQL

GET/schema/{connectionId}

Get full schema

Returns every table and column for a connection, so an agent can write SQL against the real structure. For large databases prefer GET /schema/context. Available to API keys with the `read` scope.

Parameters

NameTypeRequiredDescription
connectionIdstringYes
Responses
  • 200Tables
  • 401Bad or missing credentials
  • 404Connection not found
GET/schema/context

Get relevant schema for an intent

Returns only the slice of schema relevant to a stated goal — ideal for keeping an AI agent's context small on large databases. Feed the result straight into a SQL-generation prompt. Available to API keys with the `read` scope.

Parameters

NameTypeRequiredDescription
connection_idstringYesConnection to inspect
intentstringNoNatural-language description of what you want to query
tablesstringNoComma-separated allowlist of tables
current_sqlstringNoSQL already being drafted, used to rank relevance
Responses
  • 200Relevant schema
  • 400connection_id is required
  • 401Bad or missing credentials
  • 404Connection not found

Queries

Save, list, execute and review SQL

GET/queries/{id}

Get a saved query

Returns a single saved query by id. Available to API keys with the `read` scope.

Parameters

NameTypeRequiredDescription
idstringYes
Responses
  • 200Saved query
  • 401Bad or missing credentials
  • 404Query not found
POST/queries/{id}/run

Run a saved query

Runs a saved query, substituting its declared `{{variables}}` from the values supplied. The caller sends values, never SQL: the SQL comes from the stored query and every value is type-checked and escaped server side. A value that is missing or invalid returns `422` with one entry per bad variable and executes nothing. An absent name falls back to that variable's default; a name present with an empty string stays empty. Same read-only guard, rate limit and monthly quota as POST /queries/execute. Available to API keys with the `execute` scope.

Parameters

NameTypeRequiredDescription
idstringYes
valuesobjectYesVariable values by name. An absent name falls back to that variable's default; a name present with an empty string stays empty.
timeZonestringYesIANA time zone used to resolve relative date defaults (default: UTC)
limitnumberYesMaximum rows to return (default: 1000, max: 10000)
refreshbooleanYesSkip the result cache and read the database now. Omit to allow a cached result.

Request body

NameTypeRequiredDescription
valuesobjectNoVariable values by name
timeZonestringNoIANA zone used to resolve relative date defaults (default UTC)
limitintegerNoMax rows (default 1000)
Responses
  • 200Result rows
  • 400Invalid request, no connection on the query, or variables on a DynamoDB connection
  • 401Bad or missing credentials
  • 403Insufficient scope, paused connection, or non-read-only SQL
  • 404Query not found
  • 422One or more variable values are missing or invalid; nothing was executed
  • 429Query quota or rate limit exceeded
POST/queries/execute

Execute SQL

Runs SQL against a connection and returns rows plus column metadata. Read-only guarded: only SELECT, WITH (CTE) and EXPLAIN are accepted, one statement at a time; write/DDL/admin verbs (INSERT, UPDATE, DELETE, DROP, …) are rejected with `400`, the same status as any invalid or multi-statement SQL. `limit` defaults to 1000, caps at 10000. Available to API keys with the `execute` scope. Exceeding the monthly query quota or the rate limit returns `429`.

Parameters

NameTypeRequiredDescription
connectionIdstringYesDatabase connection ID
sqlstringYesRead-only SQL query to execute (SELECT / WITH / EXPLAIN). May contain {{variable}} placeholders, in which case `variables` must define every one of them.
limitnumberYesMaximum rows to return (default: 1000, max: 10000)
variablesobjectYesDefinitions for the {{placeholders}} in `sql`. Values are type-checked and escaped against these server side.
valuesobjectYesVariable values by name. An absent name falls back to that variable's default; a name present with an empty string stays empty.
timeZonestringYesIANA time zone used to resolve relative date defaults (default: UTC)

Request body

NameTypeRequiredDescription
connectionIdstringYes
sqlstringYesRead-only SQL: SELECT / WITH / EXPLAIN
limitintegerNoMax rows (default 1000)
Responses
  • 200Result rows
  • 400Invalid SQL or non-read-only statement
  • 401Bad or missing credentials
  • 403Insufficient scope
  • 429Query quota or rate limit exceeded
GET/queries/history

Query run history

Returns the caller's recent query executions (SQL, connection, timing, status). Available to API keys with the `read` scope.

Responses
  • 200History
  • 401Bad or missing credentials
GET/queries

List saved queries

Returns the saved queries visible to the caller (own + company-shared). Available to API keys with the `read` scope.

Responses
  • 200Saved queries
  • 401Bad or missing credentials
POST/queries

Save a query

Saves a query for reuse, optionally filed into a folder with `folderPath` (null = root). Available to API keys with the `write` scope. Sharing a query company-wide is plan-gated and done in the web app. The fields below are the whole create surface: `description` and `chart` are ignored if sent. The saved query is immediately readable at `/app/report/{id}`, where the result shape decides the chart.

Request body

NameTypeRequiredDescription
namestringYes
sqlstringYes
connectionIdstringNoOptional; the connection this query targets
folderPathstringNoA single flat folder label, not a nested path ("A/B" is one label); null = root
variablesQueryVariable[]NoDeclarations for the `{{name}}` placeholders in `sql`. Omit or send [] when there are none.
Responses
  • 201Saved
  • 400Validation failed
  • 401Bad or missing credentials
  • 403Saved-query limit reached (plan)
PUT/queries

Update a query

Updates a saved query. Only the fields you send change; `sql` and `folderPath` are optional. Available to API keys with the `write` scope.

Request body

NameTypeRequiredDescription
queryIdstringYes
namestringYes
sqlstringNo
folderPathstringNoA single flat folder label, not a nested path ("A/B" is one label); null moves to root; omit to leave unchanged
variablesQueryVariable[]NoReplaces the stored declarations. Omit to leave them unchanged; send [] to clear them.
Responses
  • 200Updated
  • 400Validation failed
  • 401Bad or missing credentials
  • 403Insufficient scope or plan restriction
  • 404Resource not found

Reports

Present a saved query as a form its audience fills in, without ever showing them the SQL

PUT/queries/{id}/chart

Set a report’s chart, or clear it

Stores a ChartSpec on a saved query, or clears it with `null` to go back to deciding from the result shape. The spec is stored verbatim and not validated against columns, because the columns only exist after a run; a spec that stops fitting falls back to the guess at render time rather than erroring. Owner only, and its own route rather than a field on PUT /queries so that choosing a chart cannot collide with that endpoint’s optimistic-concurrency baseline. Session token only; API keys cannot reach this.

Parameters

NameTypeRequiredDescription
idstringYes

Request body

NameTypeRequiredDescription
chartYesThe spec to store, or null to go back to deciding from the shape
Responses
  • 200The stored configuration
  • 400Chart must be an object or null
  • 401Bad or missing credentials
  • 403Editor access required
  • 404Query not found, or the caller does not own it
GET/queries/{id}/report

Get a report’s metadata

Everything needed to present a saved query as a form: title, description, which connection and database the numbers come from, the variables to fill in, and the chart configuration. Never returns the SQL, so this is the endpoint to use when a caller should be able to run a query without reading it. Scoped to the caller’s company and to their own or shared queries; anything else is a 404 rather than a 403, so the endpoint cannot be used to discover query ids. Available to API keys with the `read` scope.

Parameters

NameTypeRequiredDescription
idstringYes
Responses
  • 200Report metadata
  • 401Bad or missing credentials
  • 403Insufficient scope
  • 404Query not found, or not shared with the caller

Schedules

Schedule saved queries to export to Google Sheets or Slack

GET/schedules/{id}

Get a schedule

Returns a single schedule by id. Requires a session token.

Parameters

NameTypeRequiredDescription
idstringYes
Responses
  • 200Schedule
  • 401Bad or missing credentials
  • 404Schedule not found
PUT/schedules/{id}

Update a schedule

Updates cadence, destination, or active state of a schedule. Send only the fields you want to change. Requires a session token.

Parameters

NameTypeRequiredDescription
idstringYes

Request body

NameTypeRequiredDescription
frequencystringNo
hourintegerNo
minuteintegerNo
isActivebooleanNo
Responses
  • 200Updated
  • 400Validation failed
  • 401Bad or missing credentials
  • 403Insufficient scope or plan restriction
  • 404Resource not found
DELETE/schedules/{id}

Delete a schedule

Deletes a schedule by id. Requires a session token.

Parameters

NameTypeRequiredDescription
idstringYes
Responses
  • 200Deleted
  • 401Bad or missing credentials
  • 404Schedule not found
POST/schedules/{id}/run

Run a schedule now

Triggers an immediate run of a schedule (runs the saved query and exports to its destination), independent of its cadence. Takes no request body. Requires a session token.

Parameters

NameTypeRequiredDescription
idstringYes
Responses
  • 200Ran
  • 401Bad or missing credentials
  • 404Schedule not found
  • 500Export failed
GET/schedules

List schedules

Lists scheduled exports of saved queries (to Google Sheets or Slack). Requires a session token.

Responses
  • 200Schedules
  • 401Bad or missing credentials
POST/schedules

Create a schedule

Schedules a saved query to run on a cadence and export its result to ONE destination — Google Sheets (spreadsheetId/spreadsheetName + sheetName + startCell) OR Slack (slackChannelId). Scheduling is plan-gated. Requires a session token.

Request body

NameTypeRequiredDescription
savedQueryIdstringYes
frequencystringYes
hourintegerYes
minuteintegerNoDefault 0
dayOfWeekintegerNoFor weekly (0=Sun)
dayOfMonthintegerNoFor monthly
spreadsheetIdstringNoGoogle Sheets destination (existing sheet)
spreadsheetNamestringNoOr create a new spreadsheet by name
sheetNamestringNo
startCellstringNoA1 notation, default A1
includeHeadersbooleanNoDefault true
slackChannelIdstringNoSlack destination (instead of Sheets)
slackMessageTemplatestringNo
Responses
  • 201Created
  • 400Validation failed / more than one destination
  • 401Bad or missing credentials
  • 403Scheduling not available on plan
SaturnSQL - Featured on Startup Fame

© 2026 Panda Capital Oy Ab. All rights reserved.