API Reference
API Overview
Base URL, envelope shape, content type, server time, rate limits, and the full endpoint index for the Kubeadapt /v1 REST API.
The Kubeadapt /v1 REST API exposes read-only access to cost, capacity, and resource data across every Kubernetes cluster connected to a tenant. Every endpoint documented in this reference is GET except the Cost Explorer query, which is POST. Responses use a single {data, meta, error} envelope, with the exceptions listed under The universal envelope. Every request authenticates with a Bearer API key, except the discovery and health endpoints below. Discovery (/v1/openapi.json, /v1/openapi.yaml, /v1/docs) stays under /v1; health checks (/health, /health/live, /health/ready) are the only routes that sit outside it.
Interactive surface
These need no API key:
- Swagger UI — public-api.kubeadapt.io/v1/docs. Paste a Bearer token, execute requests against your tenant in the browser.
- OpenAPI 3.1 spec —
/v1/openapi.jsonor/v1/openapi.yaml. Source of truth for the contract; feed it to an SDK generator. - Postman collection — Download the Postman collection. Set the
apiKeyvariable to your key and every request is ready to send.
Health checks
GET /health, GET /health/live, and GET /health/ready are also unauthenticated, but unlike every other endpoint on this page, they sit at the site root — not under /v1 — and return a small flat JSON body instead of the {data, meta, error} envelope. See Discovery for the full shape of each.
Base URL
Every request goes to:
https://public-api.kubeadapt.ioThe path prefix /v1 carries the version. Every endpoint in this reference starts with /v1 (so GET /v1/clusters, GET /v1/organization, and so on) — except the three unauthenticated health checks, which are root-level (/health, /health/live, /health/ready).
The universal envelope
Every response on this page's endpoint families uses the same top-level wrapper. The exceptions:
- The unauthenticated discovery endpoints (
/v1/openapi.json,/v1/openapi.yaml,/v1/docs) and health checks (/health,/health/live,/health/ready). See Discovery for their flat response shapes. - A successful
POST /v1/cost-explorer/queryreturns{success, data, request_id}with nometablock. Its errors use the standard envelope. See Cost Explorer. GET /v1/exports/focusstreams a CSV, JSON Lines or Parquet file. Its errors use the standard envelope. See Exports.
{
"data": <resource | array of resources>,
"meta": { "request_id": "...", "applied_at": "..." },
"error": { "code": "...", "message": "...", "details": [...] }
}- On success, the
errorkey is omitted entirely (not"error": null). Branch onresponse.error == nullas the canonical success check. - On error,
datais explicitlynullanderrorcarriescode,message, and an optionaldetailsarray. See Error Handling for the full code catalog. meta.request_idandmeta.applied_atare present on every enveloped response. Includerequest_idwhen contacting support.
A success body:
1{
2 "data": {
3 "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
4 "kind": "Organization",
5 "metadata": {
6 "name": "Acme Platform",
7 "domain": "acme.io",
8 "plan_type": "enterprise",
9 "is_active": true,
10 "created_at": "2026-01-03T00:08:51Z"
11 },
12 "utilization": {
13 "counts": {
14 "clusters": 14,
15 "connected_clusters": 12
16 }
17 },
18 "cost": {
19 "current_run_rate_hourly": { "amount": "233.0945", "currency": "USD" },
20 "last_updated_at": "2026-05-21T10:29:00Z",
21 "components": {
22 "compute.cpu": { "amount": "140.2210", "currency": "USD" },
23 "compute.ram": { "amount": "61.4035", "currency": "USD" },
24 "compute.gpu": { "amount": "0.0000", "currency": "USD" },
25 "storage.pv": { "amount": "12.3500", "currency": "USD" },
26 "storage": { "amount": "4.9600", "currency": "USD" },
27 "management": { "amount": "14.1600", "currency": "USD" }
28 },
29 "unallocated": { "amount": "31.4700", "currency": "USD" },
30 "basis": "legacy",
31 "partial": false
32 }
33 },
34 "meta": {
35 "request_id": "0196f1c2-7a3e-7b5d-9c41-2e8f6a1d3b70",
36 "applied_at": "2026-05-21T10:30:00Z",
37 "basis": "legacy"
38 }
39}See Organization for the complete Organization schema, including capacity and full utilization.
Cost blocks
Every cost block carries four fields beside its money figure:
| Field | Type | Meaning |
|---|---|---|
components | object of Money | What the figure is made of, keyed by component (compute.cpu, compute.ram, compute.gpu, storage.pv, storage, management, overhead, network, external). A component Kubeadapt could not produce is absent, never 0. {} means the figure could not be split. |
unallocated | Money | The share of the figure that belongs to no single resource. |
basis | string | The set of components summed into the figure: legacy or all. See Cost basis. |
partial | boolean | true when some component of the basis could not be produced for this figure. |
Every cost endpoint accepts ?basis=legacy|all (default legacy) and echoes it on meta.basis. The examples on the endpoint pages leave these four fields out for brevity; the OpenAPI spec lists them on every schema.
Content type
Request bodies (when one exists) are JSON, and responses are application/json with three exceptions: /v1/docs returns HTML (the Swagger UI page), /v1/openapi.yaml returns YAML, and GET /v1/exports/focus streams a CSV, JSON Lines or Parquet file. The API does not speak XML or protobuf, and there are no Server-Sent Events or WebSockets.
Set Accept: application/json if your HTTP client requires an explicit Accept header. Without it, the API still responds with JSON.
Server time
All timestamps are UTC, formatted as RFC 3339 with seconds precision and a Z suffix:
2026-05-21T10:30:00ZEvery field ending in _at follows this format: applied_at, created_at, updated_at, last_seen_at, and the rest. No millisecond precision, no +00:00 offset, no timezone names. If your client reads applied_at and compares it to time.Now(), do it in UTC.
Dates without a time component (in query parameters, for instance) are YYYY-MM-DD. Date ranges are inclusive at the start and exclusive at the end, except Cost Explorer's end_date, which is inclusive.
Rate limits
The default is 100 requests per minute per API key, counted over a sliding one-minute window. The FOCUS export has its own limit of 6 requests per minute per key on top of that. Every authenticated response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; use them to slow down before you hit the limit. Exceeding the quota returns 429 RATE_LIMITED with Retry-After: 60. If rate-limit enforcement is temporarily unavailable, the API returns 503 RATE_LIMIT_UNAVAILABLE with Retry-After: 5.
For the full envelope shapes and retry policy, see Error Handling. For a higher limit on a specific integration, contact support@kubeadapt.io.
What's NOT here
Things the /v1 API explicitly does not do, listed so you don't spend time looking:
- No write operations. Every endpoint is read-only. All are
GETexceptPOST /v1/cost-explorer/query, which uses POST only to carry its query body. There is noPOST /v1/recommendations/{id}/apply, noPUT /v1/teams/{id}, noDELETEanywhere. - No webhooks. The API does not push events to your endpoint. Poll the resources you care about, or use the Smart Alerting channels (Slack, email, webhook) configured in the dashboard.
- No event streams. No long-polling, no Server-Sent Events, no WebSockets. List endpoints paginate with cursors. The only streamed response is the FOCUS export file.
- No GraphQL. Plain REST. If you want a single round-trip with joined data, use the
dashboardendpoints (e.g.,/v1/organization/dashboard), which embed top-N summaries. - No OpenTelemetry receiver. Kubeadapt does not accept OTLP traces or metrics. The in-cluster agent is the only data source. See How Kubeadapt works.
- No SDK shipping in this release. The OpenAPI spec is public; generate a client with
openapi-generator-cliagainst/v1/openapi.yaml.
Endpoint index
One row per resource family. Every endpoint requires a Bearer API key (see Authentication) and a scope (see Permission Scopes) — except Discovery, which is unauthenticated (see below). The Paginated column refers to the family's list endpoints. The cost_mode column describes how the family handles the ?cost_mode= query parameter (see Cost Modes).
| Family | Scope | Paginated | cost_mode |
|---|---|---|---|
| Organization | organization:read | no | accepts on /dashboard, rejects on snapshot |
| Clusters | clusters:read | yes (cursor) | REJECTS |
| Namespaces | namespaces:read | yes (cursor) | accepts |
| Workloads | workloads:read | yes (cursor) | accepts |
| Nodes | nodes:read | yes (cursor) | REJECTS |
| Node Groups | nodes:read | no | REJECTS |
| Recommendations | recommendations:read | yes (cursor) | REJECTS |
| Teams | teams:read | yes (cursor) | accepts |
| Departments | departments:read | yes (cursor) | accepts |
| Cost Explorer | cost_explorer:read | yes (offset) | accepts, as a JSON body field |
| Cost Trends | per-resource scope | n/a (series) | accepts (except node-group, which REJECTS) |
| Exports | cost_explorer:read | n/a (stream) | n/a |
| Discovery | unauthenticated | n/a | n/a |
Notes on the table:
- Node Groups uses
nodes:read— the two families share authorization. - Cost Explorer uses offset pagination (
page/per_page), not cursor pagination. Every other paginated family uses cursors. - Cost Trends scope depends on the path: cluster →
clusters:read, namespace →namespaces:read, workload →workloads:read, node-group →nodes:read, organization-dashboard →organization:read. cost_modebehavior — on Namespaces, Workloads (including pods), the Organization Dashboard'smonth_to_datefigure, Cost Trends (cluster, namespace, workload, organization) and Cost Explorer, the two modes return different amounts. Teams and Departments echo the requested mode but return the sameamountin both. The node-group cost-trend endpoint rejects?cost_mode=(one physical bill per node).- Cost Explorer takes
cost_modein the JSON body. A?cost_mode=query parameter on that route is ignored. - REJECTS means the endpoint returns
422 INVALID_COST_MODEif you pass?cost_mode=. The Organization snapshot at/v1/organizationrejects it (the org-level bill is one physical number); the Organization Dashboard at/v1/organization/dashboardaccepts it. - Discovery covers
/v1/openapi.*and/v1/docs. These do not require a Bearer token.
First request
Fetch the organization snapshot to confirm the key works end-to-end:
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://public-api.kubeadapt.io/v1/organization"A 200 OK returns the tenant snapshot — full schema in the Organization reference. A 401 UNAUTHORIZED means the key was not accepted; a 403 FORBIDDEN means the key lacks the organization:read scope. See Authentication for the 401 vs 403 distinction.
See also
- Authentication: the Bearer token format, key minting, rotation, and 401 vs 403.
- Permission Scopes: the nine read scopes and which endpoints they cover.
- Exports: the FOCUS 1.4 cost export and its schema.
- Pagination & Filtering: cursor semantics,
limit,include_total, and filter operators. - Error Handling: the full error code catalog and recommended retry behavior.
- Cost Modes:
fully_loadedvsworkload_only, when each is accepted, and how the response echoes the mode. - Organization: the full snapshot and dashboard schemas, plus the cost-mode rejection on the snapshot endpoint.