HTTP SQL API

Run SQL over HTTPS with streamed or asynchronous statements, sessions, safe retries and stable error codes.

The HTTP SQL API is a complete SQL interface over HTTPS. Use it from scripts, services and any language that has an HTTP client. It runs the same SQL, permissions and warehouses as Flight SQL and Spark Connect.

RequestBehavior
POST /v1/sqlRuns one statement and streams its result in the requested format.
POST /v1/sql?query=INSERT INTO t FORMAT <format>Inserts the request body (CSV, JSONEachRow, Parquet or Arrow).
POST /v1/statementsSubmits a statement. Returns the result when it is ready within wait_timeout, otherwise a statement ID.
GET /v1/statements/{id}Returns the statement's state, progress and result.
DELETE /v1/statements/{id}Cancels the statement.
DELETE /v1/sessions/{id}Closes a session, cancelling its active statement and rolling back an open transaction.
GET /openapi.jsonDescribes the requests, states, errors, result schemas and the limits in force.

Every request carries a credential (Authorization: Bearer <token> or X-API-Key: <api key>) and the X-Vega-Organization and X-Vega-Workspace headers. See authentication.

Stream a result

Send the SQL as the request body, or in the query parameter.

curl --request POST "https://<warehouse endpoint>/v1/sql?format=CSV&warehouse=bi" \
  --header "Authorization: Bearer $VEGADB_TOKEN" \
  --header "X-Vega-Organization: <organization id>" \
  --header "X-Vega-Workspace: <workspace id>" \
  --header "Accept-Encoding: zstd" \
  --data-binary @daily_revenue.sql \
  --output daily_revenue.csv.zst
ParameterMeaning
formatJSON (default), JSONEachRow, CSV, TSV, Arrow (Arrow IPC stream) or Parquet.
warehouseThe warehouse to run on, when the workspace has more than one.
databaseThe default schema, as schema or catalog.schema.
session_id, create_sessionRun in a named session, or create one. See sessions.
request_id (or X-Request-Id)A UUID that makes a retry safe. See retries.
query_idA UUID for the query; defaults to the request ID or a generated UUID.
read_onlyRefuse any statement that changes data or session state.
timeout_msStatement timeout, including time spent queued and waiting for the warehouse.
settingsA JSON object of session settings, such as application_name.

gzip and zstd compression follow Accept-Encoding. Response headers carry X-Vega-Query-Id, X-Vega-Statement-Id and, in a session, X-Vega-Session-Id. Progress and a summary (rows, bytes, elapsed time and warehouse wait) arrive in headers and trailers.

A complete result always says so

A streamed result can fail after its first byte. Every format therefore ends with an explicit marker, and the response ends with an x-vega-status trailer:

FormatEnd of a complete result
JSONThe document's complete field, or its error.
JSONEachRowEach data row is wrapped as {"row": {...}}; the last record has status and complete, or error.
CSV, TSVA final #vega_status record.
ArrowThe Arrow IPC end-of-stream marker.
ParquetThe Parquet footer.

Treat a response without its marker as incomplete. Because data rows are wrapped, a column named status or complete cannot be mistaken for the end of the result.

JSON, JSONEachRow, CSV and TSV render values as text or null and report each column's SQL type. Arrow and Parquet keep the native types, including nested ones; use them for exports.

Insert data

curl --request POST "https://<warehouse endpoint>/v1/sql?query=INSERT%20INTO%20sales.orders%20FORMAT%20CSV" \
  --header "X-API-Key: $VEGADB_API_KEY" \
  --header "X-Vega-Organization: <organization id>" \
  --header "X-Vega-Workspace: <workspace id>" \
  --header "X-Request-Id: 6f1c2b8e-3f43-4a51-9f39-2a1d7c0e5b10" \
  --data-binary @orders.csv

The body goes through the ordinary write path, with the same permissions and constraints as an INSERT statement. A request_id makes the load safe to retry.

Asynchronous statements

POST /v1/statements takes a JSON body:

{
  "sql": "select region, sum(amount) as amount from sales.orders group by region",
  "warehouse": "bi",
  "wait_timeout": 10
}

The request waits up to wait_timeout seconds (0–50, default 0). A small result that is ready in time comes back inline; otherwise the response carries the statement id to poll.

{
  "id": "<statement id>",
  "query_id": "1b0d9c62-6c4e-4b0e-9f0c-0e8f5f3e2a71",
  "state": "running",
  "progress": {"elapsed_ms": 1840, "rows": 0, "bytes": 0, "warehouse_wait_ms": 1200}
}
StateMeaning
queuedWaiting for warehouse capacity.
resumingWaiting while the warehouse starts.
runningExecuting.
succeededFinished; result or affected_rows is present.
failederror says why.
cancelledCancelled by DELETE or by its timeout.

A succeeded result has columns (name, SQL type, nullability) and either inline rows or links. Results larger than the inline limits are returned as links to Arrow IPC chunks, each with its row and byte counts, a SHA-256 and an expiry. Download the chunks in order. Links expire within a minute; poll the statement again for fresh links. Every download checks the caller's current permissions.

With truncate: true, a result over the inline limits returns its first rows with complete: false instead.

A statement can also be polled or cancelled by its query_id together with the X-Vega-Session-Id header. A cancellation that arrives before its statement is kept, and cancels the statement when it starts.

Sessions

Requests are independent by default: each one runs in its own short-lived context. To keep state across requests, set create_session: true on the first one and pass the returned session_id (also sent as X-Vega-Session-Id) on the next ones.

A session keeps USE, SET application_name, SET query_timeout_ms, the request's database, warehouse and settings, temporary views and an open transaction. One statement runs in a session at a time. Idle sessions close after the session timeout. A credential can use a session only if it carries at least the session's roles and scopes.

VEGA=(--header "X-API-Key: $VEGADB_API_KEY"
      --header "X-Vega-Organization: <organization id>"
      --header "X-Vega-Workspace: <workspace id>"
      --header "Content-Type: application/json")

# BEGIN in a new session; the response carries "session_id".
curl --request POST "https://<warehouse endpoint>/v1/statements" "${VEGA[@]}" \
  --data '{"sql": "BEGIN", "create_session": true}'

curl --request POST "https://<warehouse endpoint>/v1/statements" "${VEGA[@]}" \
  --data '{"sql": "UPDATE sales.orders SET status = '\''paid'\'' WHERE order_id = 42", "session_id": "<session id>", "request_id": "<uuid>"}'

curl --request POST "https://<warehouse endpoint>/v1/statements" "${VEGA[@]}" \
  --data '{"sql": "COMMIT", "session_id": "<session id>", "request_id": "<uuid>"}'

# Close the session.
curl --request DELETE "https://<warehouse endpoint>/v1/sessions/<session id>" "${VEGA[@]}"

EXPLAIN ANALYZE and a second BEGIN inside an open transaction return TRANSACTION_ACTIVE. See transactions and parameters.

Retries

Give each write a UUID request_id (or X-Request-Id) and keep the same SQL, input and context when you retry it.

  • A repeated request returns the recorded outcome of the first; the write is not applied twice.
  • The same ID with different SQL or input returns REQUEST_CONFLICT.
  • WRITE_CONFLICT and OUTCOME_UNKNOWN can be resubmitted with the same ID; VegaDB resolves whether the earlier attempt committed.
  • ROLLBACK discards the outcomes recorded for that transaction's statements, including its BEGIN. Those IDs then return TRANSACTION_ROLLED_BACK; use fresh IDs to run the work again.

Errors

A refused request returns an HTTP error status and {"error": {"code": "...", "message": "..."}}. A statement that fails reports error in its state. A write that committed but whose result could not be delivered reports result_error instead, so a client never mistakes a committed write for a failed one.

Codes are stable; messages are descriptive and can change.

CodeMeaning
SQL_SYNTAXThe statement does not parse.
SQL_CATALOGA catalog, schema, table or other object does not exist or already exists.
SQL_BINDINGA column, function or type does not resolve.
SQL_CONSTRAINTA constraint such as NOT NULL or PRIMARY KEY failed.
SQL_CONVERSION, SQL_RANGEA value cannot be converted, or is out of range.
DATA_ACCESS_DENIEDThe identity lacks permission on an object the statement uses.
QUERY_CANCELLEDThe statement was cancelled.
QUERY_TIMEOUTThe statement passed its timeout.
READ_ONLY_VIOLATIONA read_only request tried to change data or session state.
TRANSACTION_ACTIVEThe statement is not allowed inside an open transaction.
TRANSACTION_ROLLED_BACKThe request ID belongs to a rolled-back transaction.
WRITE_CONFLICTA concurrent write conflicted; retry with the same request ID.
OUTCOME_UNKNOWNResubmit with the same request ID to resolve the outcome.
REQUEST_CONFLICTThe request ID was used for a different request.
SESSION_BUSYAnother statement is running in the session.
SESSION_UNAVAILABLEThe session no longer exists; start a new one.
WAREHOUSE_SUSPENDEDThe warehouse was stopped explicitly and does not start on a query; start it first.
WAREHOUSE_RESUME_TIMEOUTThe warehouse did not start within its resume timeout.

Limits

GET /openapi.json lists the limits in force, under x-vega-limits: request body and statement size, inline result rows and bytes, export bytes for Arrow streams, concurrent statements, and connection limits. A result has at most 256 columns, and a text cell in JSON, JSONEachRow, CSV or TSV at most 1 MiB. Statement timeouts include time spent queued and waiting for the warehouse.

Warehouse control

Start, stop and inspect warehouses under /api/v1/vegadb/orgs/{org_id}/workspaces/{workspace_id}/warehouses/{warehouse_id}. See warehouses and scaling.

Vegalake, VegaDB and VegaFlow are trademarks or registered trademarks of Vegalake Inc.

On this page