# Kite Platform API: augment robot datasets and train policies from your code
Source: https://docs.kiteml.com/index
One small REST API for running Kite from your own code — augment robot datasets and train policies on cloud GPUs today, evaluate in simulation soon.
The Kite Platform API runs the platform from your own code. Today that means two things.
**Augmentation:** point Kite at a teleoperation dataset, describe a visual change in plain language, and get back new episodes as a standard LeRobot dataset. **Training:** pick your policies and a GPU tier, and get back trained checkpoints. Both run on cloud GPUs you never have to manage, and the output of the first is valid input to the second. Simulation evaluation is coming to the API next.
Start with the [quickstart](/quickstart) to run your first augmentation, or read [authentication](/platform-api/authentication) to create an API key. Every endpoint is listed in the [API reference](/platform-api/api-reference).
Prefer a terminal or an AI assistant? The same platform is available through the [Kite CLI](https://kiteml.com/docs/cli) and the [Kite MCP server](https://kiteml.com/docs/mcp), which share this API's authentication.
# Platform API reference: endpoints, methods, and paths
Source: https://docs.kiteml.com/platform-api/api-reference
Every Kite Platform API endpoint at a glance — augmentation, training, and account routes, with methods, paths, and links to the OpenAPI spec.
All endpoints are under `https://api.kiteml.com/v1` and require a Bearer token. Full request and response schemas are published as OpenAPI at `https://api.kiteml.com/v1/openapi.json`, rendered at [`/v1/docs`](https://api.kiteml.com/v1/docs).
## Augmentations
| Method | Path | Description |
| ------ | ------------------------------ | ---------------------------------------------------------- |
| `POST` | `/v1/augmentations` | Start a run (`source`, `instructions`, `config`, `output`) |
| `POST` | `/v1/augmentations/estimate` | Estimate the token cost of a run before starting it |
| `GET` | `/v1/augmentations/:id` | Status, progress, and — when done — output files |
| `GET` | `/v1/augmentations` | List your runs |
| `POST` | `/v1/augmentations/:id/cancel` | Cancel a running augmentation |
See [Augmentations](/platform-api/augmentation) for the full lifecycle, request body, and response shapes.
## Account
| Method | Path | Description |
| ------ | ------------- | -------------------------------------------------- |
| `GET` | `/v1/keys/me` | Identity and scopes for the current API key |
| `GET` | `/v1/usage` | Token usage over a date range, grouped by resource |
| `GET` | `/v1/credits` | Current credit balance |
## Training runs
| Method | Path | Description |
| ------ | -------------------------------------------------- | ------------------------------------------------------------------------- |
| `POST` | `/v1/training_runs` | Start a run per policy (`dataset`, `policies`, `hardware_tier`, `config`) |
| `POST` | `/v1/training_runs/estimate` | Estimate the token cost of a run before starting it |
| `GET` | `/v1/training_runs/:id` | Status, phase, live metrics, and output |
| `GET` | `/v1/training_runs` | List your runs |
| `GET` | `/v1/training_runs/:id/logs` | Tail the trainer's output |
| `GET` | `/v1/training_runs/:id/checkpoints` | List the checkpoints a run saved |
| `GET` | `/v1/training_runs/:id/checkpoints/:step/download` | Download one checkpoint as a ZIP of its `pretrained_model/` |
| `POST` | `/v1/training_runs/:id/cancel` | Cancel a run and release its GPU |
See [Training runs](/platform-api/training-runs) for the full lifecycle, request body, and response shapes.
## Catalog
| Method | Path | Description |
| ------ | ----------------------- | ------------------------------------------------------ |
| `GET` | `/v1/training_policies` | Policy types you can train, and the GPU each one needs |
| `GET` | `/v1/hardware_tiers` | GPU tiers, with the token rate each one bills at |
| `POST` | `/v1/datasets/inspect` | Check a dataset is trainable and read its camera keys |
## Operations and webhooks
| Method | Path | Description |
| -------- | --------------------------- | --------------------------------------------------------- |
| `GET` | `/v1/operations/:id` | Uniform status for any run, by id prefix (`aug_`, `trn_`) |
| `POST` | `/v1/webhook_endpoints` | Register a receiver for run events |
| `GET` | `/v1/webhook_endpoints` | List your receivers |
| `DELETE` | `/v1/webhook_endpoints/:id` | Remove a receiver |
| `GET` | `/v1/events` | The durable event log, so polling is always a fallback |
Events: `augmentation.completed`, `augmentation.failed`, `augmentation.canceled`, `training_run.completed`, `training_run.failed`, `training_run.canceled`.
## Evaluate
**Coming soon.** Simulation evaluation endpoints aren't on `/v1` yet. Evaluation runs are available today from the [dashboard](https://app.kiteml.com) and the [Kite CLI](https://kiteml.com/docs/cli).
The interactive reference at [`api.kiteml.com/v1/docs`](https://api.kiteml.com/v1/docs) is generated from the same OpenAPI spec the API is built on, so it's always in sync with production.
Every long-running resource answers to `GET /v1/operations/:id`, whatever its id prefix. If you poll more than one kind of run, write that loop once against `operations` rather than one loop per resource — the status vocabulary is the same for all of them.
# Dataset augmentations: create, poll, and download runs
Source: https://docs.kiteml.com/platform-api/augmentation
Generate augmented robotics datasets programmatically — point Kite at a dataset, describe the change, and get back a ready-to-train Parquet dataset.
An **augmentation** takes an existing robotics dataset and produces new episodes with a visual change you describe in plain language — a different table surface, new lighting, a swapped background. You give Kite four things; it generates the episodes on its GPUs, streams you progress, and delivers a standard LeRobot dataset.
## See it in action
Here is one real demonstration — a bimanual toast-plating task — re-rendered by Augment from a single instruction. Every camera of the episode is transformed together, and the robot's motion and joint trajectories are preserved unchanged. Only the scene changes.
Prompt
"Replace the white tabletop with warm walnut wood, keep the same lighting and objects."
Original teleoperation capture versus the Augment render — same frame, same motion, new environment. One prompt produced all four camera views, each with matching joint trajectories ready to train on.
## Create a run
One call starts a run. Give it the source dataset, your instructions, the episode count, and where the results should go.
```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/augmentations \
-H "Authorization: Bearer $KITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": { "repo_id": "lerobot/pusht" },
"instructions": "change the table surface to white marble, vary the lighting",
"config": { "episode_count": 100 },
"output": { "type": "download" }
}'
```
### Request body
The Hugging Face LeRobot dataset to augment, e.g. `lerobot/pusht`.
Plain-language description of the visual change to apply to every episode.
Number of augmented episodes to generate. Must be between `1` and `50`.
Where results are delivered. `download` keeps them on Kite for you to fetch; `huggingface` pushes the finished dataset to your account.
Required when `output.type` is `huggingface` — the destination repo, e.g. `your-org/pusht-marble`.
The call returns the augmentation resource, including its `id`, immediately:
```json Response — 202 Accepted theme={"system"}
{
"id": "aug_01J8X4M2K9ZQ6R7T3V5W8Y0B1C",
"object": "augmentation",
"status": "processing",
"progress": 0.0,
"output": { "type": "download" },
"created_at": "2026-07-21T09:14:00Z"
}
```
Common failures at create time:
* `400 parameter_invalid` — a malformed field, named in `param`
* `400 episode_limit_exceeded` — `episode_count` above 50
* `402 insufficient_tokens` — not enough credits
* `400 huggingface_not_connected` — for `huggingface` output, when you haven't linked a Hugging Face token in the dashboard
See [Authentication → Errors](/platform-api/authentication#errors) for the envelope.
### Idempotency
Pass a unique `Idempotency-Key` header to make retries safe. A repeated request with the same key returns the original run instead of starting a duplicate — so a dropped connection or a CI retry never double-charges you.
```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/augmentations \
-H "Authorization: Bearer $KITE_API_KEY" \
-H "Idempotency-Key: 9f1c8e2a-run-42" \
-H "Content-Type: application/json" \
-d '{ "source": { "repo_id": "lerobot/pusht" }, "instructions": "...", "config": { "episode_count": 100 }, "output": { "type": "download" } }'
```
Reusing a key with a *different* payload returns `409 Conflict` — the key is bound to the first request body it saw.
## Track progress
Episodes are generated and saved incrementally. Poll the run to watch it move through its lifecycle, with a live `progress` value and a human-readable `status_message`.
```bash theme={"system"}
curl https://api.kiteml.com/v1/augmentations/aug_01J8X4... \
-H "Authorization: Bearer $KITE_API_KEY"
```
```json theme={"system"}
{
"id": "aug_01J8X4M2K9ZQ6R7T3V5W8Y0B1C",
"object": "augmentation",
"status": "processing",
"progress": 0.42,
"status_message": "Generated 42 of 100 videos"
}
```
The `status` field moves through:
| Status | Meaning |
| ------------ | ----------------------------------------------------------- |
| `processing` | Accepted — waiting for a GPU slot, then generating episodes |
| `succeeded` | All episodes generated; output is ready |
| `failed` | The run stopped before completing (see `status_message`) |
| `canceled` | You canceled the run |
Poll on an interval of a few seconds. Episodes are saved as they finish, so a long run's `progress` moves steadily rather than jumping at the end.
## Get your dataset
When `status` is `succeeded`, a `download` run exposes its files under `output.files`. Fetch each one, preserving its `path`, to reconstruct a standard LeRobot Parquet dataset on disk.
```json Response — output.files theme={"system"}
"output": {
"type": "download",
"files": [
{ "path": "meta/info.json", "bytes": 3186, "url": "https://..." },
{ "path": "data/chunk-000/file-000.parquet", "bytes": 16457, "url": "https://..." },
{ "path": "videos/chunk-000/observation.images.cam/episode_000000.mp4", "url": "https://..." }
]
}
```
The `kite augment download` CLI command does this for you — see the [CLI docs](https://kiteml.com/docs/cli). If you chose `huggingface` output instead, `output.url` links the dataset pushed to your account.
Each `url` is either a short-lived signed storage URL or an authenticated `/v1/augmentations/:id/files/:path` proxy path. Send your `Authorization` header when fetching and handle both — the proxy path needs the key; the signed URL ignores it.
The result is a standard **LeRobot v3.0** dataset: Parquet tables for states and actions plus MP4 camera video. It's the same format Kite training accepts, so you can train on it with no conversion. No proprietary output format, no lock-in.
## Cancel a run
Stop a processing run at any time. You're only billed for episodes generated before cancellation.
```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/augmentations/aug_01J8X4.../cancel \
-H "Authorization: Bearer $KITE_API_KEY"
```
## Next: train on it
An augmented dataset is a standard LeRobot dataset, so it goes straight into a [training run](/platform-api/training-runs) — same API key, same credit balance, and no conversion step in between.
# API authentication: bearer tokens, scopes, and 401s
Source: https://docs.kiteml.com/platform-api/authentication
Create a Kite API key and authenticate every Platform API request as a Bearer token, including key format, scopes, and 401 handling for invalid keys.
The Platform API lives at `https://api.kiteml.com/v1` and authenticates every request with an API key.
## Create a key
Create a key from **Platform API → API keys** in the [dashboard](https://app.kiteml.com/platform-api/keys). Scope it to what the key actually does — augmentations if you only generate datasets, training runs if you only train.
Keys start with `kite_` and are shown **only once**, at creation time. Store the value in a secret manager or environment variable — you can't retrieve it later, only revoke it and create a new one.
## Authenticate a request
Send the key in the `Authorization` header as a Bearer token on every request.
```bash theme={"system"}
export KITE_API_KEY=kite_aBcDeFgH...
curl https://api.kiteml.com/v1/keys/me \
-H "Authorization: Bearer $KITE_API_KEY"
```
A valid key returns the key's identity and scopes:
```json theme={"system"}
{
"object": "api_key",
"id": "key_01J8X4...",
"name": "ci-augmentations",
"scopes": ["augmentations:read", "augmentations:write"],
"created_at": "2026-07-21T09:00:00Z"
}
```
An invalid or revoked key returns `401 Unauthorized`.
## Scopes
Scopes are `resource:verb` strings. `write` implies `read` for the same resource, so a key with `augmentations:write` can also read augmentations.
Grant a key the narrowest scopes that its job needs. A CI task that only starts runs needs `augmentations:write` and nothing else, so a leaked key can't read your usage or credit balance.
| Scope | Grants |
| --------------------- | ----------------------------------------------------------------------- |
| `augmentations:write` | Create and cancel augmentation runs (implies `:read`) |
| `augmentations:read` | List and read augmentations, episodes, and downloads |
| `training_runs:write` | Launch and cancel training runs (implies `:read`) |
| `training_runs:read` | List and read training runs, logs, and the policy and hardware catalogs |
| `usage:read` | Read token usage and credit balance |
A call whose key is missing the required scope returns `403` with code `missing_scope`.
Scope each key to the job it does. A CI pipeline that only launches training needs `training_runs:write` and nothing else — it can start and cancel runs, while a leaked key still can't read your augmentations, your usage, or your credit balance.
## Errors
Every error returns the same envelope. `type` maps to the HTTP status; `code` is a stable, specific reason you can branch on; `request_id` identifies the request — include it in any support message.
```json theme={"system"}
{
"error": {
"type": "invalid_request_error",
"code": "parameter_invalid",
"message": "episode_count must be between 1 and 50",
"param": "config.episode_count",
"request_id": "req_01J8X4..."
}
}
```
| Status | Common `code` | Meaning |
| ------ | ------------------------ | -------------------------------------------------- |
| `400` | `parameter_invalid` | Malformed or out-of-range field (named in `param`) |
| `401` | `unauthenticated` | Missing, invalid, or expired API key |
| `402` | `insufficient_tokens` | Not enough credits |
| `403` | `missing_scope` | The key lacks the scope this call requires |
| `404` | `resource_not_found` | No such resource |
| `409` | `idempotency_key_reused` | `Idempotency-Key` reused with a different payload |
| `429` | `rate_limit_exceeded` | Rate limited — honor the `Retry-After` header |
| `5xx` | `api_error` | Server error — safe to retry idempotent requests |
Keep your key server-side. Never ship it in a browser, mobile app, or public repository. If a key leaks, revoke it from the dashboard immediately and create a scoped replacement.
# Platform API overview: augment datasets and train policies on demand
Source: https://docs.kiteml.com/platform-api/overview
A small, predictable REST API for running the Kite platform from your own code — dataset augmentation, policy training, and async runs on cloud GPUs.
The Kite Platform API lets you run the platform programmatically. Today that means two things: generating **augmented robotics datasets**, and **training policies** on managed GPUs. Point Kite at a dataset, say what you want, and Kite does the work on its GPUs and delivers a ready-to-train dataset or a trained policy.
## Base URL
All requests go to:
```
https://api.kiteml.com/v1
```
Every request is authenticated with an API key sent as a Bearer token. See [Authentication](/platform-api/authentication).
## What you can run
There's no infrastructure to manage. Both resources run on Kite's GPUs and deliver standard LeRobot artifacts.
Point Kite at a LeRobot dataset, describe a visual change in plain language, and get back new episodes with the robot's motion preserved. See [Augmentations](/platform-api/augmentation).
Point Kite at a LeRobot dataset, pick your policies and a GPU tier, and get back trained checkpoints. See [Training runs](/platform-api/training-runs).
## Asynchronous by design
Runs are asynchronous. Creating one returns immediately with an `id`; you then poll it as it progresses through `processing → succeeded`, or register a [webhook](/platform-api/api-reference) and skip polling. `failed` and `canceled` are the other terminal states.
Every resource shares that status vocabulary, and `GET /v1/operations/:id` reports it uniformly for any id — so one polling loop handles them all.
A completed augmentation gives you a standard LeRobot dataset: Parquet tables for states and actions plus MP4 camera video. A completed training run gives you a standard LeRobot policy checkpoint. Neither is a proprietary format.
## Conventions
Full request and response schemas are published as OpenAPI at `https://api.kiteml.com/v1/openapi.json`, rendered at [`/v1/docs`](https://api.kiteml.com/v1/docs).
* **JSON everywhere.** Requests and responses are `application/json` unless noted.
* **Resource ids are prefixed** — an augmentation is `aug_…`, a training run is `trn_…`, an API key is `key_…` — so they're easy to recognize in logs.
* **Timestamps are ISO 8601** in UTC (e.g. `2026-07-21T09:14:00Z`).
* **Idempotency is supported** on run creation via an `Idempotency-Key` header — see [Augmentations](/platform-api/augmentation#idempotency) and [Training runs](/platform-api/training-runs#idempotency).
* **Lists are cursor-paginated** — pass the response's `next_cursor` back as `after`, and stop when `has_more` is false. There is no offset paging.
# Training runs: fine-tune a policy on managed GPUs
Source: https://docs.kiteml.com/platform-api/training-runs
Train robot policies programmatically — point Kite at a LeRobot dataset, pick your policies and a GPU tier, and get back trained checkpoints.
A **training run** fine-tunes one policy on one GPU from a LeRobot dataset. You choose the dataset, the policy types, and the GPU tier; Kite provisions the hardware, runs the training, streams you progress, and saves checkpoints.
One run trains one policy. A single request can start several — pass more than one entry in `policies` and you get one run back per policy, all training in parallel on their own GPUs so you can compare them.
## Create a run
```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/training_runs \
-H "Authorization: Bearer $KITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"dataset": { "uri": "lerobot/pusht" },
"policies": ["act"],
"hardware_tier": "gcp_gpu_t4",
"config": { "steps": 20000 }
}'
```
### Request body
The dataset to train on: a Hugging Face LeRobot repo id such as `lerobot/pusht`, or a `gs://` path to a LeRobot dataset.
Policy types to train, one run each. Between `1` and `8` entries. Fetch the list from [`GET /v1/training_policies`](#discover-policies-and-gpus).
The GPU to train on. Defaults to `gcp_gpu_t4`. Each policy declares a minimum tier — see [`GET /v1/hardware_tiers`](#discover-policies-and-gpus).
Training steps. Defaults to the policy's own recommended value.
Batch size. Defaults to the policy's own recommended value.
Save a checkpoint every N steps.
Maps your dataset's camera keys to the policy's image slots: each key is a camera key from your dataset, each value the slot to feed it into. Omit it and Kite maps them for you. See [Inspect a dataset](#inspect-a-dataset) for your dataset's camera keys and a worked example.
Push the trained policy to your connected Hugging Face account when the run finishes. Defaults to `false`.
The policy name to push under. The namespace is always your connected account.
Opaque data echoed back on the run and on its webhook events. Use it to tie a run to your own job id.
The call returns one resource per policy, immediately:
```json Response — 202 Accepted theme={"system"}
{
"object": "list",
"data": [
{
"id": "trn_01J8X4M2K9ZQ6R7T3V5W8Y0B1C",
"object": "training_run",
"status": "processing",
"phase": "provisioning",
"progress": 0.0,
"policy": "act",
"hardware_tier": "gcp_gpu_t4",
"group_id": "5f2c9a1b4e7d",
"dataset": { "uri": "lerobot/pusht" },
"tokens": { "charged": 500, "refunded": 0 },
"created_at": "2026-07-30T09:14:00Z"
}
]
}
```
Runs started in the same request share a `group_id`, so you can tell which ones were launched together even if their webhooks arrive out of order.
Common failures at create time:
* `400 policy_not_available` — an unknown policy, or one that can't currently be trained
* `400 hardware_tier_too_small` — the tier is below the policy's minimum; `details.min_hardware_tier` tells you what it needs
* `400 hardware_tier_not_available` — an unknown or currently disabled tier; `details.available_tiers` lists the usable ones
* `402 insufficient_tokens` — not enough credits for the whole request
* `429 concurrency_limit_exceeded` — you already have the maximum number of runs in flight
* `429 capacity_exceeded` — Kite is at GPU capacity; retry shortly
* `503 service_unavailable` — cloud GPU training is temporarily down
Validation covers the whole request before anything launches, so a request either starts all of its runs or none of them.
See [Authentication → Errors](/platform-api/authentication#errors) for the envelope.
### Idempotency
Pass a unique `Idempotency-Key` header to make retries safe. A repeated request with the same key returns the original runs instead of starting duplicates — so a dropped connection or a CI retry never double-charges you.
```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/training_runs \
-H "Authorization: Bearer $KITE_API_KEY" \
-H "Idempotency-Key: 9f1c8e2a-run-42" \
-H "Content-Type: application/json" \
-d '{ "dataset": { "uri": "lerobot/pusht" }, "policies": ["act"] }'
```
Reusing a key with a *different* payload returns `409 Conflict` — the key is bound to the first request body it saw.
## Track progress
Poll the run to watch it move through its lifecycle. `progress` and `metrics` come straight from the running trainer.
```bash theme={"system"}
curl https://api.kiteml.com/v1/training_runs/trn_01J8X4... \
-H "Authorization: Bearer $KITE_API_KEY"
```
```json theme={"system"}
{
"id": "trn_01J8X4M2K9ZQ6R7T3V5W8Y0B1C",
"object": "training_run",
"status": "processing",
"phase": "training",
"progress": 0.42,
"status_message": "Training",
"metrics": { "step": 8400, "max_steps": 20000, "loss": 0.31 },
"output": { "checkpoint_count": 8, "latest_checkpoint": { "step": 8000 } }
}
```
The `status` field moves through:
| Status | Meaning |
| ------------ | ----------------------------------------------- |
| `processing` | The run is provisioning, preparing, or training |
| `succeeded` | Training finished and checkpoints are saved |
| `failed` | The run stopped before completing (see `error`) |
| `canceled` | You canceled the run |
`phase` tells you *what* a `processing` run is doing right now — useful, because provisioning a GPU and training on it both look the same from `status` alone:
| Phase | Meaning |
| --------------------- | ------------------------------------------ |
| `provisioning` | Waiting for a GPU and pulling the image |
| `downloading_dataset` | Fetching your dataset |
| `downloading_weights` | Fetching the pretrained base model |
| `loading_model` | Building the policy |
| `computing_stats` | Computing dataset normalization statistics |
| `preparing_optimizer` | Setting up the optimizer |
| `training` | Training, with live `metrics` |
| `saving` | Writing the final checkpoint |
| `pushing_to_hub` | Uploading to Hugging Face |
Poll every few seconds while a run is active, or register a [webhook](/platform-api/api-reference) and skip polling. Kite emits `training_run.completed`, `training_run.failed`, and `training_run.canceled`.
You can also poll any run through the uniform operations view, which works the same for every Kite resource:
```bash theme={"system"}
curl https://api.kiteml.com/v1/operations/trn_01J8X4... \
-H "Authorization: Bearer $KITE_API_KEY"
```
## Read the logs
```bash theme={"system"}
curl "https://api.kiteml.com/v1/training_runs/trn_01J8X4.../logs?tail=200" \
-H "Authorization: Bearer $KITE_API_KEY"
```
Returns the last `tail` lines of the trainer's output, capped at 1000. Logs stay readable after the run finishes, so this is where you look when a run fails.
## Get your policy
When `status` is `succeeded`, `output` describes what the run produced.
```json Response — output theme={"system"}
"output": {
"checkpoint_count": 20,
"latest_checkpoint": {
"object": "training_checkpoint",
"step": 20000,
"artifact_uri": "gs://.../artifacts/lerobot/checkpoints/020000/pretrained_model/",
"download_url": "/v1/training_runs/trn_01J8X4.../checkpoints/20000/download"
},
"hf_repo_id": "your-account/act-pusht",
"hf_url": "https://huggingface.co/your-account/act-pusht",
"hf_push_status": "ok"
}
```
Only the latest checkpoint is summarised inline — list them all at `/checkpoints`. `artifact_uri` is the raw storage path, useful if you have your own access to the bucket; otherwise use `download_url`.
## Download a checkpoint
List every checkpoint a run saved, oldest first:
```bash theme={"system"}
curl "https://api.kiteml.com/v1/training_runs/trn_01J8X4.../checkpoints?limit=20" \
-H "Authorization: Bearer $KITE_API_KEY"
```
Then fetch one as a ZIP of its `pretrained_model/` directory:
```bash theme={"system"}
curl -L -o act_step20000.zip \
https://api.kiteml.com/v1/training_runs/trn_01J8X4.../checkpoints/20000/download \
-H "Authorization: Bearer $KITE_API_KEY"
```
It unzips to a standard LeRobot checkpoint — the same files the dashboard gives you, and the same ones `push_to_hub` uploads. Kite streams the archive rather than building it up front, so a multi-GB policy downloads like any other file.
Checkpoints are paginated on `step`: pass the response's `next_cursor` back as `after`, and stop when `has_more` is false. A run with a small `save_freq` can have thousands of them.
`output.push_to_hub` is the other route: set it at create time and the finished policy lands in your Hugging Face account automatically, no download step.
The result is a standard **LeRobot** policy checkpoint. It's the same artifact the dashboard produces, so it loads anywhere LeRobot policies load. No proprietary output format, no lock-in.
## Cancel a run
Stop a run at any time. Cancelling releases the GPU immediately.
```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/training_runs/trn_01J8X4.../cancel \
-H "Authorization: Bearer $KITE_API_KEY"
```
Cancelling a run that has already finished returns `409 not_cancelable`.
## Discover policies and GPUs
Rather than hard-coding names, fetch what's currently trainable. Both lists are small and never paginate.
```bash theme={"system"}
curl https://api.kiteml.com/v1/training_policies \
-H "Authorization: Bearer $KITE_API_KEY"
```
Each policy reports its `min_hardware_tier`, `min_vram_gb`, and recommended `default_steps` and `default_batch_size`. A policy with `"available": false` can't be trained right now.
```bash theme={"system"}
curl https://api.kiteml.com/v1/hardware_tiers \
-H "Authorization: Bearer $KITE_API_KEY"
```
Each tier reports its GPU and its `tokens_per_hour` rate. A tier with `"available": false` exists but is switched off.
## Inspect a dataset
Check a dataset is trainable — and get its camera keys — before spending any credits.
```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/datasets/inspect \
-H "Authorization: Bearer $KITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "uri": "lerobot/pusht" }'
```
```json theme={"system"}
{
"object": "dataset_inspection",
"uri": "lerobot/pusht",
"trainable": true,
"camera_keys": ["observation.images.top", "observation.images.wrist"],
"action_dim": 7,
"state_dim": 14,
"fps": 30,
"num_episodes": 50,
"total_frames": 12000,
"issues": []
}
```
`camera_keys` is what `cameras.rename` maps from. To feed those two cameras into a policy's `top` and `wrist` slots:
```json theme={"system"}
"cameras": {
"rename": {
"observation.images.top": "observation.images.cam_high",
"observation.images.wrist": "observation.images.cam_low"
}
}
```
An unreadable dataset returns `400 dataset_not_readable`.
## What a run costs
Every run reserves one GPU-hour of credits up front, at its tier's rate. A run that trains for longer than an hour is not charged extra.
| Tier | GPU | Tokens per hour |
| -------------- | ----------- | --------------- |
| `gcp_gpu_t4` | NVIDIA T4 | 500 |
| `gcp_gpu_l4` | NVIDIA L4 | 1,000 |
| `gcp_gpu_a100` | NVIDIA A100 | 5,000 |
A request that starts several runs reserves for each of them, so training three policies on a T4 costs 1,500 tokens. A run whose GPU never starts — a submission failure — is not charged at all.
Preview the cost before committing:
```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/training_runs/estimate \
-H "Authorization: Bearer $KITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "hardware_tier": "gcp_gpu_a100", "policies": 2 }'
```
Runs started through the API bill from the same credit balance as the dashboard, and appear in [`GET /v1/usage`](/platform-api/api-reference) under `training`.
## List your runs
```bash theme={"system"}
curl "https://api.kiteml.com/v1/training_runs?limit=20" \
-H "Authorization: Bearer $KITE_API_KEY"
```
Returns your API-created runs, newest first. Pass the response's `next_cursor` as `after` to fetch the next page.
Runs you start through the API also appear in the **Training** section of the [dashboard](https://app.kiteml.com), alongside the ones you start there. It's one set of runs on one set of GPUs, whichever way you launch them.
# Quickstart: augment a LeRobot dataset with one call
Source: https://docs.kiteml.com/quickstart
Go from an API key to a downloaded, augmented LeRobot dataset in a few minutes using the Kite Platform API and a single curl request.
This guide takes you from zero to a finished dataset augmentation. You provide four things: a source dataset, a prompt, an episode count, and a destination. Kite generates the episodes on its GPUs and delivers a standard LeRobot dataset you can train on directly.
## Prerequisites
Before you begin, you need:
* A Kite account with API access enabled
* An **API key** — create one from **Platform API → API keys** in the [dashboard](https://app.kiteml.com/platform-api/keys). Keys start with `kite_` and are shown only once.
* `curl` (or any HTTP client)
Export the key so the examples below can use it.
```bash theme={"system"}
export KITE_API_KEY=kite_aBcDeFgH...
```
Confirm your key is valid against the API base URL, `https://api.kiteml.com/v1`.
```bash theme={"system"}
curl https://api.kiteml.com/v1/keys/me \
-H "Authorization: Bearer $KITE_API_KEY"
```
One call starts a run. Point it at a source dataset, describe the change, choose how many episodes, and where the results go.
```bash theme={"system"}
curl -X POST https://api.kiteml.com/v1/augmentations \
-H "Authorization: Bearer $KITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": { "repo_id": "lerobot/pusht" },
"instructions": "change the table surface to white marble, vary the lighting",
"config": { "episode_count": 20 },
"output": { "type": "download" }
}'
```
You get back the augmentation resource — including its `id` — immediately, with `status: "queued"`.
Poll the run until `status` is `succeeded`. It moves through `queued → processing → succeeded` with a live `progress` value.
```bash theme={"system"}
curl https://api.kiteml.com/v1/augmentations/aug_01J8X4... \
-H "Authorization: Bearer $KITE_API_KEY"
```
A completed `download` run exposes its files under `output.files`. Fetch each one, preserving its `path`, to reconstruct a standard LeRobot Parquet dataset on disk — ready to train on with no conversion.
That's the full loop. For the details — idempotency, Hugging Face delivery, and the complete endpoint list — continue to [Augmentations](/platform-api/augmentation).
Need help? Reach out at [raul@kiteml.com](mailto:raul@kiteml.com).