// PYTHON SDK

Python SDK

import matmul gives you the whole GPU workflow from Python: offers, instances, SSH keys, billing and jobs. It's the same package as the CLI, needs Python 3.9+ and uses only the standard library.

Install

pip install matmul-cloud

A GPU box in ten lines

import matmul

client = matmul.Client()   # MATMUL_API_KEY, or the key saved by `matmul login`

offer = client.offers(gpu="H100", max_price_cents=300)[0]   # cheapest first
key = client.ssh_keys()[0]
inst = client.create_instance(offer.id, "dev", image=None, ssh_key_id=key.id,
                              max_hourly_price_cents=300)
inst = client.wait_instance(inst.id)          # blocks until running (up to 15 min)
print(inst.ssh_command)                       # ssh -p <port> <user>@<ip>

# ... work ...
client.delete_instance(inst.id)               # stops billing

Client

matmul.Client(api_key=None, base_url=None, timeout=30.0)

Each argument falls back to an environment variable, then to ~/.config/matmul/config.json (written by matmul login):

ArgumentFallbacks
api_keyMATMUL_API_KEY, then the config file. With none, the constructor raises MatMulError(401, ...).
base_urlMATMUL_API_URL, the config file, then https://api.matmul.cloud.
timeoutSeconds per HTTP request.

MATMUL_CONFIG points at a different config file. Prices are always integer US cents per hour for the whole machine; Offer.hourly_price gives dollars.

Offers

MethodReturns
offers(gpu=None, count=None, region=None, max_price_cents=None, kind=None)list[Offer], cheapest first. kind="gpu" or "cpu" narrows it to GPU or CPU-only machines.

Offer has id, gpu_type, num_gpus, vram_per_gpu_gb, vcpus, memory_gb, storage_gb, interconnect, region, hourly_price_cents, boot_min_sec and boot_max_sec (the typical boot time range; 0 when unknown), and the properties hourly_price (dollars) and is_cpu.

The kind argument and the storage_gb, interconnect, boot_min_sec, boot_max_sec and is_cpu fields needs a matmul-cloud release after 0.1.0. In 0.1.0, filter on num_gpus instead of kind.

SSH keys

MethodReturns
add_ssh_key(name, public_key)SSHKey (id, name, fingerprint). Pass the public key line, e.g. the contents of ~/.ssh/id_ed25519.pub.
ssh_keys()list[SSHKey]
delete_ssh_key(key_id)None. Like add_ssh_key, not for viewers.

Instances

create_instance(offer_id, name, image, ssh_key_id, *, args=None, envs=None, ports=None,
                shared_memory_gb=None, max_hourly_price_cents=None, keep_alive=False,
                expose=None) -> Instance

image=None (or "") launches the provider's plain VM. With an image it runs as a container on that VM. The keyword arguments match the CLI flags: keep_alive keeps an interactive image running, max_hourly_price_cents refuses the launch if the live price is higher, and expose asks for public app links, which aren't switched on yet.

MethodReturns
instances()list[Instance], including deleted ones.
instance(ref)Instance by id (ins_...) or by name.
wait_instance(ref, timeout=900.0, poll=3.0, on_update=None)The Instance once it's running (with an image, once the container is up). Raises MatMulError if it errors or is deleted (409) or times out (408). on_update(instance, elapsed_seconds) is called on every poll.
delete_instance(ref)The Instance, now deleting. Billing stops and its slot frees up right away.

Instance fields: id, name, status, image, gpu_type, num_gpus, region, hourly_price_cents, ip, ssh_user, ssh_port, ssh_command, container_shell_command (a shell inside the container, for instances with an image), error, running_seconds, accrued_cost_cents, ports and exposable. The running time and cost are estimates for display; the ledger is what you're billed.

Billing

MethodReturns
billing_balance()BillingAccount: balance_cents, frozen, frozen_reason, frozen_at, grace_hours_remaining, verified, limits.
billing_ledger(limit=None, before=None)list[LedgerEntry], newest first: id, kind, amount_cents, created_at, instance_id. Pass the last entry's id as before for the next page.
billing_checkout(amount_cents)A Stripe checkout URL (str) to open and pay.

These need the owner role.

Managed jobs

Not available on MatMul yet; see Managed jobs. capabilities() returns the features the server has switched on, e.g. {"instances": True, "app_links": False, "jobs": False}.

job = client.launch("train", "python train.py", accelerators="H100:1",
                   setup="pip install -r requirements.txt",
                   max_hourly_price_cents=350)      # whole job, all nodes
job = client.wait("train")                          # until succeeded/failed/cancelled/finished
print(client.logs("train"))
print(job.ok, job.billed_cents)
MethodReturns
launch(name, run, *, setup, envs, num_nodes=1, cpus, memory, accelerators, disk_size, max_hourly_price_cents)Job
jobs()list[Job]
job(name)Job by job_... id, or by name (the most recent).
logs(name)The job's logs (str).
down(name)Stops the job; returns the Job.
wait(name, timeout=86400.0, poll=5.0, on_update=None)The finished Job.

Job fields: id, name, state, detail, error, created_at, gpu_type, num_gpus, num_nodes, hourly_price_cents, running_seconds, billed_cents, plus the properties done and ok.

Errors

Every failure raises matmul.MatMulError with .status (the HTTP status, or 0 when the API can't be reached) and .message (the API's error text). The statuses are listed in the REST API reference.

try:
    client.create_instance(offer.id, "dev", None, key.id, max_hourly_price_cents=300)
except matmul.MatMulError as e:
    if e.status == 409:      # offer gone or price rose: search again
        ...
    elif e.status == 429:    # instance limit, frozen account, low balance,
        ...                  # or "GPU capacity is temporarily limited" (retry later)
← All docs