API & MCP
API overview & authentication
GoPie exposes a read-only HTTP API for querying your data, plus an MCP server for connecting AI clients. Both live on the same surface, authenticated by an API key sent as a bearer token. This page is the shared foundation — read it once, then jump to the endpoint you need.
Everything here is already reachable from inside the app: every dataset is an instant REST API, and you create keys under applications. The reference pages exist for when you're calling GoPie from your own code, a scheduled job, or another service.
The base URL#
The public API and MCP are served on a dedicated port — 8002 — separate from the app itself. In a hosted deployment that port sits behind your GoPie host, so every path in these docs is relative to:
https://<your-gopie-host>/v1/apiThe one exception is MCP, which is served at /mcp (no /v1/api prefix). On a local self-hosted stack the surface is http://localhost:8002. The API is read-only — there are no write endpoints for data on this port; ingestion happens in the app or through MCP tools.
Authenticate every request#
Send your key as a bearer token:
curl -H "Authorization: Bearer gp_pub_your_key_here" \
https://<your-gopie-host>/v1/api/configThere is no session, no cookie, no OAuth handshake — just the header, on every request. A missing or malformed header is rejected immediately:
{ "code": 401, "error": "Authorization header is missing", "message": "Unauthorized" }Two kinds of key#
GoPie has two distinct key types, and using the wrong one is the most common first mistake:
| Application key | Organisation-wide key | |
|---|---|---|
| Prefix | gp_pub_ | gp_mcp_ |
| Created in | Settings → Applications (guide) | no UI yet — see MCP overview |
| Reaches | only the projects & datasets its application grants | everything in the organisation |
| Use it for | the REST data API | the REST data API and MCP |
| MCP endpoint | rejected (403) | accepted |
Both act with organisation-owner privileges for role checks — the only thing that narrows an application key is its grants. So an application key scoped to one dataset can read exactly that dataset; an organisation-wide key can read every dataset in the org. Treat organisation-wide keys with extra care.
ImportantApplication keys and the datasets/projects you share with a teammate are unrelated systems. Granting a person a dataset does nothing for any API key, and an application's grants have nothing to do with member access — they're scoped independently.
The identifiers you'll juggle#
Four identifiers show up across the endpoints. Getting them straight saves a lot of 404s:
| Identifier | Looks like | What it is |
|---|---|---|
| Project id | 550e8400-… (UUID) | a project |
| Dataset id | 550e8400-… (UUID) | a dataset, for metadata & sharing endpoints |
| Table name | gp_Dh790Asdf17kd | the dataset's physical name — what you query with |
| Alias | Orders | the dataset's human-friendly display name (not a path param) |
The load-bearing subtlety: query endpoints take the gp_… table name, metadata endpoints take the UUID. They're two keys for the same dataset. The alias is only for display and search.
Discover what you can reach#
There is no "list projects" endpoint on this port. Instead, list datasets — the response embeds each dataset's project, so one call gives you projects, datasets, and the table names you query with:
curl -H "Authorization: Bearer gp_pub_…" \
"https://<your-gopie-host>/v1/api/datasets?limit=20"{
"results": [
{ "id": "…", "name": "gp_DzuutVJef9rSG", "alias": "Orders",
"row_count": 1200, "project_id": "…", "project_name": "Sales Analytics" }
],
"offset": 0, "limit": 20, "total": 3
}Take name (the gp_… value) and you're ready to query. Full listing details are on the Datasets API page.
The error envelope#
Errors return a small JSON object. The numeric code mirrors the HTTP status; message is a human summary and error carries the specific reason:
{ "code": 403, "error": "This API key is not scoped to the requested dataset", "message": "Forbidden" }What you'll see:
| Status | When |
|---|---|
400 | malformed body, an invalid filter operator, a non-read-only or multi-statement SQL query |
401 | missing, malformed, expired, or disabled key |
403 | an application key reaching a dataset or project it wasn't granted |
404 | no dataset with that table name or id in your organisation |
500 | an unexpected server-side error |
NoteThe
errorfield is usually a string, but validation failures onPOST /sqlreturn an array of{field, tag, value}objects instead. Readcodeto branch, then inspecterrorfor detail.
Rate limits#
Rate limiting is off by default. When a deployment enables it (GOPIE_RATELIMIT_ENABLED=true), requests are counted per API key — the default budget is 1,000 requests per minute. Over the limit, you get:
{ "code": 429, "error": "rate_limited", "message": "too many requests; slow down and retry" }with a Retry-After: 60 header. There are no X-RateLimit-* headers — honour Retry-After and back off.
Where to next#
- Querying data —
/sql, the/tablesREST facade,/nl2sql, schemas, summaries and config. Start here. - Datasets API — list and inspect datasets, edit column descriptions, share, reindex.
- Downloads API — export query results to a file over a streamed connection.
- Chat API — drive the SQL agent and consume its streamed answer.
- Dashboards API — generate and manage dashboards programmatically.
- MCP — connect Claude, Cursor and other AI clients to your data.