SDK setup
Install the SDK and configure authentication to start logging runs.
Install
requests is the only hard dependency (Python 3.9+). Numpy and Pillow are optional extras — never install requirements.
The distribution is named sagea-cursus because cursus is taken on PyPI by an unrelated SageMaker tool. The product is still branded "Cursus".
Environment variables
Every call authenticates with an API key. Create one at <server>/<org>/settings/keys — shown once, copy it immediately. The API Keys page also shows your deployment's base URL with a copy-paste setup block.
Precedence
| Precedence | API key source | Base URL source |
|---|---|---|
| 1 (highest) | api_key= argument | base_url= argument |
| 2 | CURSUS_API_KEY env var | CURSUS_BASE_URL env var |
| 3 | ~/.cursus/config JSON {"api_key"} | ~/.cursus/config JSON {"base_url"} |
| default | — (raises) | http://localhost:3000 |
Explicit credentials
Credentials come from the environment above, or explicitly per call (handy for multi-server scripts — never commit real keys to git):
Without a key, init() raises RuntimeError. In CI prefer the env var; on a shared training box the config file avoids secrets in shell history.
Config file
For shared training boxes, create ~/.cursus/config:
Starting a run
Returns a Run with .id, .name, .url (dashboard link), and .config (a mutable dict mirror of the snapshot).
projectresolves or is created on first use. Withoutgroup, the project is org-wide and visible to every member.configis a frozen snapshot of hyperparameters. Usecursus.config.update()for mid-run changes (debounced, synced back to the server).nameis optional — the server generates one when omitted.tagsare free-text labels (max 32).groupscopes the project to a team (membership required).sweep_idlinks the run into a sweep (see Sweeps).
Trailing slashes on URLs are stripped. init() pings GET /api/v1/health and raises on an incompatible server API version — version skew fails fast at startup.
Finishing a run
Always finish in a finally block:
cursus.finish()— marks the run asfinishedcursus.finish("crashed")orcursus.finish("killed")— alternate statuses
There is no exit hook: if the process dies without finish(), buffered points (up to 5s / 50 points) are lost and the run stays RUNNING until the server declares it stale (15 minutes) and flips it to CRASHED.
With no active run, finish() is a silent no-op.
What's next
- Logging Guide — metrics, artifacts, images, and config sync
- Sweep Guide — grid and random hyperparameter search
- Troubleshooting
