API Reference
Namespaces
List namespaces scoped to a cluster, fetch one by name with its top workloads inline, or pull a cross-cluster flat list for an org-wide export.
Three endpoints cover the namespace surface: a cross-cluster flat list, a per-cluster scoped list, and a detail endpoint that returns the namespace plus its top five workloads by run-rate cost.
GET /v1/clusters/{cluster_id}/namespaces is the scoped list. GET /v1/clusters/{cluster_id}/namespaces/{namespace} is the detail endpoint that includes workloads_top_5 inline. GET /v1/namespaces is the cross-cluster flat list with optional cluster_id scoping. All three require the namespaces:read scope and accept ?cost_mode=. Both modes currently return the same amount for namespace endpoints. See Cost Modes.
List namespaces in cluster
GET /v1/clusters/{cluster_id}/namespaces
Required scope: namespaces:read
Returns the paginated list of namespaces in one cluster, sorted by cost.current_run_rate_hourly DESC. Filter with min_cost_hourly to skip the long tail of cheap namespaces.
metadata.team and metadata.department are populated only for namespaces where a team or department has been assigned (manually or by Kubernetes label); on unassigned namespaces, the fields are absent. capacity is omitted entirely on namespaces with no ResourceQuota — its presence distinguishes quota'd from open namespaces.
Path parameters
| Name | Type | Description |
|---|---|---|
cluster_id | UUID | Parent cluster identifier. |
Query parameters
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
limit | integer | 100 | 1..500 | Page size. |
cursor | string | - | - | Opaque pagination token. |
include_total | boolean | false | true, false | Include meta.pagination.total_count. Opt-in. |
cost_mode | string | fully_loaded | fully_loaded, workload_only | Cost attribution mode. See Cost Modes. |
min_cost_hourly | number | - | decimal ≥ 0 | Minimum hourly cost. Use this to skip near-zero namespaces. |
See Pagination & Filtering for the cursor contract.
Example request
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://public-api.kubeadapt.io/v1/clusters/c1a2b3c4-d5e6-7890-abcd-ef1234567890/namespaces?cost_mode=fully_loaded&min_cost_hourly=0.10&limit=50"Example response
1{
2 "data": [
3 {
4 "id": "n1f2e3d4-c5b6-4798-89a0-1b2c3d4e5f60",
5 "kind": "Namespace",
6 "metadata": {
7 "name": "payments",
8 "cluster": { "id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890", "name": "prod-eu-west-1" },
9 "uid_k8s": "0b97f1d2-3c4e-5f60-7a8b-9c0d1e2f3a4b",
10 "labels": {
11 "team": "payments",
12 "owner": "checkout-platform",
13 "managed-by": "argo-cd"
14 },
15 "team": { "id": "t8a7b6c5-d4e3-4f21-9088-1a2b3c4d5e6f", "name": "Payments" },
16 "department": { "id": "d2c3b4a5-9687-4f5e-8d7c-6b5a4d3c2b1a", "name": "Engineering" },
17 "created_at_k8s": "2025-11-09T08:30:14Z",
18 "last_seen_at": "2026-05-21T10:29:47Z"
19 },
20 "utilization": {
21 "cpu": { "requested_cores": 18.4, "used_cores": 12.7, "utilization_percent": 69.0 },
22 "memory": { "requested_bytes": 68719476736, "used_bytes": 52613349376, "utilization_percent": 76.6 },
23 "counts": {
24 "workloads": 28,
25 "deployments": 22,
26 "statefulsets": 4,
27 "daemonsets": 0,
28 "jobs": 1,
29 "cronjobs": 1,
30 "pods": 412,
31 "running_pods": 408,
32 "containers": 947,
33 "persistent_volumes": 14
34 }
35 },
36 "cost": {
37 "current_run_rate_hourly": { "amount": "8.4200", "currency": "USD" },
38 "cost_mode": "fully_loaded",
39 "last_updated_at": "2026-05-21T10:29:49Z"
40 }
41 },
42 {
43 "id": "n2e3d4c5-b6a7-4988-90a1-2c3d4e5f6071",
44 "kind": "Namespace",
45 "metadata": {
46 "name": "kube-system",
47 "cluster": { "id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890", "name": "prod-eu-west-1" },
48 "uid_k8s": "1c08e2f3-4d5f-6071-8b9c-0d1e2f3a4b5c",
49 "labels": {
50 "kubernetes.io/metadata.name": "kube-system"
51 },
52 "created_at_k8s": "2025-11-08T14:22:31Z",
53 "last_seen_at": "2026-05-21T10:29:47Z"
54 },
55 "capacity": {
56 "cpu": { "quota_cores": 4.0 },
57 "memory": { "quota_bytes": 8589934592 }
58 },
59 "utilization": {
60 "cpu": { "requested_cores": 2.4, "used_cores": 1.8, "utilization_percent": 45.0 },
61 "memory": { "requested_bytes": 4294967296, "used_bytes": 3221225472, "utilization_percent": 37.5 },
62 "counts": {
63 "workloads": 12,
64 "deployments": 6,
65 "statefulsets": 2,
66 "daemonsets": 4,
67 "jobs": 0,
68 "cronjobs": 0,
69 "pods": 60,
70 "running_pods": 60,
71 "containers": 124,
72 "persistent_volumes": 2
73 }
74 },
75 "cost": {
76 "current_run_rate_hourly": { "amount": "1.9200", "currency": "USD" },
77 "cost_mode": "fully_loaded",
78 "last_updated_at": "2026-05-21T10:29:49Z"
79 }
80 }
81 ],
82 "meta": {
83 "request_id": "req_01J5K5C2E9G1IK3M5O7Q9S1U3V",
84 "applied_at": "2026-05-21T10:30:00Z",
85 "pagination": {
86 "next_cursor": "",
87 "has_more": false,
88 "limit": 50,
89 "total_count": 24
90 },
91 "cost_mode": "fully_loaded"
92 }
93}The first namespace has team and department populated and no capacity block (no ResourceQuota). The second is the opposite: no team/department, but a ResourceQuota is in place so capacity.cpu.quota_cores and capacity.memory.quota_bytes appear. Both shapes are valid; check for presence before reading the fields.
Example error response
1{
2 "data": null,
3 "meta": {
4 "request_id": "req_01J5K5C2E9G1IK3M5O7Q9S1U3W",
5 "applied_at": "2026-05-21T10:30:01Z"
6 },
7 "error": {
8 "code": "INVALID_COST_MODE",
9 "message": "cost_mode must be one of: fully_loaded, workload_only",
10 "details": [
11 { "field": "cost_mode", "reason": "invalid_value", "allowed": ["fully_loaded", "workload_only"] }
12 ]
13 }
14}Returned when ?cost_mode= is supplied with anything other than the two allowed values. See Error Handling for the catalog.
Common errors
| HTTP | error.code | When |
|---|---|---|
| 400 | INVALID_CLUSTER_ID | cluster_id isn't a valid UUID. |
| 400 | INVALID_CURSOR | cursor couldn't be decoded. |
| 401 | UNAUTHORIZED | Missing or invalid Bearer token. |
| 403 | FORBIDDEN | Token lacks the namespaces:read scope. |
| 403 | CLUSTER_ACCESS_DENIED | API key's cluster allowlist excludes this cluster. |
| 404 | CLUSTER_NOT_FOUND | UUID well-formed but no cluster matches in the tenant. |
| 410 | CURSOR_EXPIRED | Cursor older than 24h or query parameters changed. |
| 422 | INVALID_COST_MODE | cost_mode value not in the allowed enum. |
| 429 | RATE_LIMITED | Per-key quota exceeded. |
| 500 | INTERNAL_ERROR | Server-side failure. |
Get namespace by name
GET /v1/clusters/{cluster_id}/namespaces/{namespace}
Required scope: namespaces:read
Returns one namespace's full body, with workloads_top_5 populated inline — the top five workloads in that namespace ranked by hourly run rate. workloads_top_5 appears only on this endpoint; the list endpoints omit it. Each entry carries id, kind, name, and cost.current_run_rate_hourly.
Path parameters
| Name | Type | Description |
|---|---|---|
cluster_id | UUID | Parent cluster identifier. |
namespace | string | Kubernetes namespace name. Must be non-empty; an empty string returns BAD_REQUEST (400). |
Query parameters
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
cost_mode | string | fully_loaded | fully_loaded, workload_only | Cost attribution mode. See Cost Modes. |
Example request
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://public-api.kubeadapt.io/v1/clusters/c1a2b3c4-d5e6-7890-abcd-ef1234567890/namespaces/checkout?cost_mode=fully_loaded"Example response
Returns a single Namespace object. Same shape as one element of the List namespaces in cluster response above, wrapped directly under data (no array, no pagination in meta), plus the inline workloads_top_5 array described above. For full per-workload accounting, see Workloads.
1{
2 "data": {
3 "id": "n3d4c5b6-a7e8-49f0-81a2-3d4e5f607182",
4 "kind": "Namespace",
5 "metadata": { "name": "checkout", "cluster": { "id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890", "name": "prod-eu-west-1" }, "team": { "id": "t9b8c7d6-e5f4-4032-9199-2b3c4d5e6f70", "name": "Checkout" }, "...": "..." },
6 "utilization": { "...": "..." },
7 "cost": { "current_run_rate_hourly": { "amount": "5.6700", "currency": "USD" }, "cost_mode": "fully_loaded", "last_updated_at": "2026-05-21T10:29:49Z" },
8 "workloads_top_5": [
9 { "id": "w1a2b3c4-d5e6-4f78-89a0-b1c2d3e4f506", "kind": "Workload", "name": "api-gateway", "cost": { "current_run_rate_hourly": { "amount": "2.4000", "currency": "USD" } } },
10 { "id": "w2b3c4d5-e6f7-4089-91a2-c3d4e5f60718", "kind": "Workload", "name": "cart-service", "cost": { "current_run_rate_hourly": { "amount": "1.5300", "currency": "USD" } } },
11 { "...": "..." }
12 ]
13 },
14 "meta": {
15 "request_id": "req_01J5K5D4F1H3JK5M7O9Q1S3U5V7",
16 "applied_at": "2026-05-21T10:30:00Z",
17 "cost_mode": "fully_loaded"
18 }
19}For the complete field list, see the canonical example in List namespaces in cluster or the OpenAPI spec.
Common errors
| HTTP | error.code | When |
|---|---|---|
| 400 | INVALID_CLUSTER_ID | cluster_id isn't a valid UUID. |
| 400 | BAD_REQUEST | namespace path parameter is empty. |
| 401 | UNAUTHORIZED | Missing or invalid Bearer token. |
| 403 | FORBIDDEN | Token lacks the namespaces:read scope. |
| 403 | CLUSTER_ACCESS_DENIED | Cluster allowlist excludes this cluster. |
| 404 | CLUSTER_NOT_FOUND | Cluster UUID has no match in the tenant. |
| 404 | NAMESPACE_NOT_FOUND | Cluster exists, but no namespace with that name. |
| 422 | INVALID_COST_MODE | cost_mode value not in the allowed enum. |
| 429 | RATE_LIMITED | Per-key quota exceeded. |
| 500 | INTERNAL_ERROR | Server-side failure. |
List namespaces (cross-cluster)
GET /v1/namespaces
Required scope: namespaces:read
Cross-cluster flat list of namespaces across the API key's allowed clusters. Each namespace's metadata.cluster identifies its parent cluster — the endpoint returns one row per (cluster, namespace) pair, with no deduplication across clusters (a namespace named default in two clusters appears as two rows). Pass cluster_id as a CSV to narrow to a subset of clusters. Default sort is cost.current_run_rate_hourly DESC.
Query parameters
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
limit | integer | 100 | 1..500 | Page size. |
cursor | string | - | - | Opaque pagination token. |
include_total | boolean | false | true, false | Include meta.pagination.total_count. |
cost_mode | string | fully_loaded | fully_loaded, workload_only | Cost attribution mode. See Cost Modes. |
min_cost_hourly | number | - | decimal ≥ 0 | Minimum hourly cost. |
cluster_id | string | - | UUID csv | Scope to a subset of clusters. Omit to include every allowed cluster. |
Example request
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://public-api.kubeadapt.io/v1/namespaces?cluster_id=c1a2b3c4-d5e6-7890-abcd-ef1234567890,a8d3e1b2-f4c6-4789-9bde-2345678901cd&min_cost_hourly=0.50&include_total=true&limit=100"Example response
Returns an array of Namespace objects across the API key's allowed clusters. Same shape as the List namespaces in cluster above — each row's metadata.cluster identifies which cluster it belongs to, and meta.pagination follows the same cursor rules. The workloads_top_5 summary is not present on this endpoint; it appears only on the detail endpoint.
1{
2 "data": [
3 {
4 "id": "n4e5f607-1829-4abc-94d5-e607182a3b4c",
5 "kind": "Namespace",
6 "metadata": { "name": "payments", "cluster": { "id": "c1a2b3c4-d5e6-7890-abcd-ef1234567890", "name": "prod-eu-west-1" }, "team": { "id": "t8a7b6c5-d4e3-4f21-9088-1a2b3c4d5e6f", "name": "Payments" }, "...": "..." },
7 "utilization": { "...": "..." },
8 "cost": { "current_run_rate_hourly": { "amount": "8.4200", "currency": "USD" }, "cost_mode": "fully_loaded", "last_updated_at": "2026-05-21T10:29:49Z" }
9 }
10 ],
11 "meta": {
12 "request_id": "req_01J5K5E6G3J5LN7P9R1T3V5X7Z9",
13 "applied_at": "2026-05-21T10:30:00Z",
14 "pagination": { "next_cursor": "", "has_more": false, "limit": 100, "total_count": 87 },
15 "cost_mode": "fully_loaded"
16 }
17}For the complete field list, see the canonical example in List namespaces in cluster or the OpenAPI spec.
Common errors
| HTTP | error.code | When |
|---|---|---|
| 400 | BAD_REQUEST | Malformed query, bad cluster_id in the CSV, bad min_cost_hourly. |
| 400 | INVALID_CURSOR | cursor couldn't be decoded. |
| 401 | UNAUTHORIZED | Missing or invalid Bearer token. |
| 403 | FORBIDDEN | Token lacks the namespaces:read scope. |
| 410 | CURSOR_EXPIRED | Cursor older than 24h or query parameters changed. |
| 422 | INVALID_COST_MODE | cost_mode value not in the allowed enum. |
| 429 | RATE_LIMITED | Per-key quota exceeded. |
| 500 | INTERNAL_ERROR | Server-side failure. |
See also
- Clusters, parent resource;
metadata.clusteron every namespace points back here. - Workloads, drill from
workloads_top_5into the full per-workload body. - Organization, the tenant-wide aggregate that
utilization.counts.namespacesrolls up from. - Cost Modes, what
fully_loadedandworkload_onlymean, and which resources are already mode-aware. - Cost Trends, namespace cost time series at hourly, daily, weekly, or monthly granularity.