SDKs and CLI
Python SDK
Reference for vmyard-sdk, the Python 3.9+ client built on the standard library alone.
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.
git clone https://github.com/erphq/vmyard-sdk.git
pip install ./vmyard-sdk/pythonCreate a client
from vmyard import VMYard
vy = VMYard(base_url="https://api.example.com") # key from VMYARD_API_KEY| Argument | Default | Meaning |
|---|---|---|
api_key | VMYARD_API_KEY | A vmy_… key. Required. |
base_url | VMYARD_API_URL | Your API origin. Required: there is no default host. |
organization_id | VMYARD_ORGANIZATION_ID | Sent as X-Organization-ID. Optional with an API key; must match the key. |
max_retries | 2 | Retries for retryable failures. |
timeout | 60.0 | Seconds 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
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()| Call | Route | Notes |
|---|---|---|
vy.machines.create(**params) | POST /v1/machines | Resolves plan, image, and region names or IDs to exact versions; waits; returns a Machine. |
vy.machines.begin_create(**params) | POST /v1/machines | Submits without waiting. |
vy.machines.build_create_request(**params) | — | The exact request create would send. |
vy.machines.list(limit=, cursor=) | GET /v1/machines | One 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 …/usage | Seconds 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
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), errorA 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
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
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
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
| Call | Route |
|---|---|
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
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 keyIdempotency, 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.