HeliosDB HTTP/REST API Reference
HeliosDB Full includes an HTTP gateway next to its wire-protocol listeners. It runs
SQL over HTTP and returns JSON, so scripts, serverless functions and tools that
cannot load a database driver can still reach the database. The gateway runs inside
the heliosdb-full server process; there is nothing extra to install.
This page describes the endpoints the heliosdb-full server serves, how to change
the port and bind address, and how requests are authenticated.
The HTTP gateway has no S3/object endpoints (planned): an S3-compatible object API
for HeliosDB Full is on the roadmap and is not part of the current release. The
/snowflake/, /dbsql/ and /pinecone/ paths answer 501 Not Implemented; those
interfaces are planned as well.
Default address
| Setting | Default | Change it with |
|---|---|---|
| Port | 8443 | --http-port <port> or HELIOSDB_HTTP_PORT |
| Bind address | 127.0.0.1 | --bind-addr <address> or HELIOSDB_BIND_ADDR |
| Enabled | yes | --no-http turns the gateway off |
The listener speaks plain HTTP on port 8443; the port number is only the default, not an indication of TLS. For connections that leave the host, put the gateway behind a TLS-terminating reverse proxy or load balancer (nginx, HAProxy, Envoy, a cloud load balancer) and keep the gateway itself on a private address.
# Local only (default): http://127.0.0.1:8443heliosdb-full
# Another portheliosdb-full --http-port 9443# orHELIOSDB_HTTP_PORT=9443 heliosdb-fullBinding to a non-loopback address
--bind-addr applies to every listener the server opens (PostgreSQL, HTTP and any
protocol you enable). Binding to anything other than a loopback address requires an
explicit opt-in, and the server refuses to start without it:
heliosdb-full --bind-addr 0.0.0.0 --allow-external# orHELIOSDB_BIND_ADDR=0.0.0.0 HELIOSDB_ALLOW_EXTERNAL=true heliosdb-full--allow-external accepts 1/0, true/false, yes/no and on/off. Configure an API
key (below) and a firewall before you expose the gateway.
Containers
The HeliosDB Full container image (provided during onboarding; it is not on a public
registry) already binds 0.0.0.0 inside the container and sets
HELIOSDB_ALLOW_EXTERNAL=true. Publish the port you need:
docker run -d --name heliosdb \ --env-file /etc/heliosdb/heliosdb.env \ -p 127.0.0.1:8443:8443 -p 127.0.0.1:5432:5432 \ -v heliosdb-data:/var/lib/heliosdb/data \ <heliosdb-full-image>/etc/heliosdb/heliosdb.env holds HELIOSDB_SUPERUSER_PASSWORD and
HELIOSDB_API_KEY and is readable only by root. To serve HTTP on another port inside
the container, add -e HELIOSDB_HTTP_PORT=9443 and publish -p 127.0.0.1:9443:9443.
Authentication
| Route | Authentication |
|---|---|
GET /, GET /health, GET /healthz, GET /ready, GET /readyz, GET /status, GET /api/v1/status | None (probes and server information) |
| Every other route | API key |
Set the key on the server with the HELIOSDB_API_KEY environment variable. Clients
send it in either header:
Authorization: Bearer <api-key>X-API-Key: <api-key>The key is compared in constant time. A request without a valid key receives:
HTTP/1.1 401 UnauthorizedWWW-Authenticate: BearerContent-Type: application/json
{"error":"unauthorized"}When HELIOSDB_API_KEY is not set, every non-public route returns 401. The only way
to call them without a key is --auth-mode trust, which the server accepts on a
loopback bind only (it refuses to start with trust on an external address). Use it
for local development.
The HTTP gateway has one shared key, not per-user accounts: SQL sent over HTTP is not tied to a database role, so treat the key like a superuser password. For per-user logins, connect with a PostgreSQL client and a database role instead.
Keep the key out of shell history and process listings: export it from a secrets
manager or an environment file readable only by the service account, for example
EnvironmentFile= in a systemd unit or --env-file with Docker.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /health, /healthz | Liveness |
| GET | /ready, /readyz | Readiness |
| GET | /status, /api/v1/status | Server status |
| GET | / | Server name, version and route list |
| POST | /api/v1/query, /query | Run one SQL statement |
| POST, DELETE | /api/v1/sessions | Pinned sessions (opt-in, see below) |
| POST | /api/v1/chat/completions, /v1/chat/completions | Chat completion through a configured AI provider |
| POST | /api/v1/embeddings, /v1/embeddings | Embeddings through a configured AI provider |
| POST | /api/v1/audio/transcribe, /api/v1/audio/transcriptions, /v1/audio/transcriptions | Speech-to-text through a configured AI provider |
| POST | /api/v1/vectors/stores | Create a vector store |
| POST | /api/v1/vectors/stores/{name}/texts | Add texts to a vector store |
| POST | /api/v1/vectors/stores/{name}/search/hybrid | Hybrid (vector + keyword) search |
| GET, POST | /api/v1/agents/memory | List or create agent memory sessions |
| GET, DELETE | /api/v1/agents/memory/{id} | Read or delete a memory session |
| GET | /api/v1/agents/memory/{id}/messages | Read messages |
| POST | /api/v1/agents/memory/{id}/messages/batch | Append messages |
| POST | /api/v1/agents/memory/{id}/clear, /summarize, /search | Clear, summarize or search a session |
| GET, POST | /api/v1/documents (also POST /api/v1/documents/ingest) | List or ingest documents |
| GET, DELETE | /api/v1/documents/{id} | Read or delete a document |
| POST | /api/v1/documents/search | Search documents |
Any other path returns 404 with {"error":"Not found","path":"..."}.
Health and status
curl -s http://127.0.0.1:8443/health# {"status":"healthy","version":"x.y.z"}
curl -s http://127.0.0.1:8443/ready# {"ready":true}
curl -s http://127.0.0.1:8443/status# {"status":"running","version":"x.y.z","protocols":["postgresql","mysql","http"]}/health and /ready need no key, so you can use them directly as container
health checks and load-balancer probes. The protocols list in /status is a fixed
summary; the server log and SHOW PROTOCOLS (from psql) show which listeners are
actually open.
Run SQL: POST /api/v1/query
Send one statement in the query field of a JSON body:
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 id, name FROM customers WHERE id = 1"}'Successful result:
{ "success": true, "columns": [ {"name": "id", "type": "integer"}, {"name": "name", "type": "text"} ], "rows": [[1, "Acme"]], "row_count": 1}Column type is one of integer, float, boolean or text; other SQL types are
reported as text and their values are returned as JSON strings. NULL is JSON
null. DDL and DML return an empty columns/rows and the affected row count in
row_count.
A SQL error is returned with HTTP status 200 and success: false, so check the
success field rather than only the status code:
{"success": false, "error": "relation \"no_such_table\" does not exist"}A missing or malformed body returns 400 with a hint:
{"error": "Missing 'query' field", "hint": "Send JSON body: {\"query\": \"SELECT 1\"}"}Notes:
- The body field is
query. Bind parameters are not part of the HTTP API; build statements with values your application has validated, or use a PostgreSQL driver when you need server-side parameter binding. - Each request runs on a pooled PostgreSQL handler that is reset afterwards, so
BEGIN, temporary tables andSETdo not carry over to the next request. ABEGINover plain HTTP is refused. Use a pinned session (next section) for multi-statement transactions. - Responses carry
Access-Control-Allow-Origin: *. - The pool size is set with
--http-pg-pool-size/HELIOSDB_HTTP_PG_POOL_SIZE(default 16;0builds a handler per request).
Pinned sessions: /api/v1/sessions
A pinned session keeps one dedicated PostgreSQL session for an HTTP client across
requests, so transactions, temporary tables and SET work over HTTP. Start the server
with --http-sessions to enable it; without it these routes return 501
(sessions_disabled).
# 1. Create a sessioncurl -s -X POST http://127.0.0.1:8443/api/v1/sessions \ -H "Authorization: Bearer $HELIOSDB_API_KEY"# {"session":"<node>.<token>","node":"<node>","expires_in_secs":300,# "idle_in_transaction_timeout_secs":60,"max_lifetime_secs":3600,"in_transaction":false}
# 2. Run statements on itcurl -s -X POST http://127.0.0.1:8443/api/v1/query \ -H "Authorization: Bearer $HELIOSDB_API_KEY" \ -H "X-HeliosDB-Session: $SESSION" \ -d '{"query": "BEGIN"}'# ... more statements with the same header ...curl -s -X POST http://127.0.0.1:8443/api/v1/query \ -H "Authorization: Bearer $HELIOSDB_API_KEY" \ -H "X-HeliosDB-Session: $SESSION" \ -d '{"query": "COMMIT"}'
# 3. End it (an open transaction is rolled back)curl -s -X DELETE http://127.0.0.1:8443/api/v1/sessions \ -H "Authorization: Bearer $HELIOSDB_API_KEY" \ -H "X-HeliosDB-Session: $SESSION"Responses on a pinned session add "in_transaction": true|false. The token travels
only in the X-HeliosDB-Session header, never in the URL or body, and the server
stores only its SHA-256 hash.
| Flag (environment variable) | Default | Meaning |
|---|---|---|
--http-sessions | off | Enable pinned sessions |
--http-max-sessions (HELIOSDB_HTTP_MAX_SESSIONS) | 256 | Sessions per node |
--http-max-sessions-per-principal (HELIOSDB_HTTP_MAX_SESSIONS_PER_PRINCIPAL) | 32 | Sessions per API key |
--http-session-idle-timeout-secs (HELIOSDB_HTTP_SESSION_IDLE_TIMEOUT_SECS) | 300 | Idle time before the session ends |
--http-session-idle-in-txn-timeout-secs (HELIOSDB_HTTP_SESSION_IDLE_IN_TXN_TIMEOUT_SECS) | 60 | Idle time while a transaction is open (work is rolled back) |
--http-session-max-lifetime-secs (HELIOSDB_HTTP_SESSION_MAX_LIFETIME_SECS) | 3600 | Maximum session age |
--http-session-reap-interval-ms (HELIOSDB_HTTP_SESSION_REAP_INTERVAL_MS) | 1000 | Reaper interval; also the Retry-After of a 429 |
--http-session-concurrency wait|reject (HELIOSDB_HTTP_SESSION_CONCURRENCY) | wait | A second concurrent request on one token waits, or gets 409 |
--http-session-bind-ip | off | Also bind each session to the client IP that created it |
--node-id (HELIOSDB_NODE_ID) | hostname | Node id carried in each token, for load-balancer routing |
Session errors:
| Status | Code | Meaning |
|---|---|---|
| 400 | session_malformed | The token is not well formed |
| 403 | session_forbidden | Token used with another API key, or from another IP with --http-session-bind-ip |
| 404 | session_unknown | No such session |
| 409 | session_not_local | The token belongs to another node (the body names it) |
| 409 | session_busy | Another request is running on the token and concurrency is reject |
| 410 | session_expired | The session timed out |
| 429 | too_many_sessions | A session limit was reached (Retry-After is set) |
| 500 | session_lost | The session ended because of an internal error |
| 501 | sessions_disabled | The server was started without --http-sessions |
Behind a load balancer, route requests that carry X-HeliosDB-Session to the node
named in the token (the part before the first .), or use sticky sessions.
AI routes
The chat, embeddings and audio routes forward requests to an AI provider configured on the server. The vector store, agent memory and document routes store their data in HeliosDB and call the provider for embeddings.
| Environment variable | Purpose |
|---|---|
HELIOSDB_AI_PROVIDER | openai (default), anthropic or ollama |
HELIOSDB_AI_API_KEY | Provider API key |
HELIOSDB_AI_API_BASE | Provider base URL (defaults to the provider’s public endpoint) |
HELIOSDB_AI_CHAT_MODEL, HELIOSDB_AI_EMBED_MODEL | Model names |
HELIOSDB_AI_ALLOW_PRIVATE_NETWORK | 1 allows an http:// or private-network base URL, for a local Ollama |
HELIOSDB_AI_HOST_ALLOWLIST | Comma-separated host names allowed as the base URL |
HELIOSDB_AI_RATE_LIMIT, HELIOSDB_AI_RATE_WINDOW_SECS | Requests per tenant per window (default 60 per 60 s; 0 disables) |
Requests are counted per tenant, taken from the X-HeliosDB-Tenant header (default
when absent). Over the limit, the route returns 429 with Retry-After. The chat
route accepts the OpenAI-style body (messages, optional model, temperature,
max_tokens, stream); with "stream": true the response is server-sent events.
curl -s -X POST http://127.0.0.1:8443/api/v1/chat/completions \ -H "Authorization: Bearer $HELIOSDB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"messages":[{"role":"user","content":"Summarise last week'\''s orders"}]}'Snowflake, Databricks and Pinecone paths
Requests under /snowflake/, /dbsql/ and /pinecone/ are answered with 501 Not Implemented by the heliosdb-full HTTP listener. Use POST /api/v1/query or a
PostgreSQL client for SQL access.
Rate limits
| Limit | Default | Setting |
|---|---|---|
| New connections per client IP | 500 per minute (burst 100), all listeners | --conn-rate-limit <per-minute> (HELIOSDB_CONN_RATE_LIMIT) |
| SQL queries per client IP | not limited | --query-rate-limit <per-second> (HELIOSDB_QUERY_RATE_LIMIT); applies to POST /api/v1/query and the PostgreSQL wire |
| AI requests per tenant | 60 per 60 s | HELIOSDB_AI_RATE_LIMIT, HELIOSDB_AI_RATE_WINDOW_SECS |
| Concurrent connections, all protocols | 10000 | --max-connections (HELIOSDB_MAX_CONNECTIONS) |
A query over the limit returns 429 Too Many Requests with a Retry-After header.
Setting the environment variable HELIOSDB_RATE_LIMITING=false turns the connection
and query limiters off (not recommended for production). The --rate-limiting
command-line flag can only switch them on, which is already the default:
--rate-limiting false and --rate-limiting=false are rejected by the argument
parser.
Client examples
Python
import osimport requests
BASE = os.environ.get("HELIOSDB_HTTP_URL", "http://127.0.0.1:8443")HEADERS = {"Authorization": f"Bearer {os.environ['HELIOSDB_API_KEY']}"}
def query(sql: str) -> dict: r = requests.post(f"{BASE}/api/v1/query", json={"query": sql}, headers=HEADERS, timeout=30) r.raise_for_status() body = r.json() if not body.get("success"): raise RuntimeError(body.get("error")) return body
result = query("SELECT COUNT(*) AS n FROM orders")print(result["rows"][0][0])JavaScript / TypeScript
const BASE = process.env.HELIOSDB_HTTP_URL ?? "http://127.0.0.1:8443";
async function query(sql: string) { const res = await fetch(`${BASE}/api/v1/query`, { method: "POST", headers: { "Authorization": `Bearer ${process.env.HELIOSDB_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ query: sql }), }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const body = await res.json(); if (!body.success) throw new Error(body.error); return body;}Shell health check
curl -fsS http://127.0.0.1:8443/health > /dev/null || echo "HeliosDB is down"Related
- Connecting to HeliosDB — PostgreSQL, MySQL and other wire-protocol clients
- Configuration reference
- Run
heliosdb-full --helpfor every flag and its environment variable, andheliosdb-full --show-featuresfor the listeners and features in your build