Skip to main content

API Documentation

Overview

Observability API for ingesting and querying telemetry data. This API allows you to ingest application metrics, OpenTelemetry logs and traces, then query the collected data using SQL.

Server URL: https://<SERVER_URL> — Search (SQL query)

Engine URL: https://<ENGINE_URL> — Ingestion

These can differ by deployment. Use the Server URL for SQL query requests and the Engine URL for all ingestion endpoints.

Authentication

Seeker (SQL query) — use a Bearer token against the Server URL:

Authorization: Bearer <YOUR_API_KEY>

POST /api/v1/sql accepts the workspace API_KEY as a Bearer token.

Engine (ingestion) — use Basic auth against the Engine URL:

Authorization: Basic <YOUR_API_KEY>

Endpoints

SQL Query

Execute a SQL query against ingested telemetry data.

Request

POST /api/v1/sql

Query Parameters

NameTypeRequiredDescription
typestringNoDataset type: logs (default), traces, or metrics

Request Body

FieldTypeRequiredDescription
querystringYesSQL query to execute (max 5000 characters)
fromintegerYesStart timestamp (Unix seconds).
tointegerYesEnd timestamp (Unix seconds).

The dataset name is taken from the SQL FROM clause. When authenticated with the API_KEY, access is unrestricted (admin).

Recommendation: Always include a LIMIT clause in your SQL query. Avoid querying more than 7 days in a single request — large time ranges can trigger expensive unbounded scans. For longer windows, split the query into smaller time-range partitions (for example, daily or weekly) and combine the results.

Example Request

curl -X POST "https://<SERVER_URL>/api/v1/sql?type=logs" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "SELECT * FROM application_logs WHERE level = '\''ERROR'\'' LIMIT 100",
"from": 1705756200,
"to": 1705759800
}'

Response - Success

{
"matches": [
{
"timestamp": "2024-01-20T10:30:00Z",
"level": "ERROR",
"message": "Connection failed",
"service": "payment-api"
},
{
"timestamp": "2024-01-20T10:31:00Z",
"level": "ERROR",
"message": "Database timeout",
"service": "payment-api"
}
],
"total": 156,
"offset": 0,
"limit": 100,
"scanSize": 1048576,
"trace_id": "",
"partial": false
}

Response Fields

FieldTypeDescription
matchesarrayMatching result rows
totalnumberTotal number of results found
offsetnumberOffset used in the query
limitnumberPage size used in the query
scanSizenumberBytes scanned to produce the result
trace_idstringQuery trace identifier (when available)
partialbooleanWhether the response is a partial result set

Error Response Examples

Invalid or unrecognized API token:

{
"error": "token not found"
}

Malformed request body:

{
"error": "Invalid request body"
}

Empty query:

{
"error": "query cannot be empty"
}

No extractable table in the SQL FROM clause:

{
"error": "no table found in FROM clause"
}

Query longer than 5000 characters:

{
"error": "query too long"
}

Invalid type query parameter:

{
"error": "invalid dataset type"
}

Invalid SQL or engine errors:

{
"error": "internal server error"
}

Dataset access denied:

{
"error": "user not allowed access to dataset: application_logs"
}

Status Codes

CodeDescription
200Query successful
400Invalid request body, empty query, missing FROM table, query too long, or bad type
401Unauthorized — invalid token (token not found)
403Forbidden — dataset access denied
500Engine/runtime query failure (internal server error)

Ingest - JSON

Ingest JSON data to a stream.

Request

POST /api/{org_id}/{stream_name}/_json_evolving

Path Parameters

NameTypeRequiredDescription
org_idstringYesOrganization ID
stream_namestringYesStream name to ingest into

Request Body

JSON object or array of JSON objects. Schema is inferred and evolved based on incoming data.

Use _timestamp for the event time. It must be a Unix timestamp in microseconds. If _timestamp is omitted or not in Unix microseconds, the CtrlB engine sets it to the time of ingestion.

Example Request

curl -X POST "https://<ENGINE_URL>/api/default/application_logs/_json_evolving" \
-H "Authorization: Basic YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"_timestamp": 1705746600000000,
"level": "ERROR",
"message": "Database connection failed",
"service": "payment-api",
"trace_id": "abc123def456"
}'

Response - Success

OK

Error Response Examples

Invalid JSON:

{
"error": "Failed to parse JSON",
"code": "BAD_REQUEST",
"details": "Invalid JSON at line 1, column 15"
}

Organization not found:

{
"error": "Organization not found",
"code": "NOT_FOUND",
"details": "Organization 'default' does not exist"
}

Status Codes

CodeDescription
200Data ingested successfully
400Invalid JSON format
401Unauthorized
403Forbidden - no write permission
404Organization or stream not found
429Rate limit exceeded
500Internal server error

Ingest - OpenTelemetry Logs

Ingest OpenTelemetry logs.

Request

POST /api/{org_id}/{stream_name}/_otel/v1/logs

Path Parameters

NameTypeRequiredDescription
org_idstringYesOrganization ID
stream_namestringYesStream name to ingest into

Request Body

OpenTelemetry Protocol (OTLP) logs in JSON or protobuf format. Payload should conform to the OpenTelemetry specification.

Example Request

curl -X POST "https://<ENGINE_URL>/api/default/otel_logs/_otel/v1/logs" \
-H "Authorization: Basic YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"resourceLogs": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": { "stringValue": "payment-service" }
}
]
},
"scopeLogs": [
{
"scope": {
"name": "my-instrumentation"
},
"logRecords": [
{
"timestamp": 1642598400000000000,
"severityNumber": 13,
"severityText": "ERROR",
"body": { "stringValue": "Payment processing failed" },
"attributes": [
{
"key": "transaction_id",
"value": { "stringValue": "txn_12345" }
}
]
}
]
}
]
}
]
}'

Response - Success

Plain text response:

OK

Error Response Examples

Invalid JSON format:

{
"error": "Failed to decode OTel JSON",
"code": "BAD_REQUEST",
"details": "Invalid field value in resourceLogs[0]"
}

Invalid protobuf format:

{
"error": "Failed to decode OTel protobuf",
"code": "BAD_REQUEST",
"details": "Protobuf deserialization error at offset 42"
}

Status Codes

CodeDescription
200Logs processed
400Invalid OTEL format (JSON/protobuf decode error)
401Unauthorized
403Forbidden - no write permission
404Organization or stream not found
429Rate limit exceeded
500Internal server error

Ingest - OpenTelemetry Traces

Ingest OpenTelemetry traces.

Request

POST /api/{org_id}/v1/traces

Path Parameters

NameTypeRequiredDescription
org_idstringYesOrganization ID

Request Body

OpenTelemetry Protocol (OTLP) traces in JSON or protobuf format. Payload should conform to the OpenTelemetry specification.

Example Request

curl -X POST "https://<ENGINE_URL>/api/default/v1/traces" \
-H "Authorization: Basic YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"resourceSpans": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": { "stringValue": "api-server" }
}
]
},
"scopeSpans": [
{
"scope": {
"name": "my-instrumentation"
},
"spans": [
{
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"name": "payment_processing",
"startTimeUnixNano": 1642598400000000000,
"endTimeUnixNano": 1642598400000500000,
"status": { "code": 0 }
}
]
}
]
}
]
}'

Response - Success

Plain text response:

OK

Error Response Examples

Invalid JSON format:

{
"error": "Failed to decode OTel JSON",
"code": "BAD_REQUEST",
"details": "Invalid traceId format"
}

Invalid protobuf format:

{
"error": "Failed to decode OTel protobuf",
"code": "BAD_REQUEST",
"details": "Unexpected EOF while decoding spans"
}

Status Codes

CodeDescription
200Traces processed
400Invalid OTEL format (JSON/protobuf decode error)
401Unauthorized
403Forbidden - no write permission
404Organization not found
429Rate limit exceeded
500Internal server error

cURL

# SQL Query
curl -X POST "https://<SERVER_URL>/api/v1/sql?type=logs" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "SELECT * FROM my_stream LIMIT 10", "from": 1705756200, "to": 1705759800}'

# Ingest JSON
curl -X POST "https://<ENGINE_URL>/api/default/my_stream/_json_evolving" \
-H "Authorization: Basic YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"_timestamp": 1705746600000000, "level": "ERROR", "message": "Failed"}'

# Ingest OTel Logs
curl -X POST "https://<ENGINE_URL>/api/default/otel_logs/_otel/v1/logs" \
-H "Authorization: Basic YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"resourceLogs": [...]}'

# Ingest OTel Traces
curl -X POST "https://<ENGINE_URL>/api/default/v1/traces" \
-H "Authorization: Basic YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"resourceSpans": [...]}'

Support

For questions or issues, contact: support@ctrlb.ai