Lium
from lium.sdk import Lium
Defined in lium.sdk.client.
Clean Unix-style SDK for Lium.
Lium(
config: Optional[lium.sdk.config.Config] = None,
source: str = 'sdk',
workspace: Optional[str] = None
)
Attributes
| Name | Type | Description |
|---|---|---|
config | ||
source | ||
headers | ||
api_keys | ||
CLUSTER_IMAGE_PREFIX | ||
JOB_ENV_VAR | ||
GPU_QUERY_FIELDS | ||
GPU_QUERY_COMMAND | ||
TERMINAL_POD_STATUSES | ||
MISSING_GRACE_SECONDS | ||
FIRST_POLL_SECONDS | ||
FIRST_POLL_WINDOW_SECONDS | ||
FAST_POLL_SECONDS | ||
FAST_POLL_WINDOW_SECONDS | ||
SLOW_POLL_SECONDS | ||
TRANSFER_KEY_ENV | ||
TRANSFER_KEY_LOCK |
Methods
| Method | Description |
|---|---|
features | Optional API capabilities the backend advertises on GET /version. |
supports | Whether the backend advertises feature (see features()). |
list_ssh_keys | Return SSH keys registered for the current user. |
register_ssh_key | Register a new SSH public key under the current user. |
default_ssh_key_name | cli-<user>@<host> sanitised to A-Za-z0-9.@-. |
up | Start a new pod on a specific node. |
pod_by_name | The pod called name — or whose huid or id is name — if it exists. |
rental | Rent a pod on executorid for the duration of a with block and always remove it. |
rent | Rent the cheapest available node that satisfies a spec, without listing the fleet. |
pod | Retrieve detailed information about a specific pod. |
logs | Stream logs from a pod. |
edit | Edit a pod's template configuration. |
ls | List available nodes. |
ps | List active pods. |
billing_statement | The account's pod charges grouped by pod (GET /billing/statement), most recently billed first. |
down | Stop a pod. |
rm | Remove pod (alias for down()). |
reboot | Reboot a pod. |
clusters | Groups of free nodes that sit on one RDMA fabric and can be rented as one cluster. |
cluster_offer | The offer for one fabric (exact id, or a unique prefix of it). |
cluster_template | The template every cluster rental must use (daturaai/lium-cluster); newest tag wins. |
up_cluster | Rent several nodes of one fabric as a single all-or-nothing cluster. |
my_clusters | The caller's cluster rentals, one Cluster per clusterid found in ps(). |
cluster | One cluster by id (exact, or a unique prefix), from the caller's pods. |
wait_cluster_ready | Poll until every member of cluster is RUNNING with SSH metadata. |
rm_cluster | Remove every member pod of a cluster with one DELETE /clusters/{clusterid}. |
get_default_images | Get default images for GPU type and driver version. |
default_docker_template | Resolve the best default template for a node ID. |
templates | List available templates. |
get_executor | Resolve a node by UUID or HUID against the same listing ls() returns. |
ambiguous_executor_message | The sentence for a HUID that is shared by several listed nodes. |
executor_not_found_message | The one sentence every caller prints when a node id resolves to nothing. |
gpu_types | Get list of available GPU types. |
gpu_short_types | The short GPU types the marketplace knows (H100, RTX4090, ...), sorted. |
unknown_gpu_type | None when gpushort names a known GPU type; otherwise the list of known types. |
request_machine | Ask providers for a GPU type that is sold out (POST /machine-requests). |
get_template | Fetch a template by ID/HUID/name. |
get_template_by_image_name | Fetch a template by its Docker image + tag. |
ssh_connection | SSH connection context manager. |
ssh_session | Keep one SSH connection to pod open for the whole block. |
exec | Execute a shell command on a pod over SSH. |
default_detach_log_path | /workspace/logs/exec-<UTC stamp>-<6 hex>.log; the tail keeps two. |
build_detached_command | The remote command line that starts command in the background and prints its PID. |
login_shell_env | (prelude, execenv) that make a bash -lc job see env. |
run_background | Start command in the background on a pod and return a Job to follow it. |
job | Re-attach to a job started earlier with runbackground(), by name. |
jobs | Every job that has a PID file under jobdir on the pod, running or finished. |
wait_for_port | Block until TCP port accepts connections inside the pod (a server that is up). |
parse_gpu_stats | Parse nvidia-smi --query-gpu=... --format=csv,noheader,nounits output. |
gpu_stats | Per-GPU utilization, memory, temperature and power on a pod, via nvidia-smi. |
stream_exec | Execute a shell command and stream incremental output. |
exec_all | Execute a shell command on multiple pods in parallel. |
poll_delay | Seconds to sleep between two waitready polls. |
pod_events | The pod's event log, oldest first — creation, reboots, failures with their error, and. |
pod_failure_cause | What the backend recorded as the reason the pod failed or was closed, or None. |
wait_ready | Poll until a pod reports RUNNING + SSH metadata. |
scp | Upload a local file to a pod via SFTP. |
download | Download a file from a pod via SFTP. |
upload | Upload a file to a pod via SFTP. |
ssh_argv | The OpenSSH argument list that opens a shell on a pod. |
refresh_pod | Re-read one pod from the API. |
ssh | Get SSH command string for connecting to a pod. |
rsync_options | The rsync flags shared by rsync() and cp(). |
rsync | Sync files between the local machine and a pod with rsync. |
cp | Copy files from one pod to another over SSH, without passing through this machine. |
transfer_keypair | A fresh ed25519 key pair for one copy, as OpenSSH text. |
grant_transfer_key_command | The remote line that appends authorizedline to the pod's authorizedkeys. |
revoke_transfer_key_command | The remote line that drops every authorizedkeys entry carrying publickey. |
switch_template | Switch the template of a running pod. |
create_template | Create a new template. |
wait_template_ready | Wait for template verification to complete. |
me | The account the API key belongs to, as GET /users/me returns it. |
get_my_user_id | Get the current user's ID. |
update_template | Update an existing template owned by the caller. |
wallets | Get the caller's configured funding wallets. |
add_wallet | Link your wallet to the user account. |
alpha_subnets | The subnets whose alpha Lium accepts as payment (GET /balance/alpha/subnets). |
convert_alpha | Quote usd (USD) -> alpha via GET /balance/convert/alpha. |
company_wallet | Resolve the Lium deposit address via GET /wallet/company/?appid=. |
backup_create | Create or replace a backup configuration for a pod. |
backup_update | Replace the schedule of an existing backup configuration in place. |
backup_now | Trigger an immediate backup for a pod. |
backup_config | Return the backup configuration for a pod if one exists. |
backup_list | List all backup configurations across all pods. |
backup_logs | Get recent backup logs for a pod. |
backup_logs_all | Get all backup logs available to the current user. |
backup_log | Get one backup log owned by the authenticated user. |
resolve_backup_id | Resolve an eight-character backup ID shown by the CLI. |
backup_delete | Delete a backup configuration by ID. |
backup_cancel | Request cancellation of an active backup while retaining its history. |
backup_log_delete | Delete the stored data for a completed backup and retain its audit row. |
restore | Restore a backup to a pod. |
restore_logs | Get recent restore logs for a pod. |
resolve_restore_id | Resolve an eight-character restore ID shown by the CLI. |
restore_cancel | Request cancellation of an active restore. |
get_deployment_estimate | Estimate deployment time for a template on a node. |
balance | Get current account balance. |
balance_or_none | The balance from /users/me, or None when the answer has no balance field — where. |
events | The account's event log, newest first (GET /users/me/events). |
audit_log | One page of the account audit log, newest first (GET /account/audit). |
topup_currencies | List stablecoin currencies/networks supported for self-serve top-ups. |
topup_create_invoice | Create a stablecoin top-up invoice for the current account. |
topup_checkout_link | Open a Stripe Checkout page that tops up this account by card (POST /stripe/create-checkout-session). |
wait_for_credit | Poll the balance until it rises above baseline (a top-up landed) or timeout seconds pass. |
volumes | List all volumes for the current user. |
volume | Get a specific volume by ID. |
volume_create | Create a new volume. |
volume_update | Update a volume's metadata. |
volume_delete | Delete a volume. |
schedule_termination | Schedule a pod for automatic termination at a future date and time. |
cap_spend | Schedule the pod's removal for the moment it will have spent budgetusd. |
cancel_scheduled_termination | Cancel a scheduled termination for a pod. |
install_jupyter | Install Jupyter Notebook on a pod. |
features
def features() -> set:
Optional API capabilities the backend advertises on GET /version.
Read once per client. A backend that predates the list, or one that cannot be reached, advertises nothing — callers then take their client-side path.
supports
def supports(feature: str) -> bool:
Whether the backend advertises feature (see features()).
list_ssh_keys
def list_ssh_keys() -> List[lium.sdk.models.SSHKey]:
Return SSH keys registered for the current user.
register_ssh_key
def register_ssh_key(*, name: str, public_key: str) -> lium.sdk.models.SSHKey:
Register a new SSH public key under the current user.
default_ssh_key_name
def default_ssh_key_name() -> str:
cli-<user>@<host> sanitised to [A-Za-z0-9._@-].
up
def up(
*,
executor_id: str,
name: str = 'Your Pod',
template_id: Optional[str] = None,
image: Optional[str] = None,
dockerfile_content: Optional[str] = None,
volume_id: Optional[str] = None,
ports: Optional[int] = None,
ssh_keys: Optional[List[str]] = None,
ssh_name: Optional[str] = None,
enable_volume_encryption: bool | None = True,
backup_id: Optional[str] = None,
restore_path: Optional[str] = None,
gpu_count: Optional[int] = None,
wait: bool = False,
timeout: int = 600
) -> Union[Dict[str, Any], lium.sdk.models.PodInfo]:
Start a new pod on a specific node.
Arguments:
- executor_id: Target node ID string.
- name: Human-friendly pod name (defaults to
"Your Pod"). - template_id: Template ID. Defaults to the node's default template.
Mutually exclusive with
imageanddockerfile_content. - image: Docker image to run, passed to the backend whole
(
"repo/name:tag","registry:5000/team/img","repo/name@sha256:<hex>"; the backend splits name, tag and digest and defaults the tag tolatest), the same aslium up --image: a private one-time template is created for it with port 22 exposed and the image's own entrypoint/command, and deleted with the pod. Mutually exclusive withtemplate_idanddockerfile_content. - dockerfile_content: Raw Dockerfile text to build the pod image from on
the node (custom build). Mutually exclusive with
template_id— pass exactly one. The image is built remotely with no network access, so the Dockerfile must be self-contained (noADD <url>orADD $\{var\}directives). - volume_id: Optional volume ID to attach on spawn.
- ports: Number of exposed ports to request.
- ssh_keys: SSH public keys to authorize. Defaults to the keys discovered by the Config.
- ssh_name: Optional name to use when registering a new SSH key with the
backend. Defaults to
cli-<user>@<hostname>. Only applied to keys that are not already registered server-side. - enable_volume_encryption: Whether to request encryption for the local pod volume. Enabled by default. The image must support Lium volume encryption.
- backup_id: Optional backup ID to restore after the pod starts.
- restore_path: New or empty subdirectory where the backup is restored.
Required when
backup_idis provided. - wait: When
True, block until the pod is RUNNING with an SSH endpoint and return it as aPodInfo. The pod is billing from the moment the rent call returns, so a timeout raises aLiumErrorthat names the pod id. - timeout: Seconds to wait for readiness when
waitis set. - gpu_count: Rent only this many of the node's GPUs (GPU splitting).
Nonetakes every GPU that is free on the node right now (the whole node when none of it is rented). The API rejects a count above the free GPUs, below the provider's minimum, or on nodes that do not allow splitting.
Returns:
Pod metadata as returned by the rent API (id, name, status, ssh command,
etc.), or the ready PodInfo when wait is True.
pod_by_name
def pod_by_name(name: str) -> Optional[lium.sdk.models.PodInfo]:
The pod called name — or whose huid or id is name — if it exists.
Names are chosen by the caller and huids are what lium ps prints, so
both are accepted; the first match wins when several pods share a name.
Arguments:
- name: Pod name, huid, or id.
Returns:
The matching PodInfo, or None.
rental
def rental(
*,
executor_id: str,
timeout: int = 600,
**up_kwargs: Any
) -> Generator[lium.sdk.models.PodInfo, NoneType, NoneType]:
Rent a pod on executor_id for the duration of a with block and always remove it.
(rent() is the other way to rent: by spec, returning a RentResult
the caller owns; rental is the scoped form that cleans up after itself.)
The pod is removed on the way out whether the block returned, raised, or was interrupted, and also when the pod was created but never became ready. A pod that outlives the code that needed it is the most common way to pay for nothing.
Arguments:
- executor_id: Target node ID.
- timeout: Seconds to wait for the pod to become ready.
- **up_kwargs: Any other keyword argument
up()accepts (name,template_id,ports...).
Yields:
The ready PodInfo.
Example:
with lium.rental(executor_id=node.id, name="job") as pod:
... lium.exec(pod, command="nvidia-smi")
rent
def rent(
*,
gpu_type: str,
gpu_count: int = 1,
name: str = 'Your Pod',
template_id: Optional[str] = None,
dockerfile_content: Optional[str] = None,
min_vram_gb: Optional[float] = None,
min_cpus: Optional[int] = None,
min_ram_gb: Optional[float] = None,
min_disk_gb: Optional[float] = None,
min_download_mbps: Optional[float] = None,
min_ports: Optional[int] = None,
max_price_per_gpu_hour: Optional[float] = None,
country: Optional[str] = None,
docker_in_docker: Optional[bool] = None,
interconnect: Optional[str] = None,
volume_id: Optional[str] = None,
ports: Optional[int] = None,
ssh_keys: Optional[List[str]] = None,
ssh_name: Optional[str] = None,
enable_volume_encryption: bool | None = True,
backup_id: Optional[str] = None,
restore_path: Optional[str] = None,
dry_run: bool = False
) -> lium.sdk.models.RentResult:
Rent the cheapest available node that satisfies a spec, without listing the fleet.
When the backend advertises rent_by_spec (GET /version), one call to
POST /executors/rent-by-spec selects and rents: the server picks the cheapest
$/GPU·h node that meets every constraint (ties: faster ingress, then reliability,
then id), rents it under its own locks and, if that node is taken meanwhile, tries the
next candidate. Against an older backend the same arguments make today's client-side
pick — list, filter, cheapest exact match — and rent it with up().
Arguments:
- gpu_type: Short or full GPU name —
"H100","RTX4090","NVIDIA H200". - gpu_count: GPUs to rent (default 1). Server-side this also admits a split of a larger node when its provider allows one; client-side it is the node's size.
- name: Pod name.
- template_id: Template to run. Omitted: the node's recommended image.
Mutually exclusive with
dockerfile_content. - dockerfile_content: Build the image from this Dockerfile instead (see
up()). - min_vram_gb, min_cpus, min_ram_gb, min_disk_gb, min_download_mbps, min_ports: Floors on the host; a host that does not report the figure does not qualify.
- max_price_per_gpu_hour: Ceiling on
price_per_gpu. - country: ISO country code.
- docker_in_docker: Require a sysbox host.
- interconnect:
"nvlink"— every GPU pair on NVLink (unreported counts as no). Client-side only: a rent-by-spec backend has no such field, so the call is refused there rather than sent with a constraint the server would not check. - volume_id, ports, ssh_keys, ssh_name, enable_volume_encryption, backup_id,
restore_path: as in
up(). - dry_run: Choose and price only; nothing is rented and no SSH key is registered
with the account. Against a rent-by-spec backend the request still carries
your SSH public key (
ssh_keysor the configured key: the server validates a dry run as a rent); the client-side pick needs no key.
Returns:
RentResult — the node, the GPUs rented, the hourly price, the pod (None
on a dry run), the template used, how many nodes qualified and the runners-up.
Raises:
- LiumError: No node satisfies the spec. The message names the constraint that
left no candidate and the best value on offer, e.g.
min_cpus=64: none of the 12 node(s) matching the earlier constraints satisfies it; the best on offer is 48. - LiumError:
interconnectwas given and the backend rents by spec (nothing is registered or rented). - LiumServerError, requests.RequestException: the rent-by-spec response was lost. The
rent is posted once (a repeat could rent a second node); the pod is looked up
by name for ~9 s and returned when it appears, otherwise the error propagates
and the pod may still exist — check
ps().
pod
def pod(pod_id: str) -> Dict[str, Any]:
Retrieve detailed information about a specific pod.
Arguments:
- pod_id: The unique identifier of the pod to retrieve.
Returns: Raw pod data dictionary including template, node, status, and connection info.
logs
def logs(
pod_id: str,
*,
tail: int = 100,
follow: bool = False
) -> Generator[bytes, NoneType, NoneType]:
Stream logs from a pod.
Arguments:
- pod_id: The unique identifier of the pod.
- tail: Number of lines to retrieve from the end of the logs (default: 100).
- follow: If True, stream logs continuously (default: False).
Yields: Log lines as bytes.
edit
def edit(pod_id: str, **kwargs) -> Dict[str, Any]:
Edit a pod's template configuration.
Updates the template associated with a pod by merging the provided keyword arguments with the existing template settings.
Arguments:
- pod_id: The unique identifier of the pod whose template to edit.
**kwargs: Template fields to update. Common fields include:- docker_image (str): Docker image repository.
- docker_image_tag (str): Docker image tag.
- startup_commands (str): Commands to run on container start.
- internal_ports (List[int]): Ports to expose.
- environment (Dict[str, str]): Environment variables.
- volumes (List[str]): Volume mount paths.
Returns: Updated template data dictionary from the API.
Example:
lium.edit(pod_id, startup_commands="python main.py", environment={"DEBUG": "1"})
ls
def ls(
*,
gpu_type: Optional[str] = None,
gpu_count: Optional[int] = None,
lat: Optional[float] = None,
lon: Optional[float] = None,
max_distance_miles: Optional[int] = None,
min_cuda_version: Optional[float] = None,
min_cpus: Optional[int] = None,
nvlink: Optional[bool] = None,
min_download_mbps: Optional[float] = None,
view: str = 'summary'
) -> List[lium.sdk.models.ExecutorInfo]:
List available nodes.
Arguments:
- gpu_type: Optional GPU filter such as
"A100"or"H200". - gpu_count: Exact GPU count to match (defaults to 8, pass
Noneto disable). - lat: Optional latitude for geospatial filtering. Must be used together with
lonandmax_distance_miles. - lon: Optional longitude for geospatial filtering. Must be used together with
latandmax_distance_miles. - max_distance_miles: Optional radius (in miles) for geospatial filtering. Must be used together with
latandlon. - min_cuda_version: Optional minimum CUDA version to require (e.g.
12.4). Nodes whosemax_cuda_versionisNoneor below this threshold are excluded. NVIDIA drivers are backward compatible, so a node with a higher driver CUDA version satisfies the requirement. - min_cpus: Optional minimum CPU thread count (
specs.cpu.count). Nodes that report fewer CPUs, or none, are excluded. - nvlink:
Truekeeps only nodes where Lium's node checks saw every GPU pair on NVLink (nvlink). Nodes with no verdict yet are excluded — a renter who asks for NVLink must not be handed a PCIe box.False/Nonedo not filter. - min_download_mbps: Minimum Download in Mbps, judged on
effective_download_speed_mbps(the figurelium lsshows as Download). Nodes with no figure are excluded. - view:
"summary"(default) asks the API for the fields a listing reads — price, GPU/CPU/RAM/disk headline specs, location, tier, network; an API that does not know the parameter returns the full row."full"asks for the whole node-check scrape inspecs(docker info, verified ports, per-GPU telemetry, checksums).
Returns:
A list of ExecutorInfo objects that satisfy the filters.
ps
def ps(*, api_key_id: Optional[str] = None) -> List[lium.sdk.models.PodInfo]:
List active pods.
Arguments:
- api_key_id: Only the pods rented through this API key (
GET /pods?api_key_id=…, server support pending). A server without the filter ignores the parameter and lists every pod the caller can see.
Returns:
List of PodInfo objects representing the caller's running pods.
billing_statement
def billing_statement(
*,
start_day: Optional[str] = None,
end_day: Optional[str] = None,
api_key_id: Optional[str] = None
) -> Dict[str, Any]:
The account's pod charges grouped by pod (GET /billing/statement), most recently billed first.
Every pod the account was charged for in the period — removed pods included — with days, one
entry per ledger row, oldest first; a UTC day can have more than one row (for example when billing
stops and resumes). Each pod's total is what the ledger
debited. start_day / end_day are UTC billing days (YYYY-MM-DD), both inclusive and
optional. api_key_id keeps only the pods rented
through that key (the rows then carry api_key_name); a server without the
filter ignores it. Returns the server's \{"start_day", "end_day", "total", "pods": [...]\}.
A key needs the read scope.
down
def down(pod: lium.sdk.models.PodInfo) -> Dict[str, Any]:
Stop a pod.
Arguments:
- pod: Pod to terminate.
Returns: API response payload from the delete call.
rm
def rm(pod: lium.sdk.models.PodInfo) -> Dict[str, Any]:
Remove pod (alias for down()).
Arguments:
- pod: Pod to terminate.
Returns: API response payload from the delete call.
reboot
def reboot(
pod: lium.sdk.models.PodInfo,
volume_id: Optional[str] = None
) -> Dict[str, Any]:
Reboot a pod.
Arguments:
- pod: Pod to reboot.
- volume_id: Optional volume ID to attach for the reboot request.
Returns: Pod data from the API response after issuing the reboot.
clusters
def clusters() -> List[lium.sdk.models.ClusterOffer]:
Groups of free nodes that sit on one RDMA fabric and can be rented as one cluster.
GET /executors/infiniband-clusters. Each offer names the fabric, whether it is
InfiniBand or RoCE, whether the wire between the members was measured, how many
nodes the fabric has and which of them are free right now.
cluster_offer
def cluster_offer(fabric_id: str) -> Optional[lium.sdk.models.ClusterOffer]:
The offer for one fabric (exact id, or a unique prefix of it).
cluster_template
def cluster_template() -> lium.sdk.models.Template:
The template every cluster rental must use (daturaai/lium-cluster); newest tag wins.
up_cluster
def up_cluster(
executor_ids: List[str],
*,
name: str,
template_id: Optional[str] = None,
ssh_keys: Optional[List[str]] = None,
ssh_name: Optional[str] = None,
ports: Optional[int] = None,
enable_volume_encryption: bool | None = True,
wait: bool = False,
timeout: int = 900
) -> lium.sdk.models.Cluster:
Rent several nodes of one fabric as a single all-or-nothing cluster.
POST /executors/cluster/rent. Every node is taken whole and runs the cluster
template, which raises the private overlay each member uses to find its peers.
The same name is given to every member pod; the members share cluster_id.
Arguments:
- executor_ids: Nodes to rent; they must all be on one fabric (see
clusters()). - name: Pod name applied to every member.
- template_id: Defaults to
cluster_template(). - ssh_keys: Public keys to install; defaults to the configured key.
- ports: Ports to expose per node.
- enable_volume_encryption: As for
up(). - wait: Block until every member is RUNNING with SSH (
timeoutseconds).
Raises:
- ValueError: fewer than two nodes, or no SSH key.
- ClusterNotListedError: the nodes are, or may be, rented, but the listing did not show a whole cluster: the API confirmed the order and named the member pods but the listing did not show every one of them (or failed); or the order got no answer and the by-name lookup found fewer members than requested (or could not list). Do not rent again.
- LiumError: the API refused the group (mixed fabrics, split hosts, wrong image); the pod listing before the order failed (nothing was rented); or the order got no answer and no member appeared.
my_clusters
def my_clusters() -> List[lium.sdk.models.Cluster]:
The caller's cluster rentals, one Cluster per cluster_id found in ps().
cluster
def cluster(cluster_id: str) -> lium.sdk.models.Cluster:
One cluster by id (exact, or a unique prefix), from the caller's pods.
Raises:
- LiumNotFoundError: no pod carries that cluster id.
wait_cluster_ready
def wait_cluster_ready(
cluster: lium.sdk.models.Cluster,
*,
timeout: int = 900,
poll_interval: int = 10
) -> lium.sdk.models.Cluster:
Poll until every member of cluster is RUNNING with SSH metadata.
The members waited for are the pods of cluster as given: one of them missing from any
poll, the first included, is a member that vanished, not one that is slow.
Raises:
- PodStartError: a member reached a terminal status (
FAILED,STOPPED, …) or is missing from the pod list. Reported at once, not at the deadline, since the other members keep billing. - TimeoutError: some member was still not ready after
timeoutseconds (all members keep billing).
rm_cluster
def rm_cluster(cluster: lium.sdk.models.Cluster) -> List[Dict[str, Any]]:
Remove every member pod of a cluster with one DELETE /clusters/\{cluster_id\}.
Returns one \{"pod", "huid", "name", "node_rank", "success", "message", "error"\} per
member, in the order the server reports them (huid is the short name lium rm accepts). The server finds the members by the
cluster id, checks ownership and API-key scope on every one of them before the first
delete, then tears them down one by one; a member that failed is reported with success
false and its error text while the others are still removed, so nothing is left billing by
accident. Call again to retry the members that failed. The pinned ssh host key of every
member the server accepted is dropped, as rm() does for a single pod.
There is no per-pod fallback: a 404 from the route removes nothing and is raised.
Raises:
- LiumNotFoundError: no pod carries the id (the cluster is already gone), or the server
has no
DELETE /clusters/\{id\}route yet. Nothing was removed. - LiumPermissionError: a member belongs to another user, or the API key lacks the
managescope for a pod it did not create. Nothing was removed. - LiumError: 409 — a member is still being created and the platform is set not to cancel in-flight creates; nothing was removed, call again later. Also a 200 without the per-member rows, so an unexpected answer is never read as "removed".
get_default_images
def get_default_images(gpu_model: Optional[str], driver_version: Optional[str]) -> list[dict]:
Get default images for GPU type and driver version.
default_docker_template
def default_docker_template(executor_id: str) -> lium.sdk.models.Template:
Resolve the best default template for a node ID.
Arguments:
- executor_id: Node identifier returned by
ls().
Returns:
Template best suited for the node.
Raises:
- ValueError: If no matching node or template exists.
templates
def templates(
filter: Optional[str] = None,
only_my: bool = False
) -> List[lium.sdk.models.Template]:
List available templates.
Arguments:
- filter: Optional substring to filter by image or name.
- only_my: When
Truereturn only templates owned by the caller.
Returns:
List of Template.
get_executor
def get_executor(executor: str) -> Optional[lium.sdk.models.ExecutorInfo]:
Resolve a node by UUID or HUID against the same listing ls() returns.
Arguments:
- executor: Node UUID, or the HUID
lium lsprints for it (cosmic-hawk-f2).
Returns:
Matching ExecutorInfo or None if no listed node has that id.
Raises:
- ValueError: the HUID names more than one listed node. HUIDs are
client-side draws (10 adjectives × 10 nouns × 256 tails), so a
full listing can hold two nodes with the same one; picking the
first in API order would rent a different node than the one
lium lsshowed, so the caller is asked for the UUID instead.
ambiguous_executor_message
def ambiguous_executor_message(executor: str, matches: List[lium.sdk.models.ExecutorInfo]) -> str:
The sentence for a HUID that is shared by several listed nodes.
executor_not_found_message
def executor_not_found_message(executor: str) -> str:
The one sentence every caller prints when a node id resolves to nothing.
gpu_types
def gpu_types() -> set[str]:
Get list of available GPU types.
Returns: Set of GPU type strings advertised by the API.
gpu_short_types
def gpu_short_types() -> List[str]:
The short GPU types the marketplace knows (H100, RTX4090, ...), sorted.
These are the values --gpu / ls(gpu_type=) accept; a bare model number
(4090) and spacing/case variants (rtx 4090) resolve to them too. Names
the extractor could not type (it falls back to the last word: Ti, SUPER,
V) are left out — they are not something --gpu can usefully take.
unknown_gpu_type
def unknown_gpu_type(gpu_short: str) -> Optional[List[str]]:
None when gpu_short names a known GPU type; otherwise the list of known types.
Lets a caller tell "every 4090 is rented" from "nothing is called 4090" — the
second case is what a typo or an unsupported spelling produces, and the two need
different messages. Never raises: on an API failure the answer is None
(assume known), so a listing failure is reported as such and not as a typo.
request_machine
def request_machine(gpu: str, count: Optional[int] = None) -> Dict[str, Any]:
Ask providers for a GPU type that is sold out (POST /machine-requests).
gpu is a short type (B300, 4090) or a full catalog name. The providers who
list that type are told, and the request shows in lium.io/machine-requests. A short
type that covers several catalog names must be given by one of them.
Returns:
The API's answer: provider_notified_count and, when present, confirmation.
get_template
def get_template(template_id: str) -> Optional[lium.sdk.models.Template]:
Fetch a template by ID/HUID/name.
Arguments:
- template_id: Template ID, HUID, or name to match.
Returns:
Matching Template or None if not found.
get_template_by_image_name
def get_template_by_image_name(
image_name: Optional[str] = None,
image_tag: Optional[str] = None
) -> Optional[lium.sdk.models.Template]:
Fetch a template by its Docker image + tag.
Arguments:
- image_name: Repository/image name.
- image_tag: Tag to match.
Returns:
Matching Template or None if not found.
ssh_connection
def ssh_connection(pod: lium.sdk.models.PodInfo, timeout: int = 30):
SSH connection context manager.
Arguments:
- pod: Pod whose SSH metadata is used.
- timeout: Connection timeout in seconds.
Yields:
An active paramiko.SSHClient.
Raises:
- ValueError: no
ssh_cmdor no SSH key configured, orpod.ssh_cmdis notssh <user>@<host> [-p <port>](ssh_target(), the check the OpenSSH path applies). Nothing is connected then.
ssh_session
def ssh_session(pod: lium.sdk.models.PodInfo, timeout: int = 30):
Keep one SSH connection to pod open for the whole block.
Every exec(), stream_exec(), upload() and download()
inside it runs over this connection instead of paying a fresh TCP + SSH
handshake each (several seconds per call to a distant node).
Yields:
The active paramiko.SSHClient.
exec
def exec(
pod: lium.sdk.models.PodInfo,
*,
command: str,
env: Optional[Dict[str, str]] = None,
timeout: Optional[float] = None,
detach: bool = False,
log_path: Optional[str] = None
) -> Dict[str, Any]:
Execute a shell command on a pod over SSH.
Arguments:
- pod: Pod to target.
- command: Shell command to run remotely.
- env: Optional environment variables exported before the command runs.
The values are sent over the session's stdin, not in the remote
command line, so
pson the pod never shows them. - timeout: Seconds to wait for the command to finish. When it runs
out the channel is closed and
TimeoutErroris raised; the remote process may keep running. Withdetachit bounds only the launcher (default 60 s), never the job. - detach: Start the command in the background on the pod and return
at once. The command runs under
nohup setsidwith stdin closed and stdout/stderr tolog_path, so it survives the SSH session ending — the shape every user otherwise rediscovers by hand.envstill travels over stdin, and is applied inside the detached login shell after its profile, so the given value wins. - log_path: Log file for
detach(default/workspace/logs/exec-<UTC timestamp>-<id>.log, the<id>a 6-hex tail that keeps two launches in the same second apart).
Returns:
Dict containing stdout, stderr, exit_code, and success flag; with
detach a dict with pid, log_path and command.
Raises:
- ValueError: an
envname is not a shell identifier. Raised before the connection is opened, so nothing reaches the pod.
default_detach_log_path
def default_detach_log_path() -> str:
/workspace/logs/exec-<UTC stamp>-<6 hex>.log; the tail keeps two
jobs started in the same second from sharing (and truncating) one file.
build_detached_command
def build_detached_command(command: str, log_path: str) -> str:
The remote command line that starts command in the background and prints its PID.
nohup alone is not enough over a non-interactive SSH session: the child
keeps the session's stdin and its process group, so exec blocks until
the child exits, and a closed session can still take the child down.
setsid gives it its own session, < /dev/null detaches stdin, and both
output streams go to the log file. echo $! is the only thing the caller
reads back on stdout.
login_shell_env
def login_shell_env(
cls,
env: Optional[Dict[str, str]]
) -> Tuple[str, Optional[Dict[str, str]]]:
(prelude, exec_env) that make a bash -lc job see env.
prelude goes in front of the job's command inside the login shell;
exec_env is what the launching exec() call exports over stdin.
Both are empty when env is. lium exec --detach uses it the same
way for the launcher it builds itself. An export the job shell cannot
apply (a name bash keeps read-only, such as UID, or one the pod's
profile marked readonly) ends the shell with exit 1 and the reason
in the log; the command never runs with a different environment than
the one asked for.
Raises:
- ValueError: an
envname is not a shell identifier, or isJOB_ENV_VARitself (the carrier, unset before the command runs).
run_background
def run_background(
pod: lium.sdk.models.PodInfo,
command: str,
*,
name: Optional[str] = None,
env: Optional[Dict[str, str]] = None,
workdir: Optional[str] = None,
job_dir: str = '/workspace/logs',
timeout: float = 60
) -> lium.sdk.jobs.Job:
Start command in the background on a pod and return a Job to follow it.
The job survives this SSH session and this process: it runs under
nohup setsid with stdin closed, logs to <job_dir>/<name>.log, records
its PID in <name>.pid (with the process's boot id and start time in
<name>.id, so a reused PID never passes as the job; a job without
that file is never trusted either) and its exit code
in <name>.exit when it ends.
job() re-attaches later by name.
Arguments:
- pod: Pod to run on.
- command: Shell command (run through
bash -lc, so the login environment — conda, PATH — applies). - name: Job name, also the stem of its files; default
job-<UTC timestamp>. Refused when a job of that name is still running on the pod. - env: Environment variables for the job. Sent over the session's
stdin like
exec()does, never in a command line, and applied inside the job shell after its login profile, so the given value wins. - workdir: Directory to
cdinto first. - job_dir: Where the job files live (default
/workspace/logs, the fast local volume, not the encrypted/root). - timeout: Seconds allowed for the launcher itself (not the job).
Raises:
- LiumError: the launcher printed no PID, or the name is taken by a live job.
job
def job(
pod: lium.sdk.models.PodInfo,
name: str,
*,
job_dir: str = '/workspace/logs'
) -> lium.sdk.jobs.Job:
Re-attach to a job started earlier with run_background(), by name.
Raises:
- LiumNotFoundError: no job of that name has files on the pod.
jobs
def jobs(
pod: lium.sdk.models.PodInfo,
*,
job_dir: str = '/workspace/logs'
) -> List[lium.sdk.jobs.Job]:
Every job that has a PID file under job_dir on the pod, running or finished.
wait_for_port
def wait_for_port(
pod: lium.sdk.models.PodInfo,
port: int,
*,
timeout: float = 600,
host: str = '127.0.0.1',
poll_interval: float = 3
) -> None:
Block until TCP port accepts connections inside the pod (a server that is up).
The probe runs on the pod over SSH (bash /dev/tcp, no nc needed),
so it sees the port as the pod does. For a job started with
run_background() prefer wait_for_port(), which also
stops early when the job dies.
Raises:
- TimeoutError: the port did not answer within
timeoutseconds.
parse_gpu_stats
def parse_gpu_stats(csv_text: str) -> List[lium.sdk.models.GpuStats]:
Parse nvidia-smi --query-gpu=... --format=csv,noheader,nounits output.
[N/A] and [Not Supported] become None and the rest of the
reading is kept; a GPU whose power sensor is missing still has a
utilization figure worth showing.
gpu_stats
def gpu_stats(
pod: lium.sdk.models.PodInfo,
*,
timeout: float = 30
) -> List[lium.sdk.models.GpuStats]:
Per-GPU utilization, memory, temperature and power on a pod, via nvidia-smi.
Raises:
- LiumError: when
nvidia-smifailed or printed nothing usable.
stream_exec
def stream_exec(
pod: lium.sdk.models.PodInfo,
*,
command: str,
env: Optional[Dict[str, str]] = None,
pty: bool = True
) -> Generator[Dict[str, str], NoneType, int]:
Execute a shell command and stream incremental output.
Args:
pod: Pod to target.
command: Shell command to run remotely.
env: Optional environment variables exported before the command runs.
pty: Request a pseudo-terminal (default). A pty merges stderr into stdout
and turns into ; pass False to keep the two
streams apart, as lium.machine() does to relay a function's output.
Yields:
Streaming output chunks as \{"type": "stdout"|"stderr", "data": str\}.
Returns:
The command's exit status (the generator's StopIteration.value).
exec_all
def exec_all(
pods: List[lium.sdk.models.PodInfo],
*,
command: str,
env: Optional[Dict[str, str]] = None,
max_workers: int = 10
) -> List[Dict]:
Execute a shell command on multiple pods in parallel.
Arguments:
- pods: List of pods to target.
- command: Shell command to run on each pod.
- env: Optional environment variables exported before each command.
- max_workers: Maximum number of SSH workers to spawn.
Returns:
List of result dictionaries mirroring exec(), each with the pod
id under "pod". When SSH fails for a pod its entry is
\{"pod": <id>, "error": <message>, "success": False\} — same key,
same type as the successful entries, so callers can index results
by pod id without checking which shape they got.
Raises:
- ValueError: an
envname is not a shell identifier. Checked once, before any pod is contacted — a caller's mistake is not one failure entry per pod.
poll_delay
def poll_delay(cls, elapsed: float, poll_interval: Optional[int] = None) -> float:
Seconds to sleep between two wait_ready polls.
A caller-given poll_interval is used as-is; None selects the adaptive
schedule (FIRST_POLL_SECONDS for the first FIRST_POLL_WINDOW_SECONDS
seconds, FAST_POLL_SECONDS until FAST_POLL_WINDOW_SECONDS,
SLOW_POLL_SECONDS after that).
pod_events
def pod_events(pod_id: str) -> List[Dict[str, Any]]:
The pod's event log, oldest first — creation, reboots, failures with their error, and lifecycle entries saying why it left RUNNING. Answers for a pod whose row is already gone.
Returns [] against a backend that predates the endpoint.
pod_failure_cause
def pod_failure_cause(pod_id: str) -> Optional[str]:
What the backend recorded as the reason the pod failed or was closed, or None.
The latest event carrying an error (a failed create or reboot: the node's error headline)
or a lifecycle reason/detail wins. Never raises — this is read on a failure path.
wait_ready
def wait_ready(
pod: Union[str, lium.sdk.models.PodInfo, Dict],
*,
timeout: Optional[int] = 300,
poll_interval: Optional[int] = None,
on_poll: Optional[Callable[[Optional[lium.sdk.models.PodInfo], str, float], NoneType]] = None,
ready_port: Optional[int] = None
) -> Optional[lium.sdk.models.PodInfo]:
Poll until a pod reports RUNNING + SSH metadata.
Arguments:
- pod: Pod identifier, PodInfo, or dict with an
idfield. - timeout: Maximum number of seconds to wait;
Nonewaits until the pod is ready or fails. - poll_interval: Fixed interval between successive
pscalls;None(default) polls everyFIRST_POLL_SECONDSfor the firstFIRST_POLL_WINDOW_SECONDSseconds, everyFAST_POLL_SECONDSuntilFAST_POLL_WINDOW_SECONDS, then everySLOW_POLL_SECONDS— seepoll_delay(). - on_poll: Called after every poll with the pod as last listed (or
None), its status ("missing"when not listed) and the seconds elapsed, so a caller can show progress while waiting. - ready_port: When given, also wait until this TCP port answers inside
the pod (a template that serves a model on start is not usable
when RUNNING, only when its port accepts). The same
timeoutbounds both phases together; withtimeout=Nonethe port wait useswait_for_port()'s own default.
Returns:
PodInfo when the pod is ready, otherwise None if the timeout
expires while the pod is still starting (or, with ready_port,
while the port has not answered yet).
Raises:
- PodStartError: The pod reached a terminal status (
FAILED,CREATION_FAILED,STOPPED, …), vanished from the pod list after being seen, or was still not listedMISSING_GRACE_SECONDSseconds after the first poll. The error carries the last[PodInfo](/developers/sdk/reference/models/pod-info), its status, the status history and the cause the backend recorded (cause), so a caller can tell a dead pod from a slow one and clean up instead of retrying.
scp
def scp(pod: lium.sdk.models.PodInfo, *, local: str, remote: str) -> None:
Upload a local file to a pod via SFTP.
remote is a file path, or a directory: an existing remote directory, or a
path ending in / (created if missing), receives the file under its own name.
download
def download(pod: lium.sdk.models.PodInfo, *, remote: str, local: str) -> None:
Download a file from a pod via SFTP.
Arguments:
- pod: The pod to download from.
- remote: Remote file path on the pod.
- local: Local destination path.
Raises:
- ValueError: If SSH is not configured for the pod.
upload
def upload(pod: lium.sdk.models.PodInfo, *, local: str, remote: str) -> None:
Upload a file to a pod via SFTP.
This is an alias for scp() for parity with the CLI.
Arguments:
- pod: The pod to upload to.
- local: Local file path to upload.
- remote: Remote destination path on the pod.
Raises:
- ValueError: If SSH is not configured for the pod.
ssh_argv
def ssh_argv(pod: lium.sdk.models.PodInfo) -> List[str]:
The OpenSSH argument list that opens a shell on a pod.
Built from the user, host and port the API's ssh_connect_cmd names
(ssh_target(); any other shape is refused), with -i <configured key> when one is configured and the host-key options of
openssh_host_key_options(). Run it with subprocess.run(argv) —
no shell is involved, so nothing in the API's value is ever interpreted.
Raises:
- ValueError: The pod has no SSH command, or it is not
ssh <user>@<host> [-p <port>].
refresh_pod
def refresh_pod(pod: Union[str, lium.sdk.models.PodInfo]) -> lium.sdk.models.PodInfo:
Re-read one pod from the API.
A [PodInfo](/developers/sdk/reference/models/pod-info) is a snapshot: after a restart the pod's host, port and
ssh_cmd can all change, and the copy a caller holds says nothing
about it. Call this before reconnecting to a pod held for a while.
Arguments:
- pod: A
[PodInfo](/developers/sdk/reference/models/pod-info), a pod id or a huid.
Returns:
The current [PodInfo](/developers/sdk/reference/models/pod-info) for that pod (one /pods call).
Raises:
- LiumNotFoundError: The pod is no longer in the account's pod list.
ssh
def ssh(pod: lium.sdk.models.PodInfo, *, refresh: bool = False) -> str:
Get SSH command string for connecting to a pod.
The shell-quoted form of ssh_argv(): the configured key, the pinned
host-key options and the pod's user@host, ready to paste into a POSIX shell.
Building it creates the pod's ~/.lium/known_hosts/<pod id> file when it does
not exist yet (empty until the first connection pins the key).
Arguments:
- pod: The pod to generate SSH command for.
- refresh: Re-read the pod first (see
refresh_pod()), so the command reflects the host and port the pod has now, not the ones it had whenpodwas fetched.
Returns: SSH command string with the configured SSH key path.
Raises:
- ValueError: If SSH is not configured for the pod or no SSH key path is set.
- LiumNotFoundError:
refresh=Trueand the pod is gone.
rsync_options
def rsync_options(
*,
bwlimit: Optional[int] = None,
exclude: Optional[Sequence[str]] = None,
delete: bool = False,
partial: bool = True,
progress: bool = False
) -> List[str]:
The rsync flags shared by rsync() and cp().
partial keeps half-copied files so an interrupted transfer resumes
instead of starting over — multi-GB pulls over a flaky link are the
normal case, not the exception. It stays --partial only: --inplace
would drop rsync's write-then-rename, and a job on the pod could read a
half-written checkpoint. progress is plain --progress, which both
GNU rsync and the openrsync stock macOS ships accept (--info=progress2
needs GNU rsync >= 3.1 and dies before any byte moves on a Mac).
rsync
def rsync(
pod: lium.sdk.models.PodInfo,
*,
local: str,
remote: str,
bwlimit: Optional[int] = None,
exclude: Optional[Sequence[str]] = None,
delete: bool = False,
partial: bool = True,
progress: bool = False,
download: bool = False
) -> None:
Sync files between the local machine and a pod with rsync.
Arguments:
- pod: Pod to sync.
- local: Local path (source, or destination when
download). - remote: Path on the pod (destination, or source when
download). - bwlimit: Cap the transfer at this many KiB/s (rsync
--bwlimit). - exclude: Patterns to skip (rsync
--exclude), e.g.[".git", "*.pt"]. - delete: Remove files at the destination that are not in the source.
- partial: Keep partially transferred files so a retry resumes (default on).
- progress: Show rsync's overall progress on the terminal instead of capturing its output.
- download: Copy from the pod to the local path instead of to it.
Raises:
- RuntimeError: If the rsync command fails.
cp
def cp(
src_pod: lium.sdk.models.PodInfo,
src_path: str,
dst_pod: lium.sdk.models.PodInfo,
dst_path: str,
*,
bwlimit: Optional[int] = None,
exclude: Optional[Sequence[str]] = None,
delete: bool = False
) -> Dict[str, Any]:
Copy files from one pod to another over SSH, without passing through this machine.
Pod-to-pod links are far faster than relaying through the caller. This
client makes a one-off ed25519 key, places the private half on the
source pod (sent over the exec session's stdin, never on a command line)
and adds the public half to the destination pod's authorized_keys
for the duration of the copy; the source runs rsync straight to the
destination, and both halves are removed again whatever happened. The
source pod's output never decides what the destination trusts: the key
the destination authorizes is the one this client generated, and the
revoke removes exactly that key. The source verifies the destination with
the host key this client pinned for it (~/.lium/known_hosts/<pod id>,
written by the grant connection), copied next to the transfer key; no pin
means no copy (LIUM_SSH_INSECURE=1 accepts any key, as everywhere).
Arguments:
- src_pod, src_path: Where to copy from. A trailing
/on a directory copies its contents, as in rsync. - dst_pod, dst_path: Where to copy to.
- bwlimit, exclude, delete: As in
rsync().
Returns:
The exec() result of the rsync on the source pod.
Raises:
- LiumHostKeyError: When nothing is pinned for the destination pod.
- LiumError: When the copy fails; the message carries rsync's stderr
(
rsync: command not foundmeansapt-get install -y rsyncon the pod named).
transfer_keypair
def transfer_keypair() -> Tuple[str, str]:
A fresh ed25519 key pair for one copy, as OpenSSH text.
Returns:
(private_key, public_key): the private key in OpenSSH PEM form
(what ssh -i reads) and the public key as one ssh-ed25519 <base64> line with no comment.
grant_transfer_key_command
def grant_transfer_key_command(cls, authorized_line: str) -> str:
The remote line that appends authorized_line to the pod's authorized_keys.
revoke_transfer_key_command
def revoke_transfer_key_command(cls, public_key: str, marker: str) -> str:
The remote line that drops every authorized_keys entry carrying public_key.
The match is the key itself (ssh-ed25519 <base64>, unique to one
cp run), not the comment: a line that lost its marker still goes.
The scratch file carries the marker, so two cp runs into the same
pod never share one, and the result is written back with cat >
(the way lium-io removes keys), so the file keeps its
mode and a second run's half-written scratch file can never replace it.
grep exits 1 when nothing is left to keep, which is fine; any other
failure leaves authorized_keys untouched. The whole line runs under the
transfer-key lock, so a concurrent grant or revoke waits for it.
switch_template
def switch_template(
pod: lium.sdk.models.PodInfo,
*,
template_id: str
) -> lium.sdk.models.PodInfo:
Switch the template of a running pod.
Arguments:
- pod: Pod to update.
- template_id: ID of the template to switch to.
Returns: PodInfo object with updated pod information.
create_template
def create_template(
name: str,
docker_image: str,
docker_image_digest: str = '',
docker_image_tag: str = 'latest',
ports: Optional[List[int]] = None,
start_command: Optional[str] = None,
**kwargs
) -> lium.sdk.models.Template:
Create a new template.
Arguments:
- name: Friendly template name.
- docker_image: Image repository (e.g.,
"daturaai/pytorch"). - docker_image_digest: Digest string for pinning (defaults to empty string).
- docker_image_tag: Image tag (defaults to
"latest"). - ports: Internal ports to expose (defaults to
[22, 8000]). - start_command: Optional command executed on container start.
**kwargs: Additional template fields:- category (str): Template category (defaults to
"UBUNTU"). - is_private (bool): Whether template is private (defaults to
True). - volumes (List[str]): Volume mount paths (defaults to
["/workspace"]). - description (str): Template description.
- environment (Dict[str, str]): Environment variables.
- entrypoint (str): Container entrypoint.
- one_time_template (bool): Whether to delete template after pod removal
(defaults to
False).
- category (str): Template category (defaults to
Returns:
Newly created Template.
wait_template_ready
def wait_template_ready(
template_id: str,
timeout: int = 300
) -> Optional[lium.sdk.models.Template]:
Wait for template verification to complete.
Arguments:
- template_id: Template identifier.
- timeout: Maximum seconds to wait.
Returns:
Template when verification succeeds, otherwise None if the timeout expires.
Raises:
- LiumError: If template verification fails.
me
def me() -> Dict[str, Any]:
The account the API key belongs to, as GET /users/me returns it.
Useful keys: id, email (when the server sends it), balance.
get_my_user_id
def get_my_user_id() -> str:
Get the current user's ID.
Returns:
The ID returned by /users/me.
update_template
def update_template(
template_id: str,
name: str,
docker_image: str,
docker_image_digest: str,
docker_image_tag: str = 'latest',
ports: Optional[List[int]] = None,
start_command: Optional[str] = None,
**kwargs
) -> lium.sdk.models.Template:
Update an existing template owned by the caller.
Arguments:
- template_id: Template identifier.
- name: Friendly name.
- docker_image: Image repository.
- docker_image_digest: Optional digest.
- docker_image_tag: Image tag.
- ports: Internal ports to expose.
- start_command: Startup command.
**kwargs: Additional override fields.
Returns:
Updated Template.
Raises:
- ValueError: If the template is missing or not owned by the caller.
wallets
def wallets() -> List[Dict[str, Any]]:
Get the caller's configured funding wallets.
Returns: Raw wallet records returned by the pay API.
add_wallet
def add_wallet(bt_wallet: Any) -> tuple[str, str]:
Link your wallet to the user account.
Arguments:
- bt_wallet: Wallet object exposing
coldkey/coldkeypubfor signing.
Returns:
(app_id, customer_id) parsed from the /tao/create-transfer
redirect. The alpha funding flow reuses them for the company-wallet
lookup, so it makes one POST.
Raises:
- LiumError: If verification or wallet polling fails.
alpha_subnets
def alpha_subnets() -> lium.sdk.client.AlphaSubnets:
The subnets whose alpha Lium accepts as payment (GET /balance/alpha/subnets).
Hard-fails (no fallback): the pay API answers 503 while it has no accepted set,
which _request raises as [LiumServerError](/developers/sdk/reference/exceptions/lium-server-error).
convert_alpha
def convert_alpha(usd: Any, netuid: Optional[int] = None) -> lium.sdk.client.AlphaQuote:
Quote usd (USD) -> alpha via GET /balance/convert/alpha.
The response carries the alpha amount to transfer (converted) and the
subnet netuid the transfer goes to. netuid picks one of the accepted
subnets (alpha_subnets()); without it the pay API quotes on its primary
subnet. A pay-API error raises: _request maps 503 -> [LiumServerError](/developers/sdk/reference/exceptions/lium-server-error)
and a subnet that is not accepted (400) to a [LiumError](/developers/sdk/reference/exceptions/lium-error), so the funding
stops before any transfer is sent.
company_wallet
def company_wallet(app_id: str) -> str:
Resolve the Lium deposit address via GET /wallet/company/?app_id=.
Returns the company wallet_hash, the SS58 address Lium credits deposits
to. A 404 (app has no wallet) raises [LiumNotFoundError](/developers/sdk/reference/exceptions/lium-not-found-error) (a [LiumError](/developers/sdk/reference/exceptions/lium-error))
before any transfer is sent.
backup_create
def backup_create(
pod: lium.sdk.models.PodInfo,
*,
path: str,
frequency_hours: int = 6,
retention_days: int = 7
) -> lium.sdk.models.BackupConfig:
Create or replace a backup configuration for a pod.
Arguments:
- pod: Pod to configure.
- path: Explicit filesystem path inside the pod volume to back up.
- frequency_hours: Backup interval in hours.
- retention_days: Retention period in days.
Returns:
Created BackupConfig.
backup_update
def backup_update(
config_id: str,
*,
path: str,
frequency_hours: int,
retention_days: int
) -> lium.sdk.models.BackupConfig:
Replace the schedule of an existing backup configuration in place.
A refused update leaves the old configuration as it was.
Arguments:
- config_id: Backup configuration identifier.
- path: Explicit filesystem path inside the pod volume to back up.
- frequency_hours: Backup interval in hours.
- retention_days: Retention period in days.
Returns:
Updated BackupConfig.
backup_now
def backup_now(
pod: lium.sdk.models.PodInfo,
*,
name: str,
description: str = ''
) -> Dict[str, Any]:
Trigger an immediate backup for a pod.
Arguments:
- pod: Pod to back up.
- name: Backup name.
- description: Optional description.
Returns: API response payload from the run-now endpoint.
backup_config
def backup_config(pod: lium.sdk.models.PodInfo) -> Optional[lium.sdk.models.BackupConfig]:
Return the backup configuration for a pod if one exists.
Arguments:
- pod: Pod to inspect.
Returns:
BackupConfig if present, otherwise None.
backup_list
def backup_list() -> List[lium.sdk.models.BackupConfig]:
List all backup configurations across all pods.
Returns:
List of BackupConfig.
backup_logs
def backup_logs(pod: lium.sdk.models.PodInfo) -> List[lium.sdk.models.BackupLog]:
Get recent backup logs for a pod.
Arguments:
- pod: Pod to inspect.
Returns:
List of BackupLog entries (possibly empty).
backup_logs_all
def backup_logs_all() -> List[lium.sdk.models.BackupLog]:
Get all backup logs available to the current user.
backup_log
def backup_log(backup_id: str) -> lium.sdk.models.BackupLog:
Get one backup log owned by the authenticated user.
resolve_backup_id
def resolve_backup_id(backup_id: str) -> str:
Resolve an eight-character backup ID shown by the CLI.
backup_delete
def backup_delete(config_id: str) -> Dict[str, Any]:
Delete a backup configuration by ID.
Arguments:
- config_id: Backup configuration identifier.
Returns: API response payload.
backup_cancel
def backup_cancel(backup_id: str) -> Dict[str, Any]:
Request cancellation of an active backup while retaining its history.
backup_log_delete
def backup_log_delete(backup_id: str) -> Dict[str, Any]:
Delete the stored data for a completed backup and retain its audit row.
restore
def restore(
pod: lium.sdk.models.PodInfo,
*,
backup_id: str,
restore_path: Optional[str] = None
) -> Dict[str, Any]:
Restore a backup to a pod.
Arguments:
- pod: Pod to restore to.
- backup_id: ID of the backup to restore.
- restore_path: New or empty subdirectory where the backup is restored.
Defaults to
<pod volume>/restored.
Returns: Response from the restore API.
restore_logs
def restore_logs(pod: lium.sdk.models.PodInfo) -> List[lium.sdk.models.RestoreLog]:
Get recent restore logs for a pod.
Arguments:
- pod: Pod to inspect.
Returns:
List of RestoreLog entries (possibly empty).
resolve_restore_id
def resolve_restore_id(restore_id: str) -> str:
Resolve an eight-character restore ID shown by the CLI.
restore_cancel
def restore_cancel(restore_id: str) -> Dict[str, Any]:
Request cancellation of an active restore.
get_deployment_estimate
def get_deployment_estimate(executor_id: str, template_id: str) -> dict:
Estimate deployment time for a template on a node.
Arguments:
- executor_id: Node UUID.
- template_id: Template UUID.
Returns:
Dict with estimated_seconds, is_slow_machine, warning_message, is_cached_template,
and docker_image_size (image size in bytes, or None if unknown).
balance
def balance() -> float:
Get current account balance.
Pods are billed per second at their hourly price; the balance is debited every 5 minutes.
Returns:
Floating-point balance value reported by /users/me.
balance_or_none
def balance_or_none() -> Optional[float]:
The balance from /users/me, or None when the answer has no balance field — where
balance() would say 0, which a caller comparing balances would misread.
events
def events(
*,
since: Optional[datetime.datetime] = None,
pod_id: Optional[str] = None,
api_key_id: Optional[str] = None,
limit: int = 200
) -> List[Dict[str, Any]]:
The account's event log, newest first (GET /users/me/events).
Each entry is one recorded action — a rent request, creation, reboot, deletion with its
reason, an API/SSH key or template change — with actor naming the session or API key
(api_key_id, api_key_name) that made the request; null when the platform acted
by itself. pod_id also answers for a pod that has since been deleted.
audit_log
def audit_log(
*,
since: Optional[datetime.datetime] = None,
until: Optional[datetime.datetime] = None,
action: Optional[str] = None,
source: Optional[str] = None,
api_key_id: Optional[str] = None,
resource_id: Optional[str] = None,
cursor: Optional[str] = None,
limit: int = 100
) -> Dict[str, Any]:
One page of the account audit log, newest first (GET /account/audit).
One entry per request that changed something on the account — a pod created, restarted or
deleted, a key created or revoked, a login, a top-up requested, a setting or a workspace member
changed — with action (pod.delete, key.create, login, …), actor (auth
session or api_key, user_id, api_key_id, api_key_name), source (portal,
cli, sdk, mcp, admin, api), ip (filled on the caller's own entries only),
user_agent, request_id, method, route, status_code, resource_type,
resource_id, summary. action filters by prefix (pod. is every pod action).
Returns \{"items": [...], "next_cursor": ...\}; pass next_cursor back as cursor for the
next (older) page, None when this page was the last. limit is 1–500. A key needs the
read scope; a server without the route answers 404 ([LiumNotFoundError](/developers/sdk/reference/exceptions/lium-not-found-error)).
topup_currencies
def topup_currencies(refresh: bool = False) -> List[Dict[str, Any]]:
List stablecoin currencies/networks supported for self-serve top-ups.
Arguments:
- refresh: Bypass the server-side cache and re-fetch from the provider.
Returns:
List of \{"code", "network", "decimals", "display_decimals"\} dicts.
topup_create_invoice
def topup_create_invoice(
amount: float,
crypto_currency: str,
crypto_network: str
) -> Dict[str, Any]:
Create a stablecoin top-up invoice for the current account.
The returned deposit_address is where the exact crypto_amount of
crypto_currency (on crypto_network) must be sent. Once the provider
confirms the transfer, the account balance is credited automatically.
Arguments:
- amount: Top-up amount in USD.
- crypto_currency: Stablecoin code (e.g.
"USDT"), seetopup_currencies(). - crypto_network: Network the stablecoin is sent on (e.g.
"tron").
Returns:
Invoice dict including invoice_id, deposit_address, crypto_amount,
crypto_currency, crypto_network, exchange_rate and expires_at.
topup_checkout_link
def topup_checkout_link(
amount_usd: float,
success_url: Optional[str] = None,
cancel_url: Optional[str] = None
) -> Dict[str, Any]:
Open a Stripe Checkout page that tops up this account by card (POST /stripe/create-checkout-session).
Nothing is charged by this call: whoever opens url enters a card (and passes the bank's
3-D Secure check, if it asks) and the balance is credited by Stripe's webhook once the payment
succeeds. This is how an agent with no card of its own asks a person to pay. The key
must hold the billing scope (or be a browser session); a read / rent / manage key
is refused with LiumPermissionError.
Arguments:
- amount_usd: At least $10, the platform's top-up minimum.
- success_url: Where Checkout sends the payer after paying; default the Billing page of the site the client talks to.
- cancel_url: Where Checkout sends the payer on cancel; same default.
Returns:
\{"url", "session_id", "amount_usd", "expires_at"\}; expires_at is a Unix time, or
None when the server does not send it.
wait_for_credit
def wait_for_credit(
baseline: float,
timeout: float = 600,
interval: float = 2.0,
*,
_clock: Callable[[], float] = <built-in function monotonic>,
_sleep: Callable[[float], NoneType] = <built-in function sleep>
) -> Dict[str, Any]:
Poll the balance until it rises above baseline (a top-up landed) or timeout seconds pass.
Card and crypto top-ups are credited by the payment provider's webhook, so the call that starts
a payment returns before the money is on the balance. Read balance() before starting the
payment and pass it as baseline. A balance read that fails is retried on the next tick; the
pods' own billing can lower the balance meanwhile, so a top-up smaller than what the account's
running pods burn in interval seconds may be missed, and any other credit landing meanwhile
(a second invoice, a transfer) also ends the wait: compare balance with what you paid.
Returns:
\{"credited": bool, "balance": float | None, "seconds": float\}: credited is False
when the time ran out; balance is the last one read (None if none could be read).
volumes
def volumes() -> List[lium.sdk.models.VolumeInfo]:
List all volumes for the current user.
Returns:
List of VolumeInfo.
volume
def volume(volume_id: str) -> lium.sdk.models.VolumeInfo:
Get a specific volume by ID.
Arguments:
- volume_id: Volume identifier.
Returns:
VolumeInfo for the requested volume.
volume_create
def volume_create(name: str, *, description: str = '') -> lium.sdk.models.VolumeInfo:
Create a new volume.
Arguments:
- name: Volume name.
- description: Optional description.
Returns:
Created VolumeInfo.
volume_update
def volume_update(
volume_id: str,
*,
name: Optional[str] = None,
description: Optional[str] = None
) -> lium.sdk.models.VolumeInfo:
Update a volume's metadata.
Arguments:
- volume_id: Volume identifier.
- name: Optional new name.
- description: Optional description.
Returns:
Updated VolumeInfo.
Raises:
- ValueError: If neither
namenordescriptionis provided.
volume_delete
def volume_delete(volume_id: str) -> Dict[str, Any]:
Delete a volume.
Arguments:
- volume_id: Volume identifier.
Returns: API response payload from the delete request.
schedule_termination
def schedule_termination(
pod: Union[str, lium.sdk.models.PodInfo, Dict],
*,
termination_time: str
) -> Dict[str, Any]:
Schedule a pod for automatic termination at a future date and time.
The pod does not have to be running: the dict up() returns, or its id,
is enough, so the schedule can be set before wait_ready() — a pod that never
becomes ready is billed all the same and is removed at termination_time.
Arguments:
- pod: Pod identifier, PodInfo (or any object with an
idattribute), or dict with anidfield. - termination_time: ISO 8601 formatted datetime string (e.g., "2025-10-17T15:30:00Z")
Returns: Response from the schedule termination API
cap_spend
def cap_spend(pod: lium.sdk.models.PodInfo, *, budget_usd: float) -> datetime.datetime:
Schedule the pod's removal for the moment it will have spent budget_usd.
The API caps a rental by time only. This computes the time at which the
pod, billed at its hourly price since created_at, reaches the budget
(lium.sdk.utils.spend_cap_deadline()) and schedules removal then —
unless a removal is already scheduled earlier (a --ttl), which stays,
as lium up --budget --ttl keeps the earlier of the two. Client-side:
the pod keeps running if the schedule is canceled or the price changes.
Arguments:
- pod: A running pod with
created_atand an executor price. - budget_usd: Total spend allowed for the pod's lifetime.
Returns: The scheduled removal time (UTC): the budget deadline, or the earlier removal that was already scheduled.
Raises:
- ValueError: Budget not positive, price or creation time unknown, the budget is already spent (the deadline is in the past), or the pod's scheduled removal has already passed.
cancel_scheduled_termination
def cancel_scheduled_termination(pod: lium.sdk.models.PodInfo) -> Dict[str, Any]:
Cancel a scheduled termination for a pod.
Arguments:
- pod: Pod to cancel the schedule for
Returns: Response from the cancel scheduled termination API
install_jupyter
def install_jupyter(
pod: lium.sdk.models.PodInfo,
*,
jupyter_internal_port: int
) -> Dict[str, Any]:
Install Jupyter Notebook on a pod.
Arguments:
- pod: Pod to install Jupyter on
- jupyter_internal_port: Internal port for Jupyter Notebook
Returns: Response from the install Jupyter API