HTTP API

Use the ScopeDB Cloud HTTP API from backend services, jobs, integrations, or languages without a dedicated SDK. The customer data API consists of the ingest endpoint and the statement endpoint family documented on this page.

Base URL and authentication

Copy the ScopeDB API address from Connect in ScopeDB Console, then set it with an API key for the same workspace:

export SCOPEDB_ENDPOINT="https://<endpoint>"
export SCOPEDB_API_KEY="<api-key>"

Send the key as a bearer token on every request:

Authorization: Bearer <api-key>

Keep API keys in server-side environments. Do not ship them in browser or mobile clients.

Endpoints

MethodPathUse
POST/v1/ingestTransform and insert JSON rows.
POST/v1/statementsSubmit a ScopeQL statement.
GET/v1/statements/{statement_id}Read statement status and, when ready, its result.
POST/v1/statements/{statement_id}/cancelRequest cancellation of a statement.

Submit a statement

curl -X POST "$SCOPEDB_ENDPOINT/v1/statements" \
  -H "Authorization: Bearer $SCOPEDB_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "statement": "SELECT 1 AS ok",
  "format": "json"
}
JSON

The request body accepts these fields:

FieldRequiredDescription
statementYesScopeQL text to execute.
formatYesResult format. Use json for the response shape documented here.
statement_idNoA caller-selected UUID. ScopeDB creates one when this field is omitted.
exec_timeoutNoTime budget measured from submission, including time spent pending and executing. Use a fixed-duration ISO 8601 value such as PT30S.

The response represents the statement's current state. Its common fields are:

FieldDescription
statement_idUUID used to poll or cancel the statement.
statusOne of the five states below.
created_atTimestamp for the statement.
progressCompletion and scan counters reported while the statement runs.
result_setPresent when status is finished.
messagePresent when status is failed or cancelled.

Statement states

StateMeaningWhat the client should do
pendingAccepted and waiting to run.Poll the statement ID.
runningExecution is in progress.Continue polling with a delay.
finishedExecution completed successfully.Read result_set.
failedExecution ended with an error.Read message; fix the statement or input before retrying unless the failure is transient.
cancelledExecution ended after cancellation.Read message and stop polling.

finished, failed, and cancelled are terminal. An HTTP 2xx response only means the API request succeeded; it does not make failed or cancelled statements successful. Always inspect status.

Poll a statement

Poll only while the submit response is pending or running:

curl "$SCOPEDB_ENDPOINT/v1/statements/<statement-id>?format=json" \
  -H "Authorization: Bearer $SCOPEDB_API_KEY"

Use an increasing delay with jitter instead of a tight loop. Keep the statement_id with application logs or active job state so later polling and support reports can be correlated.

If the submit response is already terminal, consume that response directly. Statements that finish within the submit request are not necessarily retained for a later GET. In particular, a VALUES ... INSERT INTO statement returns its terminal result without creating a pollable statement record.

For JSON results, result_set has this shape:

{
  "metadata": {
    "fields": [
      { "name": "ok", "data_type": "int" }
    ],
    "num_rows": 1
  },
  "format": "json",
  "rows": [
    ["1"]
  ]
}

metadata.fields defines the column order and type. Each entry in rows is an array in that order. JSON cell values are strings or null; convert them using the corresponding data_type in your client.

Cancel a statement

curl -X POST \
  "$SCOPEDB_ENDPOINT/v1/statements/<statement-id>/cancel" \
  -H "Authorization: Bearer $SCOPEDB_API_KEY"

The response includes statement_id, its current terminal status, message, and created_at. Treat cancellation as a request and inspect the returned state; the statement may already have finished or failed.

Ingest JSON rows

POST /v1/ingest inserts JSON rows through a ScopeQL transformation that ends with INSERT INTO. See Ingest data for the current runnable workflow and request shape.

Send a complete, buffered JSON request body with a known Content-Length. Streaming or chunked request bodies are not currently supported by this endpoint. Split large datasets into batches, and reduce the batch size if the API returns 413.

A successful response reports the rows inserted:

{
  "num_rows_inserted": 2
}

If the connection ends before this response arrives, the ingest outcome may be unknown. Retrying the same batch can insert it again; do not retry blindly.

Errors and retries

API errors use one JSON envelope:

{
  "error": {
    "message": "unprocessable entity"
  },
  "request_id": "6c2bd761-9695-44c7-9723-215f618d1199"
}

Use the HTTP status for retry decisions. Preserve the X-Request-ID response header when reporting a problem; its value is also returned as request_id in the body. error.message is diagnostic and its exact wording may evolve, so do not branch application logic on the message text.

Statement execution failures can instead arrive in-band as a failed or cancelled statement state with an HTTP 2xx response. Always combine the HTTP status with the statement state:

ResponseGuidance
400Fix the request body or required headers before retrying.
401Correct the API key; do not retry unchanged credentials.
403Check the API address, API key, and intended workspace.
404Check the path or statement ID. For an ambiguous mutating submission, see below.
413Reduce the request body or ingest batch.
415Use supported Content-Type and Content-Encoding values.
422Fix ingest row decoding or the ingest transformation before retrying.
429Retry with exponential backoff and jitter; honor Retry-After when present.
5xxRetry polling reads with backoff. Treat write outcomes as unknown, as described below.

Polling GET requests can be retried with exponential backoff and jitter.

Do not automatically retry POST /v1/ingest or a statement that mutates data after a connection interruption or 5xx. The write may have completed even when the response was not received. A caller-selected statement_id helps correlate polling and logs, but it is not an idempotency key, and a later 404 does not prove that the original mutation did not execute. Reconcile the application's data before deciding whether to retry. For ingest, use stable business identifiers and application-level deduplication when replays matter.