HTTP API
Calling the HTTP API from outside Studio isn't self-service for new organizations yet. Email support@gnok.io to request client connectivity; Gnok support provides your organization's endpoint and how to obtain tokens. https://query.example.com below is a placeholder.
The query API endpoint is distinct from the Studio and catalog URLs; see connectivity. Administrative operations require explicit privileges.
Gnok exposes a JSON-based REST API for lightweight integrations. All request and response bodies use Content-Type: application/json.
Authentication
All protected endpoints require a JWT bearer token in the Authorization header:
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Gnok support explains how to obtain tokens for your organization when your client connectivity is set up. Tokens expire; request a new one rather than storing a token in code or shared files.
Query Endpoints
Execute Query
POST /api/query
Execute a SQL statement and return the results as JSON.
Request:
curl -X POST https://query.example.com/api/query \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"sql": "SELECT * FROM my_table LIMIT 10"}'
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
sql | string | yes | The SQL statement to execute |
Response (200):
{
"columns": ["id", "name", "value"],
"rows": [
[1, "alpha", 100.0],
[2, "beta", 200.0]
],
"row_count": 2,
"elapsed_ms": 42
}
Catalog Management
Catalogs are the top-level namespace in Gnok. These endpoints list the catalogs visible to you, select the session's active catalog, and browse namespaces and tables. To create a catalog, use SQL; see catalogs in DDL.
List Catalogs
GET /api/catalogs
Returns all catalogs visible to the authenticated user.
Request:
curl https://query.example.com/api/catalogs \
-H "Authorization: Bearer <token>"
Response (200):
[
{
"name": "production",
"type": "rest",
"uri": "https://catalog.example.com",
"warehouse": "s3://my-warehouse",
"isDefault": true
},
{
"name": "staging",
"type": "rest",
"uri": "https://catalog-staging.example.com",
"warehouse": "s3://staging-warehouse",
"isDefault": false
}
]
Select Active Catalog
POST /api/catalogs/select
Set the active catalog for the current session. Executes USE CATALOG SQL internally.
Request:
curl -X POST https://query.example.com/api/catalogs/select \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"catalogId": "production"}'
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
catalogId | string | yes | Name of the catalog to select |
Response (200):
{
"success": true,
"selectedCatalogId": "production"
}
Response (404) -- catalog not found:
{
"error": "Catalog 'nonexistent' not found"
}
Get Selected Catalog
GET /api/catalogs/selected
Return the currently active (default) catalog.
Request:
curl https://query.example.com/api/catalogs/selected \
-H "Authorization: Bearer <token>"
Response (200):
{
"name": "production",
"type": "rest",
"uri": "https://catalog.example.com",
"warehouse": "s3://my-warehouse",
"isDefault": true
}
List Namespaces in Catalog
GET /api/catalogs/:id/namespaces
Response (200):
{
"namespaces": ["public", "analytics", "staging"]
}
List Tables in Namespace
GET /api/catalogs/:id/namespaces/:ns/tables
Response (200):
{
"tables": ["orders", "customers", "products"]
}
Get Table Metadata
GET /api/catalogs/:id/namespaces/:ns/tables/:table
Returns Iceberg table metadata including schema, partition spec, and snapshot information.
Response (200):
{
"format-version": 2,
"table-uuid": "a1b2c3d4-...",
"location": "s3://my-warehouse/analytics/orders",
"schemas": [
{
"schema-id": 0,
"fields": [
{"id": 1, "name": "id", "type": "long", "required": true},
{"id": 2, "name": "customer_id", "type": "long", "required": true},
{"id": 3, "name": "amount", "type": "decimal(10,2)", "required": false}
]
}
],
"partition-specs": [
{
"spec-id": 0,
"fields": []
}
],
"properties": {},
"current-snapshot-id": 7891011,
"snapshots": []
}
Organizations and Users
The HTTP API doesn't create organizations or sign people up. Create an organization in Studio: on studio.gnok.io/login, choose New to gnok? Create an organization (see Quickstart). Organization administrators add members in Studio; see administration. Platform-wide limits are managed by Gnok.
Query History
Track and inspect previously executed queries.
List Query History
GET /api/query-history
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Maximum records to return (max: 500) |
offset | integer | 0 | Offset for pagination |
status | string | (none) | Filter by status: completed, failed, cancelled |
user_id | string | (none) | Filter by user ID |
query_type | string | (none) | Filter by query type: select, insert, ddl, etc. |
Request:
curl "https://query.example.com/api/query-history?limit=10&status=completed" \
-H "Authorization: Bearer <token>"
Response (200):
{
"records": [
{
"queryId": "q-abc123",
"status": "completed",
"sql": "SELECT count(*) FROM orders",
"userId": "alice",
"tenantId": "tenant-001",
"queryType": "select",
"resourceGroup": "default",
"queuedAt": "2025-06-15T10:30:00Z",
"startedAt": "2025-06-15T10:30:00.050Z",
"endedAt": "2025-06-15T10:30:02.150Z",
"durationMs": 2100,
"rowsProduced": 1,
"bytesScanned": 104857600,
"fragments": 4,
"errorMessage": null,
"priority": 100
}
],
"total": 1458,
"limit": 10,
"offset": 0
}
Get Query Details
GET /api/query-history/:id
Return the full record for a single query by its query ID.
Request:
curl https://query.example.com/api/query-history/q-abc123 \
-H "Authorization: Bearer <token>"
Response (200):
{
"queryId": "q-abc123",
"status": "completed",
"sql": "SELECT count(*) FROM orders",
"userId": "alice",
"tenantId": "tenant-001",
"queryType": "select",
"resourceGroup": "default",
"queuedAt": "2025-06-15T10:30:00Z",
"startedAt": "2025-06-15T10:30:00.050Z",
"endedAt": "2025-06-15T10:30:02.150Z",
"durationMs": 2100,
"rowsProduced": 1,
"bytesScanned": 104857600,
"fragments": 4,
"errorMessage": null,
"priority": 100
}
Response (404):
{
"error": "Query not found in history"
}
Table Browsing
List Tables
GET /api/tables
Returns tables visible in the current session context.
Response (200):
[
{
"catalog": "production",
"schema": "analytics",
"table": "orders"
},
{
"catalog": "production",
"schema": "analytics",
"table": "customers"
}
]
Stage Endpoints (COPY INTO)
These endpoints support bulk data ingestion via presigned S3 URLs, used by the COPY INTO SQL command and client SDKs.
| Endpoint | Method | Description |
|---|---|---|
/v1/stages/presign | POST | Generate S3 presigned URLs for file upload |
/v1/stages/presign/multipart/init | POST | Initialize a multipart upload |
/v1/stages/presign/multipart/complete | POST | Complete a multipart upload |
/v1/stages/presign/multipart/abort | POST | Abort a multipart upload |
Error Responses
All error responses return a JSON body with an error field. The HTTP status code indicates the category of error.
| Status Code | Meaning | Example |
|---|---|---|
200 | Success | Request completed normally |
201 | Created | Resource created successfully |
204 | No Content | Deletion completed successfully |
400 | Bad Request | Missing or invalid request parameters |
401 | Unauthorized | Missing or expired JWT token |
403 | Forbidden | User lacks permission for the requested operation |
404 | Not Found | Requested resource does not exist |
409 | Conflict | Resource already exists |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Unexpected server-side failure |
503 | Service Unavailable | Service dependency is unreachable |
Error response format
{
"error": "Human-readable error message describing what went wrong"
}
400 Bad Request
Returned when the request body is missing required fields or contains invalid values.
{
"error": "catalogId is required"
}
401 Unauthorized
Returned when the Authorization header is missing or the JWT token is expired or invalid.
{
"error": "Missing or invalid authentication token"
}
403 Forbidden
Returned when the authenticated user does not have permission to perform the operation.
{
"error": "Access denied: insufficient privileges on table 'analytics.orders'"
}
404 Not Found
Returned when the requested resource does not exist.
{
"error": "Catalog 'nonexistent' not found"
}
429 Too Many Requests
Returned when per-user or per-organization rate limits are exceeded. Clients should implement exponential backoff and retry after the period indicated by the Retry-After header, if present.
{
"error": "Rate limit exceeded. Try again later."
}
500 Internal Server Error
Returned on unexpected server failures. The error message may be generic to avoid leaking internal details.
{
"error": "Internal server error"
}
Rate Limiting
The HTTP API enforces rate limits per user and per organization. When a rate limit is exceeded, the server returns a 429 Too Many Requests response.
Recommendations for client implementations:
- Implement exponential backoff with jitter when receiving 429 responses.
- Respect the
Retry-Afterheader if present in the response. - Batch queries where possible rather than issuing many small requests.
- Use query history polling (
GET /api/query-history/:id) instead of re-submitting failed queries.
Base URL and Ports
All paths are relative to your organization's HTTPS query API endpoint, shown here as the placeholder https://query.example.com. Gnok support provides the endpoint when your client connectivity is set up; email support@gnok.io.