Home/Developers/Docs
One API key: search offers, spin up a pod, read live cost. Every endpoint below is defined in the Phase 0 contract and runs on the local development server. We do not document things that do not exist.
Phase 0 · broker MVP. The endpoints stay fixed; the supply underneath them evolves.
The honest status first
A permanent documentation site (Fumadocs) comes in a later slice. Until then this page is the single reference point: it lists only the endpoints that are written and running.
In local development the base address is https://api.kaldera.ai and the console is https://konsol.kaldera.ai. A public domain will be announced with the first beta.
Quick start
You pay by the second, not by the hour, you set the cap up front, and you stop whenever you want.
Generate a key in the console under Account → API Keys. The key starts with kld_; the full value is visible once, at the moment it is created, and only its hash is stored on the server. If you lose it you generate a new one — there is no recovery path.
A single static binary; built with Go, no runtime dependencies. You give it an address through the KALDERA_API environment variable (default https://api.kaldera.ai). You can do the same work with plain curl; the CLI is only a thin shell.
List the offers by score, then create a pod with the id of the one you picked. Put an Idempotency-Key on the create request: if the network drops, the same key returns the same pod for 24 hours, so you are not charged twice.
REST endpoints
Money is everywhere an integer in micro-dollars (*_micros): $0.42/hr, for example, travels as 420000. Time is RFC 3339 UTC. An empty list returns [], never null.
| Endpoint | What it does | Returns |
|---|---|---|
| GET /v1/offers | Searches offers: GPU model, VRAM, price ceiling, scorecard, country, region group, Verified filters | offers[] + facets |
| GET /v1/offers/{id} | A single offer and its price breakdown — hourly, daily, monthly | offer + price_breakdown |
| POST /v1/pods | Creates a pod; reads the Idempotency-Key header | 201 pod |
| GET /v1/pods | Pod list; filter it with ?status=running | pods[] + count |
| GET /v1/pods/{id} | A single pod: status, health, connection, cost counter, region pin | pod |
| POST /v1/pods/{id}/stop | Stops it; billing stops that second, the persistent disk stays | pod |
| POST /v1/pods/{id}/start | Starts a stopped pod where it left off | pod |
| DELETE /v1/pods/{id} | Terminates it; ?delete_volume=1 deletes the persistent disk too | 204 |
| GET /v1/pods/{id}/logs | SSE. ?follow=true streams logs live; false returns the last 200 lines | event: log |
| GET /v1/pods/{id}/telemetry | SSE. GPU utilisation, VRAM, temperature, power, health flag | event: telemetry |
| GET /v1/pods/{id}/metrics | Historical series (1h · 6h · 24h · 7d) and unbilled intervals | series[] + health_gaps[] |
| GET /v1/billing/summary | Period spend, burn rate, projection, budget cap, guarantee refund | spent + budget + guarantee |
| GET /v1/billing/live | SSE. Live cost counter — one tick per second | event: cost |
| PUT /v1/billing/budget | Sets the hard cap and what happens when it is hit: pause or notify_only | budget |
| GET /v1/host/machines | Host panel: machines, utilisation, earnings, price suggestion | machines[] |
| GET /healthz | Health probe: service, database and supply-pool state | status + db + supply |
The full list — including billing line items, invoices, payment method, host scorecard and payout endpoints — is in docs/faz0-http-sozlesmesi.md in the repository. This table is a summary of that contract and does not depart from it.
Examples
You can do the same three things from the CLI, from curl and from Python: score the offers, create a pod, listen to the cost. The values below are examples; the real ones come from the marketplace.
# List offers by score (output is illustrative) $ kaldera offers --min-vram 24 --verified GPU QTY REGION $/HR SCORECARD SCORE RTX 4090 1 Istanbul 0.42 4.9 0.87 L40S 1 Frankfurt 0.79 4.8 0.64 # Rent the one you picked, billed per second $ kaldera pod create --offer mock-ist-4090 \ --image kaldera/pytorch:2.4-cuda12.4 ✓ pod_01J8... · running · $0.42/hr
# Search offers — in the EU + TR pool $ curl -s "$KALDERA_API/v1/offers?region_group=eu_tr" \ -H "Authorization: Bearer $KLD_KEY" # Create a pod — with an idempotency key $ curl -s -X POST "$KALDERA_API/v1/pods" \ -H "Authorization: Bearer $KLD_KEY" \ -H "Idempotency-Key: 01J8ZQ3M7K" \ -H "Content-Type: application/json" \ -d '{"offer_id":"mock-ist-4090","image":"kaldera/pytorch:2.4-cuda12.4","volume_gb":250}' # Listen to the live cost (SSE · sample output) $ curl -N "$KALDERA_API/v1/billing/live" event: cost data: {"spent_micros":47820120,"running_pods":1}
# No SDK yet; plain httpx is enough. # Money is an integer in micro-dollars: 420000 = $0.42 import os, httpx api = os.environ["KALDERA_API"] h = {"Authorization": "Bearer " + os.environ["KLD_KEY"]} r = httpx.get(api + "/v1/offers", headers=h, params={"min_vram_gb": 24, "sort": "score"}) best = r.json()["offers"][0] pod = httpx.post(api + "/v1/pods", headers=h, json={ "offer_id": best["id"], "image": "kaldera/pytorch:2.4-cuda12.4", # $120 hard cap (example) "budget_cap_micros": 120000000, }).json()["pod"] print(pod["id"], pod["status"])
Authentication
The CLI and the SDK carry an API key that starts with kld_: Authorization: Bearer kld_…. Keys are stored hashed on the server and the full value is shown only once, at creation. In the browser the console uses an HttpOnly session cookie; the two paths never mix.
Give each key only the permission it needs: offers:read, pods:write, billing:read, host:write. If the key running in CI has no reason to read your invoice, it should not be able to.
The moment you suspect it leaked, revoke it and generate a new one. Because the server knows only the hash, we cannot read your key back to you either — that is not a shortcoming, it is the design.
An honest note: in Phase 0 the control plane performs no authentication for local development — localhost:8080 talks openly. Key generation, scope checks and rate limiting come with the first beta. The Authorization header in the examples is the target contract; send it today and it is ignored. All security decisions are on the Security page.
Errors
All 4xx/5xx responses return the same body: code is the general class, reason is the machine-readable cause, message is for humans, details carries the context. The console and your own code both branch on reason.
| HTTP | code | reason | What it means, what to do |
|---|---|---|---|
| 400 | invalid_argument | region_pin_violation | The requested offer is outside the region pin. This is a server-side check and cannot be bypassed. |
| 400 | invalid_argument | — | Malformed filter or missing field; details names the field. |
| 402 | resource_exhausted | budget_cap_reached | The hard cap is full. Raise the cap or wait; the job does not crash, it pauses. |
| 403 | permission_denied | role_insufficient | The key's scope or your role is not enough. |
| 404 | not_found | — | Unknown pod, offer or machine. |
| 409 | failed_precondition | offer_stale | The offer went stale; details.new_price_micros_hour carries the new price — confirm it and retry. |
| 409 | failed_precondition | no_payment_method | No balance and no payment method. |
| 409 | failed_precondition | pod_not_running | An operation that requires a running pod was sent to a pod that is not running. |
| 503 | unavailable | provider_down | The provider is not answering; fall back to alternative offers. |
Sample body: {"error":{"code":"failed_precondition","reason":"offer_stale","message":"The offer went stale.","details":{"new_price_micros_hour":445000}}}
SDKs and tooling
The order is settled because the customer's first language is Python. Until that day you make plain HTTP calls; the contract is small, so this is not a burden.
A single static binary in Go. Today it has the offers, pods, pod create and version commands; log and ssh commands do not exist yet.
A typed client, SSE helpers and micro-dollar conversion will come built in. Today you can do the same work with httpx.
Once the proto pipeline is wired it will be generated from the ConnectRPC client rather than written by hand. The console will use the same client.
A Terraform provider and SkyPilot integration are on the roadmap; neither is written and we are not giving dates. On this site, if something is labelled "soon", that thing genuinely does not exist.
Start
You don't pay for seconds that don't work, a hard budget cap, per-second billing — all three are inside the API, not features patched on later.