---
title: "Read with SQL"
description: "Runs one SELECT as you and returns rows and columns."
canonical: "https://gtable.app/docs/app-api/sql/query"
updated: "2026-10-05"
---

# Read with SQL

`POST https://your-suite.gtable.app/your-app/v1/sql/query`

Runs one SELECT as you and returns rows and columns.

> **Note: Generic contract**
>
> Your own version of this operation, with your tables and the fields you can read, is in
> your app's document: `https://{suite}.gtable.app/{app}/v1/openapi.json`, behind your credential.
> [Why each person gets their own](/docs/start/studio-and-runtime#why-an-app-has-an-api-of-its-own).

Fast for questions a filter cannot ask: aggregates, GROUP BY, CASE, functions. The dialect: SELECT \<columns or aggregates> FROM \<table> \[WHERE …] \[GROUP BY …] \[ORDER BY …] \[LIMIT n OFFSET m]; UPDATE \<table> SET col = expr \[WHERE …]; DELETE FROM \<table> \[WHERE …]. One table per statement; field names or ids (double-quote names with spaces); 'text', numbers, TRUE, FALSE, NULL, CURRENT\_DATE; AND OR NOT, = \<> \< \<= > >=, \[NOT] LIKE, \[NOT] IN (…), IS \[NOT] NULL, \[NOT] BETWEEN, CASE WHEN cond THEN v … \[ELSE v] END and CASE expr WHEN value THEN v … \[ELSE v] END; a select takes its choice's label (any case) or id; functions LENGTH LOWER UPPER TRIM ABS ROUND COALESCE SUBSTR DATE STRFTIME IFNULL INSTR REPLACE; link columns: col HAS ANY ('id', …), col HAS ALL (…), col IS EMPTY. Your text is never executed: it is compiled against your own permissions, and a column you may not read is simply an unknown column. At most 1000 rows; add LIMIT.

Requires the `records:read` scope. Operation `sql.query`. MCP tool `sql_query`. CLI, once published: `gtable sql query`.

## Headers

| Name              | Type   | Required | Description                                                                                                                                                                                                                                                        |
| ----------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `Idempotency-Key` | string | no       | Any unique string. Sending the same key with the same request again returns the first response (marked `Idempotency-Replayed: true`) instead of running it twice. Reusing it for a different request is refused with 409. Kept for 24 hours. Up to 255 characters. |

## Request body

| Field | Type   | Required | Description             |
| ----- | ------ | -------- | ----------------------- |
| `sql` | string | yes      | Up to 20000 characters. |
