For labs / API
API
Every request is a POST to a job, a GET on a resource, or a DELETE that cancels. Base URL: https://api.melaps.system/v1. All responses are JSON; the body is always an object. Current API version: 2026-06-22, sent as Melaps-Version: 2026-06-22.
Authentication
Header: Authorization: Bearer sk_live_... Keys are created in the console at /account/keys and can be revoked at any time. Test keys start with sk_test_ and do not consume capacity. A key is bound to one lab — scopes are not OAuth scopes.
$curl -H "Authorization: Bearer sk_live_..." https://api.melaps.system/v1/jobs
Scopes: read, write, jobs:write, jobs:cancel, reservations:read, usage:read, metrics:read. The response shows the key scope in X-MELAPS-SCOPES: read, write. A missing scope returns 403 with error.code "insufficient_scope". 401 means the key is wrong, expired or revoked — error.code "unauthenticated", "Invalid API key". There is no mTLS and no API key in the URL for v1 callers. Make one key per service, scope it conservatively, never put a key in a repository, rotate every 90 days.
Endpoints
| Method | Path | What it does |
|---|---|---|
| GET | /v1/jobs | List jobs — filter state, limit max 100 (default 50), cursor for the next page, newest first |
| POST | /v1/jobs | Create a job |
| GET | /v1/jobs/{id} | One job |
| DELETE | /v1/jobs/{id} | Cancel a job — queued or running only; returns {"cancelled": true} |
| GET | /v1/jobs/{id}/metrics | Telemetry time series for a job |
| GET | /v1/reservations | List reservations |
| GET | /v1/reservations/{id} | One reservation |
| GET | /v1/reservations/{id}/utilisation | Reservation utilisation |
| GET | /v1/usage | Monthly usage — e.g. ?month=2026-05 |
| GET | /v1/clusters | List clusters |
| GET | /v1/status | Status — no auth, not counted in quota |
| GET | /v1/status/history | Status history |
Create a job
Fields: name (required, max 80 characters); kind (train, fine-tune, inference, shell); image (e.g. nvcr.io/nvidia/pytorch:24.06-py3); command (one command, max 600 characters); gpu_count (1–8); gpu_model (H100-80GB, L40S-48GB, A100-80GB); gpu_memory_gb; reservation_id (optional, res_01...); mode (on-demand, reserved, batch); priority (low, standard, high); vcpu (60 per GPU, tied to GPU); memory_gb (60 GB CPU per GPU); storage_gb (max 200); timeout (90 days per job); checkpointing (optional, stored in checkpoints).
$curl -X POST https://api.melaps.system/v1/jobs \ -H "Authorization: Bearer sk_live_..." \ -H "Idempotency-Key: train-llama-3-8b-001" \ -H "Content-Type: application/json" \ -d '{"name": "train-llama-3-8b", "command": ["python", "train.py"], "gpu_count": 8, "gpu_model": "H100-80GB", "gpu_memory_gb": 60, "mode": "reserved", "reservation_id": "res_01...", "priority": "high", "command_timeout": "90d", "storage_gb": 100}'
Response 201, state queued, queue_position 3. Job states: queued, running, succeeded, failed, cancelled. High priority beats queued standard and low. A job object carries id, kind, state, queue_position, name, command, gpu_count, gpu_model, gpu_memory_gb, cluster_id, reservation_id, mode, priority, timeout, checkpoints, size_gb, created_at, started_at, ended_at (UTC ISO 8601).
Metrics
jsonGET /v1/jobs/{id}/metrics { "job_id": "job_01J6YQ...", "gpu_utilisation_pct": 88.4, "gpu_memory_used_gb": 68.2, "uptime_pct": 99.9, "points": [{ "t": "2026-05-17T10:00:00Z", "gpu_utilisation_pct": 88.4, "gpu_memory_used_gb": 68.2 }] }
Metrics are sampled every 30 seconds and kept for 90 days.
Rate limits
Default 120 requests per minute. Reads 600 per minute. POST /v1/jobs is limited to 10 per minute. Headers: X-RateLimit-Limit: 600, X-RateLimit-Remaining: 597, X-RateLimit-Reset, Retry-After: 12. A 429 carries Retry-After in seconds — use exponential backoff. Quota is per key and per lab, not per IP. 5xx responses (502, 503) are not billed.
Errors
| Code | error.code | Meaning |
|---|---|---|
| 404 | job_not_found | Job does not exist, or belongs to another lab |
| 400 | validation_error | Bad field format — e.g. gpu_count must be 1–8, command max 600 characters |
| 401 | unauthenticated | Invalid API key |
| 403 | insufficient_scope | Missing scope |
| 409 | conflict | reservation_id has no capacity right now; idempotency_key_reused |
| 429 | rate_limited | Retry after Retry-After seconds |
| 500 | internal_error | A job was not started and was not charged |
| 502 / 503 | unavailable | A job was not started and was not charged |
Error format: {"error": {"code": "job_not_found", "message": "Job not found."}} error.code is stable per API version. A 5xx response means the job request was not processed. Retry with the same Idempotency-Key.
Idempotency
POST /v1/jobs accepts an Idempotency-Key header. The same key within 24 hours returns the same job and does not create a duplicate — the replay carries Idempotency-Replayed: true. The same key with a different body returns 409 conflict, error.code "idempotency_key_reused". If the first attempt fails without a response, retry with the same key.
Webhooks
Register webhooks at /account/webhooks. Headers: X-MELAPS-SIGNATURE: sha256=..., X-MELAPS-EVENT: job.succeeded. Events: job.queued, job.started, job.succeeded, job.failed, job.cancelled. Delivery is retried up to 3 times in 24 hours with backoff. Events arrive in order — you will not receive a job.succeeded before a job.started.
json{ "event": "job.succeeded", "job_id": "job_01J6YQ...", "time": "2026-05-17T10:00:00Z" }
Changelog
2026-06-22: gpu_model added; gpu_memory_gb is now required in the response. 2026-04-15: POST /v1/jobs now returns job.id in the 201 response (previously only job_id). 2026-03-01: state added to GET /v1/jobs (previously only GET /v1/jobs/{id}). Earlier versions removed.
TELEMETRY ON THIS SITE IS LIVE DATA.
X — @melaps_system · GitHub — MelapsSystem
CONTRACT $MELAPS — 0x49b5a9fcd24ca5083fa05f9136e7d04564647621
