Nodus Python SDK
Run a training job on rented GPUs from Python. Nodus places the work on the cheapest capacity that fits, checkpoints it, and resumes it if the machine is taken back — so a fine-tune that would have died at hour six finishes.
import nodus
with nodus.Client() as client:
wl = client.run(command=["python", "train.py"], budget=20)
print(client.wait(wl.id).status)
Install
pip install nodus_compute
The distribution is nodus_compute; the import is nodus.
Point it at your account
Two settings. Sign in at https://nodus.run/console/, create an API key — it is shown once — and the console's quickstart comes with both already filled in.
export NODUS_API_KEY=nk_live_…
export NODUS_BASE_URL=https://…
Or pass them directly: nodus.Client(api_key=…, base_url=…).
There is no built-in address. With neither setting the client raises
ConfigurationError before it opens a socket, naming what is missing — it does
not dial a guess and hand you a name-lookup error.
Running work
run() takes a flat brief and returns a Workload:
import nodus
with nodus.Client() as client:
wl = client.run(
image="python:3.11-slim",
command=["python", "train.py", "--epochs", "3"],
peak_memory_gb=24, # picks capacity that fits
expected_runtime_hours=6, # informs the estimate
budget=40, # a ceiling, not a reservation
)
done = client.wait(wl.id) # polls until the run is over
print(done.status, done.cost_now_usd)
Nothing is strictly required. Omit image and the default python:3.11-slim
fills in; omit command and the image's own entrypoint runs. Everything else
narrows the search or bounds the cost; omit budget and the run is uncapped —
the SDK warns rather than inventing a ceiling on your money.
Your image must be able to fetch a small binary — it needs curl, wget,
or python3 on the PATH. Most ML images have one. A bare ubuntu image has
none of them, and a machine that cannot fetch the runner is a machine you are
billed for while it does nothing.
Waiting
wait() polls until the workload is terminal and has no deadline of its
own. An 18-hour run is normal, and a client that gave up on one would not stop
it — the work would carry on and keep billing while your program believed it had
failed. A transient network failure does not end the wait either; only a
permanent one (a revoked key, an unknown workload) is raised.
Pass timeout_seconds= if you want a bound. It ends the waiting, not the run:
APITimeoutError is raised, the workload continues, and .cancel() is what
stops it.
Watching it
for event in client.stream_events(wl.id):
print(event.type, event.payload)
print(client.logs(wl.id)) # the job's own stdout and stderr
print(wl.refresh().cost_now_usd) # charged plus what is accruing right now
logs() is not a live tail. The log is a committed artifact, so it lags the
process by a checkpoint and raises NotFoundError until the first checkpoint
carries one — for live progress, watch the events.
Async
AsyncClient mirrors Client method for method:
async with nodus.AsyncClient() as client:
wl = await client.run(command=["python", "train.py"])
done = await client.wait(wl.id)
Errors
Every failure is a subclass of NodusError, so one except catches the lot.
The column that matters when writing a handler is whether the condition clears
on its own — the SDK already retries the ones that do, and never retries the
ones that do not.
| Raised | When | Clears on its own? |
|---|---|---|
ConfigurationError |
a setting is missing, before any request | never |
AuthenticationError |
the key is wrong or revoked | never |
SignatureError |
a signed request was rejected — the key is fine, the signature is not | never |
ValidationError |
the brief was rejected, with the reason | never |
IdempotencyConflictError |
an Idempotency-Key was reused with a different payload |
never |
NotFoundError |
no such workload | never |
BudgetExceededError |
the run would pass your account's spend cap | only if you lower the ask or raise the cap |
RateLimitError |
too many requests; honours Retry-After |
yes, with time |
CapacityUnavailableError |
nothing in the market fits the brief | yes |
APIConnectionError / APITimeoutError |
the network, not the API | yes |
APIError |
any other 4xx/5xx | for 5xx, usually |
Command line
Everything after a bare -- is your program's own command line, passed through
untouched:
nodus run --budget 20 -- python train.py
nodus get wl_…
nodus logs wl_…
Licence
Apache-2.0. See LICENSE.
Release files for nodus-compute 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nodus_compute-0.1.1.tar.gz | 65.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nodus_compute-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 103.9 kB
Release files / nodus_compute-0.1.1.tar.gz
| Download URL | nodus_compute-0.1.1.tar.gz |
|---|---|
| Size | 65.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9c9c54a56e959762b97795e23ac1e184f05cb95e63dedf5b6da00d003a1efaab
|
|
BLAKE2b-256 checksum How to use checksums |
934a664283284931ef75333e3db538f59c662514b1dbeee5a1135f8e38bff9dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.
Transparency logRelease files / nodus_compute-0.1.1-py3-none-any.whl
| Download URL | nodus_compute-0.1.1-py3-none-any.whl |
|---|---|
| Size | 38.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0eecd6e89edab07168d2a5071686b34fca7eced766daea0a2034ece7d6f01005
|
|
BLAKE2b-256 checksum How to use checksums |
2d8fc74f2da19cd6b5274c37905294d2d53735bf90f9f02f49a67e863e86045e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.
Transparency log