Kubesense

Logs & Traces API

Programmatically query KubeSense logs and traces from your own tools using an API key.

KubeSense exposes the same logs and traces data you see in the UI through a REST API. This lets in-house tools, scripts, and integrations pull logs and traces programmatically — authenticating with an API key instead of an interactive login.

This guide covers authentication, request conventions, and the /logs and /traces endpoints. For querying metrics, see the Metrics API.

Base URL

All endpoints are served under the /api prefix on your KubeSense host:

https://<your-kubesense-host>/api

So the logs endpoint is https://<your-kubesense-host>/api/logs and traces is https://<your-kubesense-host>/api/traces.

note: Only paths under /api/* are exposed externally through the ingress. Use your KubeSense web address as the host.

Authentication

External tools authenticate with an API key passed in the X-API-Key header. Every request to a logs or traces endpoint must include it:

X-API-Key: <your-api-key>

Creating an API key

  1. In the KubeSense UI, go to Settings → API Key Management.
  2. Generate a new key and give it a descriptive label (e.g. inhouse-log-exporter).
  3. Copy the key immediately — it is shown only once at creation time. Store it as a secret in your tool.

warning: An API key inherits the role and permissions of the user who created it. To read logs the key's user needs Logs module access, and to read traces it needs Traces module access (including any cluster/namespace RBAC scoping). Create the key under a user with the right permissions for the data your tool should see.

Verifying access

A quick way to confirm the key works:

curl -s "https://<your-kubesense-host>/api/logs" \
  -H "X-API-Key: $KUBESENSE_API_KEY"

A 401 Unauthorized with "message": "invalid api key" means the key is wrong or expired. A 200 with a hierarchy payload means you're in.

Request conventions

Both APIs follow the same patterns.

ConcernHow it's passed
Time rangeQuery params from_time and to_time, in RFC3339Nano UTC (e.g. 2026-05-27T00:00:00Z). Required on list/count endpoints.
Cluster scopingQuery param cluster, repeatable: ?cluster=clusterA&cluster=clusterB. Omit to span all clusters the key can access.
Paginationpage (1-based) and page_size, as query params and/or in the JSON body.
FiltersJSON request body (see Filtering).
Content typeContent-Type: application/json for POST requests.

Response envelope

Responses are wrapped in a consistent envelope:

{
  "data": { },
  "error": false,
  "message": "fetched logs successfully"
}

On failure, error is true and message describes the problem. Auth failures return HTTP 401; malformed requests return 400; query timeouts return 500 with a warning message.

Logs API

MethodPathDescription
POST/api/logsSearch and page through logs.
POST/api/logs/countTotal and error log counts for the query.
POST/api/logs/timeseriesLog volume by level over time.
POST/api/logs/filtersAvailable faceted filters (type, source, namespace, workload, node…).
POST/api/logs/filter/searchSearch within a filter's values.
POST/api/logs/interestingfieldsAuto-extracted field patterns.
POST/api/logs/interestingfields/valuesValues for an interesting field.
GET/api/logs/:idFull detail for a single log.
POST/api/logs/exportExport logs (CSV/JSON).
GET/api/logs/kube/heirarchyCluster → namespace → workload hierarchy.

Fetch logs

POST /api/logs — time range and pagination go in the query string; filters and field selection go in the JSON body.

curl -s -X POST \
  "https://<your-kubesense-host>/api/logs?from_time=2026-05-27T00:00:00Z&to_time=2026-05-27T01:00:00Z&cluster=mpokket-neo-prod-eks-cluster" \
  -H "X-API-Key: $KUBESENSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "type": "common",
      "common_filter": [
        { "field": "namespace", "operation": "IN", "values": ["payments"], "type": "string" }
      ]
    },
    "sort_by": "timestamp",
    "sort_order": "desc",
    "page": 1,
    "page_size": 50
  }'

To pull all logs in the window, send an empty filter set:

{ "filters": { "common_filter": [] }, "page_size": 100 }

The response data contains the matched log rows plus total_logs_count and error_count. Page through results by incrementing page.

Request body fields

FieldTypeDescription
filtersobjectFilter set — see Filtering.
required_fieldsarrayOptional list of specific columns/attributes to return. Omit for the default column set.
sort_bystringField to sort by (e.g. timestamp).
sort_orderstringasc or desc.
pagenumber1-based page number.
page_sizenumberRows per page.

Traces API

MethodPathDescription
POST/api/tracesSearch and page through traces/spans.
POST/api/traces/countTrace count for the query.
POST/api/traces/filterAvailable faceted filters.
POST/api/traces/filter/searchSearch within a filter's values.
POST/api/traces/summaryAggregated trace summary (request/error/latency).
POST/api/traces/statsRequest/error/latency time series.
GET/api/traces/:idFull detail for a single trace.
GET/api/traces/:id/attributesAttributes for a trace.
GET/api/traces/tracemapService/span dependency map.
GET/api/traces/spangroupsGrouped spans.
POST/api/traces/comparisonCompare two time windows.
POST/api/traces/exportExport traces.
GET/api/traces/kube/heirarchyCluster → namespace → workload hierarchy.

Fetch traces

POST /api/traces — same shape as logs: time range and pagination in the query string, filters in the body.

curl -s -X POST \
  "https://<your-kubesense-host>/api/traces?from_time=2026-05-27T00:00:00Z&to_time=2026-05-27T01:00:00Z&cluster=mpokket-neo-prod-eks-cluster&page=1&page_size=50" \
  -H "X-API-Key: $KUBESENSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": {
      "type": "common",
      "common_filter": [
        { "field": "workload", "operation": "IN", "values": ["checkout-service"], "type": "string" }
      ]
    },
    "operator": "AND",
    "sort_by": "timestamp",
    "sort_direction": "desc",
    "page": 1,
    "page_size": 50
  }'

Filtering

Both APIs use the same filter object. The common case is a flat list of conditions in common_filter:

{
  "filters": {
    "type": "common",
    "common_filter": [
      { "field": "namespace", "operation": "IN",  "values": ["payments"], "type": "string" },
      { "field": "level",     "operation": "IN",  "values": ["error"],    "type": "string" }
    ]
  }
}

Each condition has:

FieldDescription
fieldColumn or attribute name to filter on. See the Log & Trace Fields reference for every available column.
operationComparison operator (see below).
valuesArray of values to match.
typeValue type — string for text/attributes, numeric for float attributes.

Common operations:

OperationMeaning
EQ / NEQEquals / not equals
IN / NINIn / not in a set of values
LIKE / ILIKESubstring match (case-sensitive / insensitive)
HAS_TOKENFull-text token match

Multiple conditions combine with the operator (AND / OR). For nested boolean logic, use the adv_filters object instead of common_filter. The easiest way to discover valid field names and values is to call POST /api/logs/filters (or /api/traces/filter), which returns the same facets the Explorer UI shows.

Advanced querying (SPL)

For analytics-style queries — aggregations, field extraction, time-series — KubeSense supports Search Processing Language (SPL), a pipeline query language over the same logs data. SPL queries are a series of commands joined by |, where each command's output feeds the next.

MethodPathDescription
POST/api/logs/spl/executeRun an SPL query and return result rows.
POST/api/logs/spl/validateCheck whether a query is syntactically valid.
POST/api/logs/spl/explainReturn the compiled SQL and parsed commands for a query.

note: Unlike /api/logs, the SPL endpoints take the time range and clusters in the JSON body, not as query params. query, clusters (at least one), from_time, and to_time are all required.

Request body fields

FieldTypeDescription
querystringThe SPL query.
clustersarrayOne or more cluster names to scope the query.
from_timestringStart of the time range, RFC3339Nano UTC.
to_timestringEnd of the time range, RFC3339Nano UTC.

Run a query

curl -s -X POST \
  "https://<your-kubesense-host>/api/logs/spl/execute" \
  -H "X-API-Key: $KUBESENSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "fields @timestamp, workload, level, body | filter level = \"ERROR\" | sort @timestamp desc | limit 50",
    "clusters": ["mpokket-neo-prod-eks-cluster"],
    "from_time": "2026-05-27T00:00:00Z",
    "to_time": "2026-05-27T01:00:00Z"
  }'

The result is column-oriented — a list of column names plus an array of row arrays:

{
  "error": false,
  "message": "query executed successfully",
  "data": {
    "columns": ["@timestamp", "workload", "level", "body"],
    "rows": [
      ["2026-05-27T00:59:12.4Z", "checkout-service", "ERROR", "payment gateway timeout"]
    ],
    "count": 1
  }
}

Example queries

Filter for errors in a namespace, newest first:

fields @timestamp, body, level
| filter namespace = "payments" and level = "ERROR"
| sort @timestamp desc
| limit 50

Error rate per workload (aggregation):

fields workload as application_name, level as severity
| filter severity in ["ERROR", "WARN", "INFO"]
| stats count(*) as total, sum(severity = "ERROR") as errors by application_name
| eval error_rate = errors / total
| sort error_rate desc

Top 10 noisiest namespaces:

filter level = "ERROR"
| top 10 namespace

Bare keyword search (case-insensitive substring match on the log body):

"timeout" and "payment"

Access parsed fields from structured logs with the log_processed. prefix:

filter log_processed.status = "500"
| stats count(*) as total by log_processed.url

See the SPL Reference for the full command and function list, and Query Jobs for running long SPL queries as asynchronous, exportable jobs.

Errors

StatusMeaning
400Invalid query params or body (e.g. missing/badly formatted from_time).
401Missing, invalid, or expired API key — or the key's user lacks permission.
500Server error, or a query timeout (message will indicate a timeout).

note: If a tool needs only a subset of clusters or namespaces, scope the API key's user with RBAC rather than filtering client-side — the key will only ever return data it's permitted to see.