Packagenodus-sdk LanguagePython ≥ 3.10 Version1.0.0 APIv1 StatusStable

Workloads API

Submit requirements. Nodus matches a nodus:… catalog route, runs to verified completion, and recovers when capacity is reclaimed. You never pick a supplier.

# Playground

Describe the work and its constraints, not a machine SKU. Watch Nodus match a catalog route, run with checkpoints, and recover from a reclaim. No API key required.

Hardware fit and placement are Nodus decisions. You never pick a supplier.

Request

                
Customer view idle
workload
-
catalog sku
-
fit
-
est. cost
-
status
-
generation
-
spend_usd
-

# Install

From the monorepo (published PyPI later). Runtime dependency: httpx.

install ~
$ pip install -e sdk/python
# or: pip install nodus-sdk  (when published)

# Authentication

Every request carries Authorization: Bearer <api_key>. Sign in to the console with your email and password, then issue a key under Access. Keys are stored as a SHA-256 hash and shown once at creation, so copy it then. Revocation is checked on every request, so cutting off a leaked key takes effect on the next call, and revoking a key never signs you out of the console.

VariableDefaultNotes
NODUS_API_KEY Required. Issued from the console as nk_live_… or nk_test_….
NODUS_BASE_URL https://api.nodus.run Override for staging or a control plane you run yourself.

Nothing here is bound to a host. Set NODUS_BASE_URL or pass nodus.Client(base_url=…); the contract is the same.

# Quick start

Describe the work and constraints. Hardware fit, placement, and recovery stay with Nodus.

quickstart · python ~/your-app
# export NODUS_API_KEY=nk_live_…
>>> import nodus
>>>
>>> with nodus.Client() as client:
...     wl = client.run(
...         command=["train"],
...         model="7B fine-tune",
...         peak_memory_gb=24,
...         expected_runtime_hours=1,
...         budget=100,
...     )
...     done = client.wait(wl.id)
...     print(done.status, done.spend_usd)
WorkloadStatus.COMPLETED 0.02

# HTTP surface

The SDK is a thin client over this surface. Everything is tenant-scoped, so another tenant’s workload id reads as absent rather than forbidden. Submission rules are in retries & idempotency.

EndpointContract
POST /v1/workloadsSubmit a brief. Requires Idempotency-Key. Returns 202 with the workload id and revision; a replay repeats the original response and sets Idempotent-Replayed: true.
GET /v1/workloadsList for the authenticated tenant. limit (default 50, max 100), offset, and status — the presets active or terminal, or a comma-separated list of concrete statuses. The response carries next_offset when more rows remain.
GET /v1/workloads/{id}Status, customer route (nodus:…), spend, and per-stage progress
GET /v1/workloads/{id}/eventsOrdered lifecycle events. Pass after to read only what is new; up to 100 per call.
GET /v1/workloads/{id}/ledgerCustomer-safe ledger evidence + settlement
POST /v1/workloads/{id}/cancelSafe stop
GET /v1/workloads/{id}/artifactsVerified checkpoint / output manifests
PUT /v1/webhooksRegister signed customer webhook endpoint (https, public hosts only)
GET /v1/webhooksRead webhook config (secret omitted)
DELETE /v1/webhooksRemove webhook
POST /v1/console/signupRedeem an invite → tenant, console account, first API key, and a session. Tenant id is assigned, never chosen.
POST /v1/console/loginEmail and password → session token. One answer for every failure, so the form cannot be used to enumerate accounts.
POST /v1/console/logoutRevoke the current session
GET /v1/console/sessionWhoami for the current session
GET /v1/console/keysList key metadata. Never returns a secret. Session only; an API key is refused.
POST /v1/console/keysIssue a key. The raw value is in this response and nowhere else.
POST /v1/console/keys/{id}/revokeRevoke a key belonging to your tenant. Another tenant's id reads as absent.
GET /v1/console/membersRoster and open invitations. Any member may read it.
POST /v1/console/invitesAdmin only. Mints a one-time invitation token, returned once. Nodus does not email it.
POST /v1/console/invite/acceptRedeem an invitation into an account and a session. The email comes from the invite, never the request.
POST /v1/console/members/{id}/roleAdmin only. 409 rather than strand an account with no admin.
POST /v1/console/members/{id}/removeAdmin only. Signs them out everywhere by cascade.
POST /v1/console/passwordChange your own password. Signs out every other session.
POST /v1/console/members/{id}/resetAdmin only. Issues a one-time reset token for a locked-out teammate.
GET / PUT /v1/console/limitsRead or set the monthly spend cap. Setting it is admin only; a null cap means unbounded.
GET /v1/console/usageDaily spend series plus month-to-date and the cap.
GET /v1/console/auditConsole actions with actor: keys, members, invitations, limits.
POST /v1/signupLegacy: invite → tenant + key, with no console account. Prefer /v1/console/signup.
PUT /v1/billing/profileSet the billing contact
GET /v1/billing/invoicesList invoices
POST /v1/billing/invoicesInvoice unbilled charges for one workload
GET /healthzLiveness. No auth, no database read
GET /readyzReadiness. 503 while draining or when the database is unreachable

# CLI

The nodus command ships with the SDK and reads the same environment. Fastest way to submit a one-off brief from a terminal.

cli ~/your-app
$ export NODUS_API_KEY=nk_live_…

# submit and block until the work is done
$ nodus run \
      --model "7B fine-tune" \
      --peak-memory-gb 80 --hours 18 --budget 400 \
      --continuity checkpointed \
      --wait --timeout 72000 \
      -- python train.py --epochs 10

# inspect without blocking
$ nodus list --status active
$ nodus get wl_9f3c1b2a --wait
$ nodus events wl_9f3c1b2a --follow
$ nodus artifacts wl_9f3c1b2a
$ nodus cancel wl_9f3c1b2a

Everything after -- runs inside the workload. --wait exits non-zero on failed or cancelled, so it composes in CI.