Python SDK
The official Python SDK for the WAYSCloud API. One client for VPS, Kubernetes, DNS, databases, storage, apps, IoT and more — with typed exceptions, automatic retries and connection pooling.
bash
pip install wayscloudRequires Python 3.10+. PyPI page.
Authentication
python
from wayscloud import WaysCloudClient
# Personal Access Token
client = WaysCloudClient(token="wayscloud_pat_xxx...")
# API key
client = WaysCloudClient(api_key="wayscloud_api_xxx...")
# Environment variables (no args needed)
# Set WAYSCLOUD_TOKEN or WAYSCLOUD_API_KEY
client = WaysCloudClient()Priority: explicit arguments > environment variables.
| Parameter | Environment variable | Default |
|---|---|---|
token | WAYSCLOUD_TOKEN | — |
api_key | WAYSCLOUD_API_KEY | — |
base_url | WAYSCLOUD_API_URL | https://api.wayscloud.services |
timeout | — | 30.0 |
Services
| Service | Property | Auth | Description |
|---|---|---|---|
| VPS | client.vps | API key | Virtual private servers |
| DNS | client.dns | API key | DNS zones and records |
| Storage | client.storage | API key | S3-compatible object storage |
| Database | client.database | PAT | Managed PostgreSQL & MariaDB |
| Redis | client.redis | API key | Managed Redis |
| Kubernetes | client.kubernetes | PAT | Managed Kubernetes clusters |
| Apps | client.apps | API key | App Platform (containers) |
| IoT | client.iot | API key | IoT device management |
| SMS | client.sms | API key | SMS messaging |
| Account | client.account | PAT | Profile and SSH keys |
Method reference
Kubernetes
python
# Catalogue
client.kubernetes.plans(region="no", kind=None)
client.kubernetes.regions()
client.kubernetes.versions()
client.kubernetes.estimate(plan_code, node_pools, currency="NOK", region="no")
# Clusters (create/delete are async — use wait())
client.kubernetes.list()
client.kubernetes.get(cluster_id)
client.kubernetes.create(name, node_pools, plan_code="k8s-cluster-dev", region="no",
version="1.35", api_ip_filter=None, ssh_key_ids=None)
client.kubernetes.wait(cluster_id, timeout=1800, interval=15)
client.kubernetes.delete(cluster_id, confirm_name)
client.kubernetes.history(cluster_id)
# Node pools
client.kubernetes.add_node_pool(cluster_id, name, plan_code, count, labels=None, taints=None,
ssh_key_ids=None)
client.kubernetes.scale_node_pool(cluster_id, pool_name, count)
client.kubernetes.delete_node_pool(cluster_id, pool_name)
# Access
client.kubernetes.kubeconfig(cluster_id) # YAML text — a secret
client.kubernetes.set_api_access(cluster_id, cidrs)
# Public IPs and reverse DNS
client.kubernetes.allocate_ip(cluster_id)
client.kubernetes.release_ip(cluster_id, address)
client.kubernetes.set_ptr(cluster_id, address, ptr_record)
# Backups
client.kubernetes.backup_policy(cluster_id)
client.kubernetes.set_backup_policy(cluster_id, enabled=True, schedule_cron="0 3 * * *",
retention_days=14, include_volumes=True, namespaces=None)
client.kubernetes.backups(cluster_id)
client.kubernetes.backup_now(cluster_id, namespaces=None)
client.kubernetes.restore(cluster_id, backup_id, namespaces=None)
# Upgrades
client.kubernetes.available_upgrades(cluster_id)
# Pre-upgrade check: errors block the upgrade, warnings are advisory
check = client.kubernetes.upgrade_preflight(
cluster_id, "1.35",
mode="next_maintenance_window", # "immediate" | "next_maintenance_window" | "scheduled"
scheduled_at=None, # required with mode="scheduled" (ISO-8601, inside the window)
strategy="rolling-update", # or "manual"
backup_before_upgrade=True, # check as if a fresh backup is taken first
)
check["errors"], check["warnings"], check["backup"]["age_hours"]
client.kubernetes.upgrade(cluster_id, "1.35", mode="next_maintenance_window",
strategy="rolling-update", backup_before_upgrade=True)
# Maintenance window (DST-safe occurrence math on the server)
client.kubernetes.get_maintenance_window(cluster_id)
client.kubernetes.set_maintenance_window(cluster_id, timezone="Europe/Oslo",
days=["SUN"], start="02:00", duration_minutes=120)
client.kubernetes.delete_maintenance_window(cluster_id)
# Upgrade jobs (queued/scheduled: list, inspect, cancel before start)
client.kubernetes.upgrade_jobs(cluster_id)
client.kubernetes.upgrade_job(cluster_id, job_id)
client.kubernetes.cancel_upgrade_job(cluster_id, job_id)
# Worker convergence (one pool at a time; rolling upgrades do this automatically)
client.kubernetes.upgrade_node_pool(cluster_id, pool_name)VPS
python
# Instances
client.vps.list(status=None, region=None)
client.vps.get(vps_id)
client.vps.create(hostname, plan, region, os_template, ssh_keys=None)
client.vps.delete(vps_id)
client.vps.start(vps_id)
client.vps.stop(vps_id)
client.vps.reboot(vps_id)
client.vps.status(vps_id)
# Plans & discovery
client.vps.plans(region=None)
client.vps.os_templates()
client.vps.regions()
# Snapshots (requires qcow2 disk format)
client.vps.snapshots(vps_id)
client.vps.create_snapshot(vps_id, name, description=None)
client.vps.delete_snapshot(vps_id, snapshot_name)
client.vps.rollback_snapshot(vps_id, snapshot_name) # VPS must be stopped
# Backups (off-site)
client.vps.backups(vps_id)
client.vps.create_backup(vps_id)
client.vps.delete_backup(vps_id, backup_id)
client.vps.backup_usage(vps_id)
client.vps.backup_policy(vps_id)
client.vps.set_backup_policy(vps_id, enabled=True, frequency="daily",
time_of_day="03:00", retention_days=7)
client.vps.delete_backup_policy(vps_id)
# Firewall
client.vps.firewall_rules(vps_id)
client.vps.add_firewall_rule(vps_id, port, protocol="tcp", source="0.0.0.0/0")
client.vps.remove_firewall_rule(vps_id, rule_id)DNS
python
# Zones
client.dns.zones()
client.dns.get(zone_name)
client.dns.create_zone(name)
client.dns.delete_zone(zone_name)
# Records
client.dns.records(zone_name)
client.dns.create_record(zone_name, record_type, value, name="", ttl=3600)
client.dns.update_record(zone_name, record_id, value=None, ttl=None)
client.dns.delete_record(zone_name, record_id)
# DNSSEC
client.dns.dnssec_status(zone_name)
client.dns.dnssec_activate(zone_name)
client.dns.dnssec_deactivate(zone_name)Database
Requires PAT authentication with database:read and database:write scopes.
python
client.database.list()
client.database.get(db_type, name)
client.database.create(name, db_type="postgresql", tier="standard")
client.database.delete(db_type, name)
# Firewall
client.database.firewall_rules(db_type, name)
client.database.add_firewall_rule(db_type, name, source)
client.database.remove_firewall_rule(db_type, name, rule_id)Connection credentials (host, port, username, password) are returned in the create() response.
Storage
python
# Buckets
client.storage.list()
client.storage.get(bucket_name)
client.storage.create_bucket(name, tier="standard")
client.storage.delete_bucket(bucket_name)
# Bucket access keys
client.storage.bucket_keys(bucket_name)
client.storage.create_bucket_key(bucket_name, name) # returns access_key + secret_key
client.storage.delete_bucket_key(bucket_name, key_id)Redis
python
client.redis.list()
client.redis.get(instance_id) # includes connection info (host, password)
client.redis.create(name, plan="redis-starter", region="no")
client.redis.delete(instance_id)
client.redis.restart(instance_id)
client.redis.rotate_credentials(instance_id)
# Discovery
client.redis.plans()
client.redis.regions()
# Firewall
client.redis.firewall_rules(instance_id)
client.redis.add_firewall_rule(instance_id, source, description="")
client.redis.remove_firewall_rule(instance_id, rule_id)Apps
python
client.apps.list()
client.apps.get(app_id)
client.apps.create(name, plan="app-basic", region="no", port=8080)
client.apps.update(app_id, **kwargs)
client.apps.delete(app_id)
client.apps.deploy(app_id, image)
client.apps.start(app_id)
client.apps.stop(app_id)
client.apps.restart(app_id)
client.apps.logs(app_id, lines=100)
# Discovery
client.apps.plans()
client.apps.regions()
# Environment variables
client.apps.env_vars(app_id)
client.apps.set_env(app_id, key, value)
client.apps.unset_env(app_id, key)IoT
python
# Devices
client.iot.devices()
client.iot.get_device(device_id)
client.iot.create_device(device_id, name, device_type=None) # returns MQTT credentials
client.iot.update_device(device_id, name=None, is_active=None)
client.iot.delete_device(device_id)
client.iot.device_telemetry(device_id)
# Groups
client.iot.groups()
client.iot.create_group(name, description=None)
client.iot.delete_group(group_id)
# Rules
client.iot.rules()
client.iot.create_rule(name, rule_type, severity="warning")
client.iot.delete_rule(rule_id)SMS
python
client.sms.send(to, message, sender_id=None)
client.sms.list()
client.sms.get(message_id)Account
python
client.account.profile()
client.account.ssh_keys()
client.account.add_ssh_key(name, public_key)
client.account.delete_ssh_key(key_id)Examples
Create a VPS with snapshot
python
vps = client.vps.create(
hostname="web01.example.com",
plan="vps-medium",
region="no",
os_template="ubuntu-24.04",
)
# After setup, create a snapshot
client.vps.create_snapshot(vps["id"], "initial-setup")Database with firewall
python
db = client.database.create(name="prod", db_type="postgresql")
print(db["host"], db["port"], db["password"])
# Whitelist your IP
client.database.add_firewall_rule("postgresql", "prod", "203.0.113.0/32")App deployment with environment variables
python
app = client.apps.create(name="api", region="eu", port=8080)
client.apps.set_env(app["id"], "DATABASE_URL", "postgres://...")
client.apps.deploy(app["id"], image="ghcr.io/org/api:latest")Error handling
python
from wayscloud import NotFoundError, AuthenticationError, ValidationError, ServerError
try:
client.vps.get("nonexistent")
except NotFoundError:
print("Resource not found")
except AuthenticationError:
print("Invalid or expired credentials")
except ValidationError:
print("Invalid request parameters")
except ServerError:
print("Server error — retry later")All exceptions inherit from WaysCloudError with status_code, message, and detail.
| Exception | HTTP Status |
|---|---|
AuthenticationError | 401, 403 |
NotFoundError | 404 |
ValidationError | 422 |
ServerError | 5xx |
Connection management
The SDK uses a persistent HTTP client for connection pooling:
python
with WaysCloudClient(token="wayscloud_pat_xxx...") as client:
zones = client.dns.zones()
vps = client.vps.list()
# Connections released hereOr close manually:
python
client = WaysCloudClient()
try:
data = client.vps.list()
finally:
client.close()Retries
Automatic retries on 429, 502, 503, 504 — up to 3 attempts with exponential backoff. Respects Retry-After headers.
Next steps
- CLI — command-line interface built on this SDK
- Terraform Provider — infrastructure as code
- Python examples — using the REST API directly