API Reference
Exports
Download a FOCUS 1.4 cost export as CSV, JSON Lines or Parquet, and read the schema that describes it. Parameters, limits and errors.
The export endpoints return your cost data as a FOCUS 1.4 Cost and Usage file, the same file the Kubeadapt dashboard produces. Use them to load Kubernetes cost into a data warehouse or a FinOps tool that reads FOCUS.
Endpoints
| Endpoint | Returns | Required scope |
|---|---|---|
GET /v1/exports/focus | The export file, streamed | cost_explorer:read |
GET /v1/exports/focus/schema | The FOCUS metadata object for one export configuration, as JSON in the standard envelope | cost_explorer:read |
Export a window
GET /v1/exports/focus
Query parameters
| Name | Required | Default | Allowed values | Description |
|---|---|---|---|---|
from | yes | — | YYYY-MM-DD (UTC) | First day of the window, inclusive. |
to | yes | — | YYYY-MM-DD (UTC) | The day after the last day of the window (exclusive). The window can be at most 3,660 days. |
format | no | csv | csv, jsonl, parquet | File format. |
grain | no | workload | workload, namespace | One row per workload or per namespace. |
idle | no | unallocated | unallocated, apportioned | unallocated keeps idle capacity on its own rows; apportioned spreads it across the rows that used the node. |
columns | no | all | comma-separated FOCUS column names | Limit the file to these columns. An unknown name returns 422. |
cluster_id | no | every cluster the key can read | UUID, repeatable or comma-separated | Limit the export to these clusters. |
basis | no | all | all | The export always carries every cost component, so basis=legacy returns 422. See Cost basis. |
Example
Export January 2026 at workload grain as CSV:
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-o kubeadapt-focus-2026-01.csv \
"https://public-api.kubeadapt.io/v1/exports/focus?from=2026-01-01&to=2026-02-01&format=csv&grain=workload"Check that the file is complete
The response streams, so the 200 status is sent before the first row. A 200 means the export started, not that it finished. Kubeadapt reports the outcome in HTTP trailers sent after the last row:
| Trailer | Meaning |
|---|---|
X-Export-Status | complete, or error. error means the file is short: discard it and retry. |
X-Export-Row-Count | Rows written to the file. |
X-Export-Ledger-Rows | Source rows read to produce them. |
Go exposes trailers as resp.Trailer once the body has been read. If your client cannot read trailers, treat a short file as possible. If the connection ends before the trailers arrive, request a shorter window.
Headers sent before the body:
| Header | Meaning |
|---|---|
Content-Disposition | attachment, with a file name that names the grain and window. |
X-Focus-Version | The FOCUS version the file follows. |
X-Focus-Schema-Id | Identifies the column set. Matches schema_id from the schema endpoint for the same configuration. |
X-Focus-Dataset-Instance-Id | Identifies the delivery scope. A later file with the same value replaces the earlier one in full. |
X-Export-Partial | true when the key's cluster allow-list left clusters out of the file. |
Limits
- 6 requests per minute per API key, on top of the 100 per minute limit. To pull more history, widen the window rather than looping over short ones.
- Cluster scope.
cluster_idis intersected with the key's cluster allow-list. A restricted key that names only clusters outside its list gets403 CLUSTER_ACCESS_DENIED; the export is never widened to the whole tenant.
Read the schema
GET /v1/exports/focus/schema
Returns the FOCUS metadata object for one configuration: the column definitions, how corrections and delivery are handled, and what the key's cluster scope leaves out. The schema_id it returns is the one stamped on every file produced with the same configuration.
It accepts grain, idle, columns and basis with the same meaning as above. It does not read from, to or cluster_id: the schema always describes what this key can export. It does not count against the 6-per-minute export limit.
curl -H "Authorization: Bearer ka_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://public-api.kubeadapt.io/v1/exports/focus/schema?grain=workload&idle=unallocated"Errors
Errors on both endpoints use the standard {data, meta, error} envelope.
| HTTP | error.code | Cause |
|---|---|---|
| 400 | INVALID_CLUSTER_ID | A cluster_id value is not a valid UUID. |
| 401 | UNAUTHORIZED | Missing or invalid Bearer token. |
| 403 | FORBIDDEN | API key lacks cost_explorer:read. |
| 403 | CLUSTER_ACCESS_DENIED | No requested cluster is in the key's allow-list (details[0].reason is no_cluster_in_scope). |
| 422 | VALIDATION_ERROR | from or to missing or not YYYY-MM-DD; to not after from; window longer than 3,660 days; unknown format, grain, idle or column; basis=legacy. |
| 429 | RATE_LIMITED | Over the export limit or the service-wide limit. Honor Retry-After. |
| 500 | INTERNAL_ERROR | Unhandled server error. |
| 502 | UPSTREAM_UNAVAILABLE | The export could not be produced. Retry with backoff. |
See Error Handling for the full catalog.
See also
- Cost Explorer, for aggregated cost queries in JSON.
- Permission Scopes, the
cost_explorer:readscope. - Error Handling, the error codes and retry rules.