Skip to content

SDKs and CLI

Python SDK

Reference for vmyard-sdk, the Python 3.9+ client built on the standard library alone.

On this page

vmyard-sdk is the Python client for the VMYard API. It needs Python 3.9 or newer and uses only the standard library (urllib), so it adds no dependencies to your project.

sh
git clone https://github.com/erphq/vmyard-sdk.git
pip install ./vmyard-sdk/python

Create a client

python
from vmyard import VMYard

vy = VMYard(base_url="https://api.example.com")  # key from VMYARD_API_KEY
ArgumentDefaultMeaning
api_keyVMYARD_API_KEYA vmy_… key. Required.
base_urlVMYARD_API_URLYour API origin. Required: there is no default host.
organization_idVMYARD_ORGANIZATION_IDSent as X-Organization-ID. Optional with an API key; must match the key.
max_retries2Retries for retryable failures.
timeout60.0Seconds per HTTP attempt.
user_agent—Extra User-Agent text.

The client never follows redirects, so the key is never sent to another origin. The client is synchronous.

Machines

python
machine = vy.machines.create(name="dev", plan="small", auto_stop_after_seconds=7200)
same = vy.machines.find("dev")          # id, tenant alias, or unique display name
page = vy.machines.list(limit=20)       # one page
everything = vy.machines.list_all()

machine.update(name="api")
machine.stop()
machine.start()
machine.restart()
machine.refresh()                       # reload machine.data
machine.delete()
CallRouteNotes
vy.machines.create(**params)POST /v1/machinesResolves plan, image, and region names or IDs to exact versions; waits; returns a Machine.
vy.machines.begin_create(**params)POST /v1/machinesSubmits without waiting.
vy.machines.build_create_request(**params)—The exact request create would send.
vy.machines.list(limit=, cursor=)GET /v1/machinesOne page; list_all() and iterate() follow every page.
vy.machines.get(id) / find(ref)GET /v1/machines/{id}find accepts an ID, tenant alias, or unique display name.
machine.update(name=, auto_stop_after_seconds=, never_auto_stop=)PATCH /v1/machines/{id}auto_stop_after_seconds=None means never.
machine.start() / stop() / restart()POST …/{action}wait=False returns the accepted operation instead.
machine.delete()POST …/delete
machine.usage(since=, until=)GET …/usageSeconds and micros; defaults to the machine's lifetime.

create takes name, alias, plan, image, region, resources, backend, auto_stop_after_seconds, never_auto_stop, and setup_script. machine.id, machine.name, machine.alias, machine.state (observed), and machine.desired_state are shortcuts into machine.data, the wire MachineDto.

Commands

python
result = machine.exec("uname -a", timeout_ms=30_000)
print(result.exit_code, result.stdout, result.stderr, result.timed_out, result.truncated)

with machine.exec(["npm", "test"], stream=True) as stream:
    for frame in stream:
        ...  # started, stdout, stderr (frame.data is bytes), exit (frame.exit_code), error

A string runs under /bin/sh -c; a list runs as argv. A non-zero exit, timeout, or cancellation raises OperationError (see Running commands).

Background commands

python
command = machine.commands.start("npm run dev")
status = machine.commands.status(command["command_id"], log_offset=0, log_limit=65536)
finished = machine.commands.wait(command["command_id"])
machine.commands.kill(command["command_id"])

Files

python
machine.files.write("app/config.json", '{"debug": true}')   # str or bytes
text = machine.files.read("app/config.json")
data = machine.files.read_bytes("out/image.png")

Operations

python
operation = vy.operations.get(operation_id)
done = vy.operations.wait(operation_id)        # raises OperationError unless it succeeds
for event in vy.operations.events(operation_id):
    print(event)

Catalog, identity, billing, webhooks

CallRoute
vy.me()GET /v1/me
vy.catalog.plans() / images() / regions()GET /v1/plans, /v1/images, /v1/regions
vy.billing.get() / limits() / invoice_preview()GET /v1/billing, /v1/limits, /v1/billing/invoice-preview
vy.billing.usage(since=, until=)GET /v1/usage
vy.webhooks.list() / get(id)GET /v1/webhooks[/{id}]
vy.webhooks.create(name=, url=, events=, enabled=)POST /v1/webhooks; returns {webhook, secret}
vy.webhooks.update(id, …) / delete(id) / rotate_secret(id)PATCH/DELETE /v1/webhooks/{id}, POST …/rotate-secret
vy.webhooks.deliveries(id, limit=)GET /v1/webhooks/{id}/deliveries

Responses are plain dictionaries with the API's JSON field names.

Device sign-in

python
from vmyard import DeviceAuthorization

device = DeviceAuthorization()  # base URL from VMYARD_API_URL
code = device.request_code(client_name="my-tool on build-host")
print(f"Open {code['verification_uri']} and enter {code['user_code']}")
token = device.wait_for_token(code)  # token["access_token"] is the new API key

Idempotency, retries, and errors

Idempotency, retries, and error classes match the TypeScript SDK, with snake_case names (idempotency_key=, retry_after_seconds, field_errors, correlation_id). The connection error class is APIConnectionError.

More of the SDK

Like the TypeScript SDK, it also covers ports and port tokens, SSH keys (set_ssh_key), internet access (set_network), snapshots, forks and restores, templates, environments, and billing changes; this page shows the common calls. For anything else, call the HTTP API directly.