---
title: "Execute SQL Query"
method: POST
path: "/sql/{sqlID}/execute"
tags: ["SQL Queries"]
---

# Execute SQL Query

`POST /sql/{sqlID}/execute`

## Request

- Base URL: `https://api.us-e1.tago.io`
- URL: `https://api.us-e1.tago.io/sql/{sqlID}/execute`
- Auth: API key in header `Profile-Token`
- TypeScript: <https://skmtc.dev/tago/apis/tagoio-api/docs/sql/:sqlID/execute/post.ts> (a function with typed inputs; its response is checked against the schema)

Run a saved query. Every body field is optional: a bare POST with an empty body runs the saved query with its stored defaults and the cache enabled. Applies a per-query soft rate cap (`rate_limit_rpm`, clamped to the plan's hard cap) checked before the per-profile hard cap (`rpm_sql_execute`); cache hits are charged against a separate cached-read ceiling (`rpm_sql_execute_cached`).

Authentication accepts either a **Profile Token** (runs the query directly as its owning profile) or an **Access-Management-granted analysis / Run-user token** (the token must be granted the `execute` action on the query by id or tag; the query then runs as its owning profile). The grant authorizes execution, not filtering.

If the query uses session functions (`session_user_id`, `session_user_tag`), those values are resolved from the executing user's session at run time, after authorization and before the cache lookup: a Run user gets their own values, the profile owner gets the query's COALESCE fallbacks (a session function with no fallback is a 400), and an analysis is rejected with a 400. A resolved value that matches nothing returns empty data (zero rows, 200), never the fallback.

## Path parameters

- `sqlID` string, required

## Request body

- object
  - `params` object[]: Parameter overrides, merged over the saved defaults per key. May be any subset of `$1..$N` (the saved defaults fill the rest). A token supplied in neither the body nor the saved defaults is a 400.
    - `key` string
    - `value` string
  - `test` boolean: Runs a probe that skips the cache entirely (no read, no write) and always returns `served_from_cache: false`. Not a limit bypass: both rate-limit buckets are still consumed.
  - `after_device` string: Keyset cursor for `device_data_by_tag` fan-out queries: resume the device set strictly after this device id (with `ORDER BY device`). Each page caches independently. Ignored by non-fan-out queries.

## Response `200`

Query result envelope.

- SqlExecuteResult: The rich result envelope returned by the TagoSQL execute endpoints. Results are never silently clipped: `row_count` is the true size of the result you asked for.
  - `status` boolean
  - `result` object
    - `columns` object[]: Ordered column descriptors.
      - `name` string
      - `type` string: One of `string`/`number`/`timestamp`/`boolean`/`json`.
    - `rows` object[]: One object per row, keyed by column name.
    - `row_count` integer: Number of rows returned.
    - `execution_ms` integer: Server-side execution time in milliseconds.
    - `served_from_cache` boolean: True when the result came from the cache.

## Other responses

- `400`: SQL validation error, inactive query, missing/invalid param, a written `LIMIT` above the plan row cap, a result exceeding the row cap ("Result exceeds the N-row limit. Add LIMIT (and OFFSET to paginate)."), or a `device_data_by_tag` fan-out rule violation (missing `variable`/`time` filter, a time bound beyond the plan's fan-out window, or a tag filter matching more devices than the plan cap), or a session-context failure (an execution without a user session, a session function with no COALESCE fallback on an owner execution, or a user tag resolving to multiple values).
- `402`: Plan limit exceeded.
- `403`: Authorization denied. The analysis or Run-user token has no Access Management policy granting the `execute` action on this query.
- `404`: Saved query not found (or not owned by your profile).
- `408`: Query exceeded the plan statement timeout (e.g. `Query timed out (15s limit)`).
- `429`: Rate limit exceeded (soft cap, hard cap, cached-read ceiling, or single-flight wait timeout). Includes a `Retry-After` header.

## Changes

- **2026-11-07** `1a281f04cdab`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/tago/apis/tagoio-api/changes/sql/:sqlID/execute/post.md)

---

[API](https://skmtc.dev/tago/apis/tagoio-api.md) - [All operations](https://skmtc.dev/tago/apis/tagoio-api/llms.txt) - [OpenAPI document](https://skmtc.dev/tago/apis/tagoio-api/revisions/1a281f04cdab?raw)
