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>/apiSo 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
- In the KubeSense UI, go to Settings → API Key Management.
- Generate a new key and give it a descriptive label (e.g.
inhouse-log-exporter). - 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.
| Concern | How it's passed |
|---|---|
| Time range | Query params from_time and to_time, in RFC3339Nano UTC (e.g. 2026-05-27T00:00:00Z). Required on list/count endpoints. |
| Cluster scoping | Query param cluster, repeatable: ?cluster=clusterA&cluster=clusterB. Omit to span all clusters the key can access. |
| Pagination | page (1-based) and page_size, as query params and/or in the JSON body. |
| Filters | JSON request body (see Filtering). |
| Content type | Content-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
| Method | Path | Description |
|---|---|---|
POST | /api/logs | Search and page through logs. |
POST | /api/logs/count | Total and error log counts for the query. |
POST | /api/logs/timeseries | Log volume by level over time. |
POST | /api/logs/filters | Available faceted filters (type, source, namespace, workload, node…). |
POST | /api/logs/filter/search | Search within a filter's values. |
POST | /api/logs/interestingfields | Auto-extracted field patterns. |
POST | /api/logs/interestingfields/values | Values for an interesting field. |
GET | /api/logs/:id | Full detail for a single log. |
POST | /api/logs/export | Export logs (CSV/JSON). |
GET | /api/logs/kube/heirarchy | Cluster → 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
| Field | Type | Description |
|---|---|---|
filters | object | Filter set — see Filtering. |
required_fields | array | Optional list of specific columns/attributes to return. Omit for the default column set. |
sort_by | string | Field to sort by (e.g. timestamp). |
sort_order | string | asc or desc. |
page | number | 1-based page number. |
page_size | number | Rows per page. |
Traces API
| Method | Path | Description |
|---|---|---|
POST | /api/traces | Search and page through traces/spans. |
POST | /api/traces/count | Trace count for the query. |
POST | /api/traces/filter | Available faceted filters. |
POST | /api/traces/filter/search | Search within a filter's values. |
POST | /api/traces/summary | Aggregated trace summary (request/error/latency). |
POST | /api/traces/stats | Request/error/latency time series. |
GET | /api/traces/:id | Full detail for a single trace. |
GET | /api/traces/:id/attributes | Attributes for a trace. |
GET | /api/traces/tracemap | Service/span dependency map. |
GET | /api/traces/spangroups | Grouped spans. |
POST | /api/traces/comparison | Compare two time windows. |
POST | /api/traces/export | Export traces. |
GET | /api/traces/kube/heirarchy | Cluster → 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:
| Field | Description |
|---|---|
field | Column or attribute name to filter on. See the Log & Trace Fields reference for every available column. |
operation | Comparison operator (see below). |
values | Array of values to match. |
type | Value type — string for text/attributes, numeric for float attributes. |
Common operations:
| Operation | Meaning |
|---|---|
EQ / NEQ | Equals / not equals |
IN / NIN | In / not in a set of values |
LIKE / ILIKE | Substring match (case-sensitive / insensitive) |
HAS_TOKEN | Full-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.
| Method | Path | Description |
|---|---|---|
POST | /api/logs/spl/execute | Run an SPL query and return result rows. |
POST | /api/logs/spl/validate | Check whether a query is syntactically valid. |
POST | /api/logs/spl/explain | Return 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
| Field | Type | Description |
|---|---|---|
query | string | The SPL query. |
clusters | array | One or more cluster names to scope the query. |
from_time | string | Start of the time range, RFC3339Nano UTC. |
to_time | string | End 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 50Error 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 descTop 10 noisiest namespaces:
filter level = "ERROR"
| top 10 namespaceBare 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.urlSee the SPL Reference for the full command and function list, and Query Jobs for running long SPL queries as asynchronous, exportable jobs.
Errors
| Status | Meaning |
|---|---|
400 | Invalid query params or body (e.g. missing/badly formatted from_time). |
401 | Missing, invalid, or expired API key — or the key's user lacks permission. |
500 | Server 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.