Home/Developers/Docs

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

How real is this document?

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.

Phase 0 REST + SSE contractwritten · running in local development
CLI (kaldera)skeleton: offers · pods · pod create
ConnectRPC / proto generation pipelineproto written, pipeline not wired
Python SDKplanned · first beta
TypeScript SDKplanned · after Python
Documentation site (Fumadocs)planned

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

Three steps from a key to your first pod.

You pay by the second, not by the hour, you set the cap up front, and you stop whenever you want.

Get your key

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.

Install the CLI

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.

Spin up your first pod

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

Small surface, clear contract.

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.

EndpointWhat it doesReturns
GET /v1/offersSearches offers: GPU model, VRAM, price ceiling, scorecard, country, region group, Verified filtersoffers[] + facets
GET /v1/offers/{id}A single offer and its price breakdown — hourly, daily, monthlyoffer + price_breakdown
POST /v1/podsCreates a pod; reads the Idempotency-Key header201 pod
GET /v1/podsPod list; filter it with ?status=runningpods[] + count
GET /v1/pods/{id}A single pod: status, health, connection, cost counter, region pinpod
POST /v1/pods/{id}/stopStops it; billing stops that second, the persistent disk stayspod
POST /v1/pods/{id}/startStarts a stopped pod where it left offpod
DELETE /v1/pods/{id}Terminates it; ?delete_volume=1 deletes the persistent disk too204
GET /v1/pods/{id}/logsSSE. ?follow=true streams logs live; false returns the last 200 linesevent: log
GET /v1/pods/{id}/telemetrySSE. GPU utilisation, VRAM, temperature, power, health flagevent: telemetry
GET /v1/pods/{id}/metricsHistorical series (1h · 6h · 24h · 7d) and unbilled intervalsseries[] + health_gaps[]
GET /v1/billing/summaryPeriod spend, burn rate, projection, budget cap, guarantee refundspent + budget + guarantee
GET /v1/billing/liveSSE. Live cost counter — one tick per secondevent: cost
PUT /v1/billing/budgetSets the hard cap and what happens when it is hit: pause or notify_onlybudget
GET /v1/host/machinesHost panel: machines, utilisation, earnings, price suggestionmachines[]
GET /healthzHealth probe: service, database and supply-pool statestatus + 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

Leave the terminal, get into the code.

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.

Sorting defaults to the score: not the cheapest, but the best offer on price × scorecard × network comes first.
The score is explained: every offer carries score_explain, telling you how many points came from which component.
The budget cap is in the API: when the cap fills you get 402 · budget_cap_reached; the job does not crash, it pauses.
CLIcurlPython
# 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
CLIcurlPython
# 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}
CLIcurlPython
# 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 console uses a cookie, your code uses a key.

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.

Scopes

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 life of a key

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

Every error carries a machine-readable reason.

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.

HTTPcodereasonWhat it means, what to do
400invalid_argumentregion_pin_violationThe requested offer is outside the region pin. This is a server-side check and cannot be bypassed.
400invalid_argumentMalformed filter or missing field; details names the field.
402resource_exhaustedbudget_cap_reachedThe hard cap is full. Raise the cap or wait; the job does not crash, it pauses.
403permission_deniedrole_insufficientThe key's scope or your role is not enough.
404not_foundUnknown pod, offer or machine.
409failed_preconditionoffer_staleThe offer went stale; details.new_price_micros_hour carries the new price — confirm it and retry.
409failed_preconditionno_payment_methodNo balance and no payment method.
409failed_preconditionpod_not_runningAn operation that requires a running pod was sent to a pod that is not running.
503unavailableprovider_downThe 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

Python first, TypeScript next. Neither exists yet.

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.

CLI

kaldera Phase 0 · skeleton

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.

PY

Python SDK soon · first beta

A typed client, SSE helpers and micro-dollar conversion will come built in. Today you can do the same work with httpx.

TS

TypeScript SDK soon · Phase 1

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

The contract is small. The first pod takes five minutes.

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.