Skip to content

HTTP/REST API Examples

All examples use the default address http://127.0.0.1:8443 and read the API key from HELIOSDB_API_KEY. The SQL endpoint takes one statement per request in the query field.

Authentication

Terminal window
# Authorization header
curl -s -X POST http://127.0.0.1:8443/api/v1/query \
-H "Authorization: Bearer $HELIOSDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "SELECT 1"}'
# X-API-Key header
curl -s -X POST http://127.0.0.1:8443/api/v1/query \
-H "X-API-Key: $HELIOSDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "SELECT 1"}'

Without a valid key the gateway answers 401 with {"error":"unauthorized"}.

A small Python client

import os
import requests
BASE = os.environ.get("HELIOSDB_HTTP_URL", "http://127.0.0.1:8443")
HEADERS = {"Authorization": f"Bearer {os.environ['HELIOSDB_API_KEY']}"}
class HeliosError(RuntimeError):
pass
def query(sql: str, session: str | None = None) -> dict:
headers = dict(HEADERS)
if session:
headers["X-HeliosDB-Session"] = session
r = requests.post(f"{BASE}/api/v1/query", json={"query": sql},
headers=headers, timeout=30)
if r.status_code == 429:
raise HeliosError(f"rate limited, retry after {r.headers.get('Retry-After')} s")
r.raise_for_status()
body = r.json()
if not body.get("success"):
raise HeliosError(body.get("error"))
return body
def rows_as_dicts(body: dict) -> list[dict]:
names = [c["name"] for c in body["columns"]]
return [dict(zip(names, row)) for row in body["rows"]]

Create, insert, read, update, delete

query("CREATE TABLE IF NOT EXISTS users (id INT PRIMARY KEY, name TEXT, active BOOLEAN)")
query("INSERT INTO users VALUES (1, 'Alice', true), (2, 'Bob', false)")
active = rows_as_dicts(query("SELECT id, name FROM users WHERE active ORDER BY id"))
print(active) # [{'id': 1, 'name': 'Alice'}]
print(query("UPDATE users SET active = true WHERE id = 2")["row_count"]) # 1
print(query("DELETE FROM users WHERE id = 2")["row_count"]) # 1

The HTTP API has no bind parameters. Build statements only from values your application has validated (numbers parsed as numbers, strings checked against an allow list or quoted with doubled single quotes), or use a PostgreSQL driver when you need server-side parameter binding.

Transactions with a pinned session

Plain requests run on a pooled handler that is reset after each request, so a transaction needs a pinned session (server started with --http-sessions):

s = requests.post(f"{BASE}/api/v1/sessions", headers=HEADERS, timeout=30).json()["session"]
try:
query("BEGIN", s)
query("UPDATE accounts SET balance = balance - 100 WHERE id = 1", s)
query("UPDATE accounts SET balance = balance + 100 WHERE id = 2", s)
query("COMMIT", s)
finally:
requests.delete(f"{BASE}/api/v1/sessions",
headers={**HEADERS, "X-HeliosDB-Session": s}, timeout=30)

Ending a session with an open transaction rolls it back.

Pagination

def all_orders(page_size: int = 500):
last_id = 0
while True:
body = query(f"SELECT id, total FROM orders WHERE id > {int(last_id)} "
f"ORDER BY id LIMIT {int(page_size)}")
if not body["rows"]:
return
yield from body["rows"]
last_id = body["rows"][-1][0]

Keyset pagination (WHERE id > last) stays fast on large tables.

Error handling

SituationHTTP statusBody
SQL error200{"success": false, "error": "..."}
Missing or malformed body400{"error": "Missing 'query' field", "hint": "..."}
No or wrong API key401{"error": "unauthorized"}
Unknown path404{"error": "Not found", "path": "..."}
Rate limit reached429Retry-After header set
Session route without --http-sessions501sessions_disabled

Health checks

Terminal window
curl -fsS http://127.0.0.1:8443/health # {"status":"healthy","version":"..."}
curl -fsS http://127.0.0.1:8443/ready # {"ready":true}
# Kubernetes probes (the gateway port must be reachable inside the pod)
livenessProbe:
httpGet: { path: /health, port: 8443 }
readinessProbe:
httpGet: { path: /ready, port: 8443 }

See the HTTP/REST API reference for every route.