API Reference
Nodes
List, filter, and inspect Kubernetes nodes across every connected cluster, including instance type, spot status, capacity, and physical hourly cost.
Three endpoints cover the node surface: a cross-cluster flat list, a per-cluster scoped list, and a single-node lookup by Kubernetes metadata.uid. Filters include is_spot, instance_type, node_group, architecture, and capacity_type.
List nodes (cross-cluster)
GET /v1/nodes
Required scope: nodes:read
A flat, cost-sorted inventory of every node the API key can see, across every cluster in the allow-list.
Default sort is cost.current_run_rate_hourly descending.
Query parameters
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
limit | integer | 100 | 1..500 | Page size. |
cursor | string | (none) | (any) | Opaque pagination token from a previous response. |
include_total | boolean | false | true, false | Include meta.pagination.total_count. |
cluster_id | string (csv) | (none) | UUIDs | Scope to one or more clusters. |
node_group | string (csv) | (none) | (any) | Filter by node-group label value. |
instance_type | string (csv) | (none) | (any) | Filter by cloud instance type. Singular here; the response field on a node-group is the plural array metadata.instance_types. |
zone | string (csv) | (none) | (any) | Filter by availability zone. |
is_spot | boolean | (none) | true, false | Spot/preemptible vs on-demand. |
is_ready | boolean | (none) | true, false | Kubernetes Ready condition. |
architecture | string (csv) | (none) | amd64, arm64 | CPU architecture. |
capacity_type | string (csv) | (none) | on-demand, spot, reserved, karpenter | Pricing capacity bucket as the cloud provider reports it. |
Example request
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://public-api.kubeadapt.io/v1/nodes?is_spot=false&architecture=amd64&limit=50&include_total=true"Example response
1{
2 "data": [
3 {
4 "id": "n1a2b3c4-d5e6-7890-abcd-ef0987654321",
5 "kind": "Node",
6 "metadata": {
7 "name": "ip-10-0-12-34.eu-west-1.compute.internal",
8 "cluster": {
9 "id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890",
10 "name": "prod-eu-west-1"
11 },
12 "node_role": "worker",
13 "instance_type": "m5.2xlarge",
14 "node_group": "prod-mixed",
15 "availability_zone": "eu-west-1a",
16 "region": "eu-west-1",
17 "is_spot": false,
18 "capacity_type": "on-demand",
19 "architecture": "amd64",
20 "operating_system": "linux",
21 "kubelet_version": "v1.29.4-eks-7c54e62",
22 "provider_id": "aws:///eu-west-1a/i-0a1b2c3d4e5f67890",
23 "is_ready": true,
24 "is_schedulable": true,
25 "labels": {
26 "kubernetes.io/arch": "amd64",
27 "node.kubernetes.io/instance-type": "m5.2xlarge",
28 "topology.kubernetes.io/zone": "eu-west-1a"
29 },
30 "created_at_k8s": "2026-04-22T15:08:11Z",
31 "last_seen_at": "2026-05-21T10:29:48Z"
32 },
33 "capacity": {
34 "cpu": { "total_cores": 8.0, "allocatable_cores": 7.91 },
35 "memory": { "total_bytes": 34359738368, "allocatable_bytes": 32212254720 },
36 "gpu": { "total": 0, "allocatable": 0, "model": "" },
37 "ephemeral_storage": { "total_bytes": 107374182400 },
38 "pods": { "allocatable": 58 }
39 },
40 "utilization": {
41 "cpu": { "used_cores": 6.32, "utilization_percent": 79.9 },
42 "memory": { "used_bytes": 25769803776, "utilization_percent": 80.0 },
43 "gpu": {
44 "utilization_percent": 0,
45 "memory_used_bytes": 0,
46 "memory_total_bytes": 0
47 },
48 "counts": { "pods": 24, "running_pods": 22 }
49 },
50 "cost": {
51 "current_run_rate_hourly": { "amount": "0.4960", "currency": "USD" },
52 "on_demand_equivalent_hourly": { "amount": "0.4960", "currency": "USD" },
53 "pricing_source": "aws-pricing-api",
54 "last_updated_at": "2026-05-21T10:29:48Z"
55 }
56 },
57 {
58 "id": "n2b3c4d5-e6f7-8901-bcde-f12345678901",
59 "kind": "Node",
60 "metadata": {
61 "name": "ip-10-0-15-78.eu-west-1.compute.internal",
62 "cluster": {
63 "id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890",
64 "name": "prod-eu-west-1"
65 },
66 "node_role": "worker",
67 "instance_type": "m5.2xlarge",
68 "node_group": "prod-mixed",
69 "availability_zone": "eu-west-1b",
70 "region": "eu-west-1",
71 "is_spot": true,
72 "capacity_type": "spot",
73 "architecture": "amd64",
74 "operating_system": "linux",
75 "kubelet_version": "v1.29.4-eks-7c54e62",
76 "provider_id": "aws:///eu-west-1b/i-1b2c3d4e5f6a78901",
77 "is_ready": true,
78 "is_schedulable": true,
79 "labels": {
80 "kubernetes.io/arch": "amd64",
81 "node.kubernetes.io/instance-type": "m5.2xlarge",
82 "node.kubernetes.io/lifecycle": "spot",
83 "topology.kubernetes.io/zone": "eu-west-1b"
84 },
85 "created_at_k8s": "2026-04-30T07:42:18Z",
86 "last_seen_at": "2026-05-21T10:29:49Z"
87 },
88 "capacity": {
89 "cpu": { "total_cores": 8.0, "allocatable_cores": 7.91 },
90 "memory": { "total_bytes": 34359738368, "allocatable_bytes": 32212254720 },
91 "gpu": { "total": 0, "allocatable": 0, "model": "" },
92 "ephemeral_storage": { "total_bytes": 107374182400 },
93 "pods": { "allocatable": 58 }
94 },
95 "utilization": {
96 "cpu": { "used_cores": 5.06, "utilization_percent": 64.0 },
97 "memory": { "used_bytes": 22441820160, "utilization_percent": 69.7 },
98 "gpu": {
99 "utilization_percent": 0,
100 "memory_used_bytes": 0,
101 "memory_total_bytes": 0
102 },
103 "counts": { "pods": 19, "running_pods": 19 }
104 },
105 "cost": {
106 "current_run_rate_hourly": { "amount": "0.1492", "currency": "USD" },
107 "on_demand_equivalent_hourly": { "amount": "0.4960", "currency": "USD" },
108 "pricing_source": "aws-pricing-api",
109 "last_updated_at": "2026-05-21T10:29:49Z"
110 }
111 }
112 ],
113 "meta": {
114 "request_id": "req_01J5K4A1Q9Y5XR8A2B3C5D7E9F",
115 "applied_at": "2026-05-21T10:31:00Z",
116 "pagination": {
117 "next_cursor": "eyJ2IjoxLCJhZnRlcl9pZCI6Im4yYjNjNGQ1LWU2ZjctODkwMS1iY2RlLWYxMjM0NTY3ODkwMSIsImFmdGVyX3ZhbHVlIjoiMC4xNDkyIiwicXVlcnlfaGFzaCI6ImI0YzVkNmU3ZjhnOWgwaTFqMmszbDRtNW42cDciLCJpc3N1ZWRfYXQiOjE3MTYyODUwNjB9",
118 "has_more": true,
119 "limit": 50,
120 "total_count": 32
121 }
122 }
123}Example error response
1{
2 "data": null,
3 "meta": {
4 "request_id": "req_01J5K4A2R0Z6YS9C4D5E7F9G1H",
5 "applied_at": "2026-05-21T10:31:05Z"
6 },
7 "error": {
8 "code": "INVALID_COST_MODE",
9 "message": "cost_mode is not accepted on this endpoint (one physical bill)",
10 "details": [
11 { "field": "cost_mode", "reason": "not_accepted_on_this_endpoint" }
12 ]
13 }
14}Common errors
| HTTP | error.code | When |
|---|---|---|
| 400 | BAD_REQUEST | Bad cursor, limit out of range, malformed UUID. |
| 401 | UNAUTHORIZED | Missing or invalid Bearer token. |
| 403 | FORBIDDEN | Token lacks the nodes:read scope. |
| 410 | CURSOR_EXPIRED | Cursor older than 24h or filter set changed. |
| 422 | INVALID_COST_MODE | ?cost_mode= was supplied. This endpoint doesn't accept it. |
| 422 | VALIDATION_ERROR | is_spot, is_ready, architecture, capacity_type, or another filter has an unsupported value. The error detail names the offending field. |
| 429 | RATE_LIMITED | 100 req/min/key by default. Honor Retry-After. |
See Error Handling.
List nodes in cluster
GET /v1/clusters/{cluster_id}/nodes
Required scope: nodes:read
Same shape as the cross-cluster list, scoped to one cluster by path. All filters except cluster_id apply.
Path parameters
| Name | Type | Description |
|---|---|---|
cluster_id | UUID | Cluster identifier. |
Query parameters
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
limit | integer | 100 | 1..500 | Page size. |
cursor | string | (none) | (any) | Opaque pagination token. |
include_total | boolean | false | true, false | Include meta.pagination.total_count. |
node_group | string (csv) | (none) | (any) | Filter by node-group label value. |
instance_type | string (csv) | (none) | (any) | Filter by cloud instance type. |
zone | string (csv) | (none) | (any) | Filter by availability zone. |
is_spot | boolean | (none) | true, false | Spot vs on-demand. |
is_ready | boolean | (none) | true, false | Kubernetes Ready condition. |
architecture | string (csv) | (none) | amd64, arm64 | CPU architecture. |
capacity_type | string (csv) | (none) | on-demand, spot, reserved, karpenter | Pricing capacity bucket. |
Example request
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://public-api.kubeadapt.io/v1/clusters/c1a2b3c4-d5e6-7890-abcd-ef1234567890/nodes?is_spot=true&limit=100"Example response
Returns an array of Node objects scoped to the path cluster. Same shape as the cross-cluster list above — metadata.cluster.id will match the path cluster_id on every row, and meta.pagination follows the same cursor rules.
1{
2 "data": [
3 {
4 "id": "n3c4d5e6-f7a8-9012-cdef-123456789012",
5 "kind": "Node",
6 "metadata": { "name": "ip-10-0-22-91.eu-west-1.compute.internal", "instance_type": "m5.4xlarge", "is_spot": true, "capacity_type": "spot", "availability_zone": "eu-west-1c", "...": "..." },
7 "capacity": { "...": "..." },
8 "utilization": { "...": "..." },
9 "cost": { "current_run_rate_hourly": { "amount": "0.2984", "currency": "USD" }, "on_demand_equivalent_hourly": { "amount": "0.9920", "currency": "USD" }, "pricing_source": "aws-pricing-api", "last_updated_at": "2026-05-21T10:29:51Z" }
10 }
11 ],
12 "meta": {
13 "request_id": "req_01J5K4A3S1A7B2EU6E7G9H1J3D",
14 "applied_at": "2026-05-21T10:31:15Z",
15 "pagination": { "next_cursor": "", "has_more": false, "limit": 100 }
16 }
17}For the complete field list, see the canonical example in List nodes (cross-cluster) or the OpenAPI spec.
Common errors
| HTTP | error.code | When |
|---|---|---|
| 400 | INVALID_CLUSTER_ID | cluster_id is not a valid UUID. |
| 401 | UNAUTHORIZED | Missing or invalid Bearer token. |
| 403 | CLUSTER_ACCESS_DENIED | Token does not include this cluster in its allowlist. |
| 404 | CLUSTER_NOT_FOUND | Cluster UUID well-formed but not present in the tenant. |
| 410 | CURSOR_EXPIRED | Cursor older than 24h or filter set changed. |
| 422 | INVALID_COST_MODE | ?cost_mode= was supplied. |
| 422 | VALIDATION_ERROR | Bad filter value. |
| 429 | RATE_LIMITED | Per-key quota exceeded. |
Get node by UID
GET /v1/nodes/{node_uid}
Required scope: nodes:read
Fetch a single node by its Kubernetes metadata.uid. The uid is stable across kubelet restarts and survives the node's full lifecycle; names like ip-10-0-12-34.eu-west-1.compute.internal change when the underlying instance is replaced. Pod responses surface the uid as metadata.node.id; recommendations surface it as resource_uid.
Path parameters
| Name | Type | Description |
|---|---|---|
node_uid | UUID | The node's Kubernetes metadata.uid. |
Example request
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://public-api.kubeadapt.io/v1/nodes/n1a2b3c4-d5e6-7890-abcd-ef0987654321"Example response
Returns a single Node object. Same shape as one element of the List nodes (cross-cluster) response above, wrapped directly under data (no array, no pagination in meta).
1{
2 "data": {
3 "id": "n1a2b3c4-d5e6-7890-abcd-ef0987654321",
4 "kind": "Node",
5 "metadata": { "name": "ip-10-0-12-34.eu-west-1.compute.internal", "instance_type": "m5.2xlarge", "is_spot": false, "capacity_type": "on-demand", "availability_zone": "eu-west-1a", "...": "..." },
6 "capacity": { "...": "..." },
7 "utilization": { "...": "..." },
8 "cost": { "current_run_rate_hourly": { "amount": "0.4960", "currency": "USD" }, "on_demand_equivalent_hourly": { "amount": "0.4960", "currency": "USD" }, "pricing_source": "aws-pricing-api", "last_updated_at": "2026-05-21T10:29:48Z" }
9 },
10 "meta": {
11 "request_id": "req_01J5K4A4T2B8C3FV7G8H0J2K4E",
12 "applied_at": "2026-05-21T10:31:20Z"
13 }
14}For the complete field list, see the canonical example in List nodes (cross-cluster) or the OpenAPI spec.
Common errors
| HTTP | error.code | When |
|---|---|---|
| 400 | BAD_REQUEST | node_uid is not a valid UUID. |
| 401 | UNAUTHORIZED | Missing or invalid Bearer token. |
| 403 | FORBIDDEN | Token lacks the nodes:read scope. |
| 404 | NODE_NOT_FOUND | UUID well-formed but no node with that uid in this tenant. |
| 422 | INVALID_COST_MODE | ?cost_mode= was supplied. |
| 429 | RATE_LIMITED | Per-key quota exceeded. |
See also
- Node Groups: group-level aggregates over the nodes returned here (spot percentage, instance-type mix, group savings).
- Clusters: the parent cluster identified by
metadata.cluster.idon every node. - Workloads: list workloads, then call
/podsto see which nodes their pods land on. - Recommendations:
workload_rightsizingfindings whose target pods land on these nodes. - Cost Modes: why nodes reject
?cost_mode=and which endpoints accept it.