Menu
On this page

API & MCP

Querying data

These are the endpoints you'll use most: they turn any dataset into queryable data over HTTP. Everything here is read-only, capped at 1,000 rows per request, and authenticated with a bearer key. The {table_name} in every path is the dataset's physical gp_… name, which you get from listing datasets.

Query rows — POST /tables/{table_name}

The REST facade is the simplest way to read a dataset: pick columns, filter, sort and paginate with a JSON body. No SQL required.

cURL
curl -X POST "https://<your-gopie-host>/v1/api/tables/gp_DzuutVJef9rSG" \
  -H "Authorization: Bearer gp_pub_…" \
  -H "Content-Type: application/json" \
  -d '{
    "columns": ["product", "revenue"],
    "filter": { "filter[category]": "Electronics", "filter[revenue]gte": "500" },
    "sort": "-revenue",
    "limit": 50,
    "page": 1
  }'
JavaScript
const res = await fetch(
  "https://<your-gopie-host>/v1/api/tables/gp_DzuutVJef9rSG",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer gp_pub_…",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      columns: ["product", "revenue"],
      filter: { "filter[category]": "Electronics", "filter[revenue]gte": "500" },
      sort: "-revenue",
      limit: 50,
      page: 1
    })
  }
);
const { data, count } = await res.json();
Python
import requests
 
res = requests.post(
    "https://<your-gopie-host>/v1/api/tables/gp_DzuutVJef9rSG",
    headers={"Authorization": "Bearer gp_pub_…"},
    json={
        "columns": ["product", "revenue"],
        "filter": {"filter[category]": "Electronics", "filter[revenue]gte": "500"},
        "sort": "-revenue",
        "limit": 50,
        "page": 1,
    },
)
rows = res.json()["data"]

Every field is optional — an empty body {} returns the first page of all columns.

FieldMeaning
columnsArray of columns to return; omit for all
filterMap of conditions, AND-ed together — see below
sortComma-separated columns; - prefix for descending (-revenue,product)
limitRows per page, capped at 1,000
page1-based page number
snapshot_versionRead the dataset as of a past version

Filter syntax

This is the one non-obvious part. Filter keys encode the column and operator as filter[column]op — a plain {"category": "Electronics"} key is silently ignored:

Key formCondition
filter[col]equals
filter[col]neqnot equal
filter[col]gt / filter[col]gtegreater than / or equal
filter[col]lt / filter[col]lteless than / or equal

Comparison operators (gt, lte, …) work on numeric values only; text values filter by equality. There is no like, in, or between — reach for SQL when you need those. An unrecognised operator is a 400:

{ "code": 400, "error": "invalid filter operator", "message": "Invalid rest params" }

Response

{
  "data": [ { "product": "27-inch Monitor", "revenue": 2490 } ],
  "count": 42,
  "executionTime": 23
}

count is the total matching rows (not the page size) — use it with limit and page to walk the whole result. If the count couldn't be computed it comes back as -1; the data is still valid.

Run SQL — POST /sql

When the filter grammar isn't enough, send arbitrary read-only SQL. Authorisation is derived by parsing the table references out of your query, so a key can only run SQL over datasets it can already reach.

curl -X POST "https://<your-gopie-host>/v1/api/sql" \
  -H "Authorization: Bearer gp_pub_…" \
  -H "Content-Type: application/json" \
  -d '{"query": "SELECT category, SUM(revenue) AS total FROM gp_DzuutVJef9rSG GROUP BY 1 ORDER BY 2 DESC"}'
{
  "columns": ["category", "total"],
  "count": 3,
  "data": [ { "category": "Electronics", "total": 148156.86 } ],
  "executionTime": 75
}
Body fieldMeaning
queryThe SQL (required)
limitRows to return, capped at 1,000
offsetRows to skip
snapshot_version / snapshot_timestampTime travel to a past version

Only SELECT, WITH, DESCRIBE and SUMMARIZE are allowed — the same read-only rules as the SQL playground. Anything that writes, and any attempt to send more than one statement, is rejected:

{ "code": 400, "error": "multiple sql statements are not allowed", "message": "Invalid SQL query" }
Note

Notice the response shapes differ: /sql includes a columns array, /tables does not. Both include count and executionTime.

Generate SQL from a question — POST /nl2sql

Turn a plain-English question into SQL without running it — useful for building query UIs or letting users preview what a question resolves to.

curl -X POST "https://<your-gopie-host>/v1/api/nl2sql" \
  -H "Authorization: Bearer gp_pub_…" \
  -H "Content-Type: application/json" \
  -d '{"query": "total revenue by category", "table": "gp_DzuutVJef9rSG"}'
{ "sql": "SELECT \"category\", SUM(\"revenue\") AS \"total_revenue\"\nFROM \"gp_DzuutVJef9rSG\"\nGROUP BY \"category\"\nORDER BY \"total_revenue\" DESC" }

Provide exactly one of table (a single table name) or tables (an array, for multi-table questions). If the question can't be answered from the schema, you get a 200 explaining why instead of SQL:

{ "sql": "", "non_sql_response": true, "message": "The data doesn't contain information about customer support tickets." }

This is a single-shot translation with no conversation memory. For a full analytical conversation — schema search, self-correcting SQL, grounded prose — use the Chat API.

Inspect structure — schemas & summaries

GET /schemas/{table_name} returns the columns and their types:

{ "schema": [ { "column_name": "revenue", "column_type": "DOUBLE" } ] }

GET /summary/{table_name} returns per-column statistics — min, max, average, quartiles, distinct and null counts, plus any column descriptions:

{ "summary": [
  { "column_name": "revenue", "column_type": "DOUBLE",
    "min": 0, "max": 9999, "avg": 123.4, "approx_unique": 812,
    "null_percentage": 0, "description": "Line-item revenue in USD" }
] }

By default the summary is served from a fast cached copy. Add ?source=olap to force a live recompute over the whole table — accurate but expensive on large datasets, so use it deliberately. The full statistical summary uses SUMMARIZE.

Get an OpenAPI spec — GET /openapi/{table_name}

Returns an OpenAPI 3.0 document generated live from the dataset's actual schema — its columns become typed response properties. Feed it to Postman, a client generator, or any tool that speaks OpenAPI to scaffold a typed client for that specific dataset.