Skip to content

API Reference

Clusters

List and fetch the Kubernetes clusters in your tenant. Filter by provider, region, environment, and status. Default sort is hourly run rate descending.

Last updated


Two endpoints cover the cluster surface: a paginated list with filters and a single-cluster lookup by UUID. Both return a Cluster body with metadata, capacity, utilization, and cost.

GET /v1/clusters returns the inventory across every cluster the API key is allowed to see. GET /v1/clusters/{cluster_id} returns one cluster's full body. Both require the clusters:read scope. Neither accepts ?cost_mode=; see Cost Modes for details.

Staleness

metadata.is_stale flips to true when the last agent heartbeat is older than 180 seconds or last_seen_at is null. While stale, utilization reflects the last successful agent snapshot, not live data.

  • Stale clusters are excluded from Organization.utilization.counts.connected_clusters. They still count toward clusters. See Organization.
  • Staleness is orthogonal to status. A cluster can be status=active and is_stale=true simultaneously — the agent has gone quiet without the cluster being marked disconnected. Filter on is_stale to detect this, not on status.

List clusters

GET /v1/clusters

Required scope: clusters:read

Returns the cluster inventory across the API key's allowed clusters. Default sort is cost.current_run_rate_hourly DESC.

cost.current_run_rate_hourly is the cluster's fully loaded cost over the last seven complete days (UTC), per hour. It is the same figure as the cluster's row in the organization dashboard's top_clusters, and the organization run rate is the sum of it over the clusters that reported in the last 15 minutes.

utilization.counts.workloads counts every workload kind, including custom kinds such as Argo Rollout, and equals total_count on /v1/clusters/{cluster_id}/workloads. The per-kind counts (deployments, statefulsets, daemonsets, jobs, cronjobs) cover the built-in kinds only, so they can add up to less than workloads.

Filters combine with AND. Each filter is a comma-separated list interpreted as an OR within that filter: provider=aws,gcp means "AWS or GCP", status=active,disconnected means "either status". Pass standard pagination via limit, cursor, and include_total; see Pagination & Filtering for the full cursor contract.

Query parameters

NameTypeDefaultAllowed valuesDescription
limitinteger1001..500Page size.
cursorstring--Opaque pagination token from a previous response.
include_totalbooleanfalsetrue, falseInclude meta.pagination.total_count. Opt-in.
sortstringcostcost, idcost puts the highest run rate first. id gives an order that holds while you walk every page. See Sort policy.
providerstring-aws, gcp, azure, on-prem (csv)Filter by cloud provider.
regionstring--Filter by region (csv).
environmentstring-production, non-production, staging, dev (csv)Filter by environment tag.
statusstring-pending, active, disconnected, error, discovered (csv)Filter by cluster status. Lowercase only.

Enum filters are case-sensitive: provider=AWS returns a 422, not an empty list. The error response echoes the allowed set under error.details[0].allowed.

Example request

bash
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  "https://public-api.kubeadapt.io/v1/clusters?provider=aws,gcp&status=active&limit=20&include_total=true"

Example response

json
1{
2  "data": [
3    {
4      "id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890",
5      "kind": "Cluster",
6      "metadata": {
7        "name": "prod-eu-west-1",
8        "provider": "aws",
9        "service": "EKS",
10        "region": "eu-west-1",
11        "availability_zones": ["eu-west-1a", "eu-west-1b", "eu-west-1c"],
12        "environment": "production",
13        "status": "active",
14        "is_stale": false,
15        "k8s_version": "1.29.5",
16        "agent_version": "1.18.2",
17        "discovery_source": "agent",
18        "created_at": "2025-11-08T14:22:31Z",
19        "last_seen_at": "2026-05-21T10:29:42Z"
20      },
21      "capacity": {
22        "cpu": { "total_cores": 256.0, "allocatable_cores": 240.8 },
23        "memory": { "total_bytes": 1099511627776, "allocatable_bytes": 1023820300288 },
24        "gpu": { "total": 8, "allocatable": 8, "model": "NVIDIA A10G" },
25        "storage": { "total_bytes": 21474836480000 },
26        "pods": { "allocatable": 7392 }
27      },
28      "utilization": {
29        "cpu": { "requested_cores": 184.2, "used_cores": 142.7, "utilization_percent": 55.8 },
30        "memory": { "requested_bytes": 769527797760, "used_bytes": 612918427648, "utilization_percent": 55.7 },
31        "gpu": { "utilization_percent": 62.4, "memory_used_bytes": 12884901888, "memory_total_bytes": 21474836480 },
32        "counts": {
33          "nodes": 32,
34          "namespaces": 24,
35          "workloads": 412,
36          "deployments": 287,
37          "statefulsets": 64,
38          "daemonsets": 12,
39          "jobs": 38,
40          "cronjobs": 11,
41          "pods": 1247,
42          "running_pods": 1247,
43          "containers": 2891,
44          "running_containers": 2891,
45          "persistent_volumes": 94
46        }
47      },
48      "cost": {
49        "current_run_rate_hourly": { "amount": "147.2400", "currency": "USD" },
50        "last_updated_at": "2026-05-21T10:29:48Z"
51      }
52    },
53    {
54      "id": "a8d3e1b2-f4c6-4789-9bde-2345678901cd",
55      "kind": "Cluster",
56      "metadata": {
57        "name": "staging-us-central1",
58        "provider": "gcp",
59        "service": "GKE",
60        "region": "us-central1",
61        "availability_zones": ["us-central1-a", "us-central1-b"],
62        "environment": "staging",
63        "status": "active",
64        "is_stale": false,
65        "k8s_version": "1.30.1",
66        "agent_version": "1.18.2",
67        "discovery_source": "agent",
68        "created_at": "2026-02-17T11:04:09Z",
69        "last_seen_at": "2026-05-21T10:29:51Z"
70      },
71      "capacity": {
72        "cpu": { "total_cores": 96.0, "allocatable_cores": 88.4 },
73        "memory": { "total_bytes": 412316860416, "allocatable_bytes": 381344419840 },
74        "gpu": { "total": 0, "allocatable": 0, "model": "" },
75        "storage": { "total_bytes": 4294967296000 },
76        "pods": { "allocatable": 2640 }
77      },
78      "utilization": {
79        "cpu": { "requested_cores": 41.6, "used_cores": 28.9, "utilization_percent": 32.7 },
80        "memory": { "requested_bytes": 142473041920, "used_bytes": 98248129024, "utilization_percent": 25.8 },
81        "gpu": { "utilization_percent": 0.0, "memory_used_bytes": 0, "memory_total_bytes": 0 },
82        "counts": {
83          "nodes": 12,
84          "namespaces": 9,
85          "workloads": 88,
86          "deployments": 62,
87          "statefulsets": 14,
88          "daemonsets": 8,
89          "jobs": 3,
90          "cronjobs": 1,
91          "pods": 234,
92          "running_pods": 234,
93          "containers": 481,
94          "running_containers": 481,
95          "persistent_volumes": 22
96        }
97      },
98      "cost": {
99        "current_run_rate_hourly": { "amount": "42.1800", "currency": "USD" },
100        "last_updated_at": "2026-05-21T10:29:52Z"
101      }
102    }
103  ],
104  "meta": {
105    "request_id": "0196f1c2-2726-7836-82f8-3582a72ede3d",
106    "applied_at": "2026-05-21T10:30:00Z",
107    "pagination": {
108      "next_cursor": "eyJ2IjoxLCJhZnRlcl9pZCI6ImE4ZDNlMWIyLWY0YzYtNDc4OS05YmRlLTIzNDU2Nzg5MDFjZCIsImFmdGVyX3ZhbHVlIjoiNDIuMTgwMCIsInF1ZXJ5X2hhc2giOiJhM2I0YzVkNmU3ZjhnOWEwYjFjMmQzZTRmNWc2IiwiaXNzdWVkX2F0IjoxNzE2Mjg1MDAwfQ",
109      "has_more": true,
110      "limit": 20,
111      "total_count": 6
112    }
113  }
114}

Two elements shown for brevity. next_cursor is displayed for shape clarity; a single-page response sets has_more: false and either omits next_cursor or returns it as an empty string.

Example error response

json
1{
2  "data": null,
3  "meta": {
4    "request_id": "0196f1c2-d3ef-7354-ad01-f652bf3e0044",
5    "applied_at": "2026-05-21T10:30:01Z"
6  },
7  "error": {
8    "code": "CLUSTER_NOT_FOUND",
9    "message": "cluster not found"
10  }
11}

That code comes back from GET /v1/clusters/{cluster_id} when the UUID is well-formed but no cluster in your tenant matches. See Error Handling for the full mapping.

Common errors

HTTPerror.codeWhen
400BAD_REQUESTMalformed query, bad limit.
400INVALID_CURSORcursor couldn't be decoded.
401UNAUTHORIZEDMissing or invalid Bearer token.
403FORBIDDENToken lacks the clusters:read scope.
410CURSOR_EXPIREDCursor older than 24h or query parameters changed since it was issued.
422VALIDATION_ERROREnum filter value isn't in the allowed set (e.g. provider=aliyun, status=Active).
422INVALID_COST_MODE?cost_mode= was supplied, not accepted on this endpoint.
429RATE_LIMITEDPer-key quota exceeded.
500INTERNAL_ERRORServer-side failure.

Get cluster by ID

GET /v1/clusters/{cluster_id}

Required scope: clusters:read

Returns one cluster's full body, the same shape as a data[] element on the list endpoint.

Path parameters

NameTypeDescription
cluster_idUUIDCluster identifier. Must be a valid UUID, INVALID_CLUSTER_ID (400) otherwise.

Example request

bash
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  "https://public-api.kubeadapt.io/v1/clusters/b7e22c91-8d34-4f5a-9c1e-0d8e3a4b5f76"

Example response

Returns a single Cluster object. Same shape as one element of the List clusters response above, wrapped directly under data (no array, no pagination in meta).

json
1{
2  "data": {
3    "id": "b7e22c91-8d34-4f5a-9c1e-0d8e3a4b5f76",
4    "kind": "Cluster",
5    "metadata": { "name": "prod-us-east-1", "provider": "aws", "service": "EKS", "region": "us-east-1", "environment": "production", "status": "active", "is_stale": false, "...": "..." },
6    "capacity": { "...": "..." },
7    "utilization": { "...": "..." },
8    "cost": { "current_run_rate_hourly": { "amount": "98.1200", "currency": "USD" }, "last_updated_at": "2026-05-21T10:29:48Z" }
9  },
10  "meta": {
11    "request_id": "0196f1c2-6f66-7b41-950f-ac95aadb92a3",
12    "applied_at": "2026-05-21T10:30:00Z"
13  }
14}

For the complete field list, see the canonical example in List clusters or the OpenAPI spec.

Common errors

HTTPerror.codeWhen
400INVALID_CLUSTER_IDcluster_id isn't a valid UUID.
401UNAUTHORIZEDMissing or invalid Bearer token.
403FORBIDDENToken lacks the clusters:read scope.
403CLUSTER_ACCESS_DENIEDUUID is valid and the cluster exists, but the API key's allowlist excludes it.
404CLUSTER_NOT_FOUNDUUID is well-formed but no cluster in the tenant matches.
422INVALID_COST_MODE?cost_mode= was supplied.
429RATE_LIMITEDPer-key quota exceeded.
500INTERNAL_ERRORServer-side failure.

See also

  • Namespaces, drill from a cluster into its namespaces and their cost breakdown.
  • Workloads, list workloads scoped to one cluster via /v1/clusters/{cluster_id}/workloads.
  • Nodes, node inventory and node-group aggregates per cluster.
  • Organization, the tenant-wide totals; connected_clusters excludes stale ones.
  • Cost Trends, hourly/daily/weekly/monthly cost time series for a single cluster.