SAGEA LogoDocs & API
Cursus

SDK setup

Install the SDK and configure authentication to start logging runs.

Install

pip install sagea-cursus

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

export CURSUS_API_KEY="cursus_..."          # from <server>/<org>/settings/keys
export CURSUS_BASE_URL="https://cursus.example.com"  # omit for localhost:3000

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

PrecedenceAPI key sourceBase URL source
1 (highest)api_key= argumentbase_url= argument
2CURSUS_API_KEY env varCURSUS_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):

import sagea_cursus as cursus
 
run = cursus.init(
    project="demo",
    config={"lr": 1e-4},
    api_key="cursus_...",
    base_url="https://cursus.example.com",
)

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:

{
  "api_key": "cursus_...",
  "base_url": "https://cursus.example.com"
}

Starting a run

import sagea_cursus as cursus
 
run = cursus.init(project="demo", config={"lr": 1e-4})

Returns a Run with .id, .name, .url (dashboard link), and .config (a mutable dict mirror of the snapshot).

  • project resolves or is created on first use. Without group, the project is org-wide and visible to every member.
  • config is a frozen snapshot of hyperparameters. Use cursus.config.update() for mid-run changes (debounced, synced back to the server).
  • name is optional — the server generates one when omitted.
  • tags are free-text labels (max 32).
  • group scopes the project to a team (membership required).
  • sweep_id links 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:

run = cursus.init(project="demo", config={"lr": 1e-4})
try:
    # ... training loop ...
    cursus.finish()
except Exception:
    cursus.finish("crashed")
    raise
  • cursus.finish() — marks the run as finished
  • cursus.finish("crashed") or cursus.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

On this page