API Reference
On this page
- Base URL
- Authentication
- Response Format
- Success Response (HTTP 200)
- Error Responses
- API Endpoints
- Health & Status
- Configuration Endpoints
- Proxmox Host Endpoints
- LXC Container Endpoints
- Docker/App Endpoints
- Security Endpoints
- Web UI
- Swagger Documentation
- Error Codes
- Rate Limiting
- CORS Configuration
- Production Deployment
- Example: Complete Workflow
- 1. Check API Health
- 2. List Proxmox Hosts
- 3. Create Container
- 4. Monitor Container
- 5. Execute Command
- Python Client Example
LWS provides a comprehensive REST API for programmatic access to all functionality.
Base URL
http://localhost:8080/api/v1
Authentication
Most endpoints require API key authentication via the X-API-Key header.
curl -H "X-API-Key: your-api-key" \
http://localhost:8080/api/v1/lxc/instances
Generate a key and write it into config.yaml in one step, so no example
value ever ends up in the file:
KEY=$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')
sed -i "s|^api_key:.*|api_key: \"$KEY\"|" config.yaml # on macOS: sed -i ''
echo "$KEY" # give this to API clients
The server refuses to start when api_key is empty or one of the example
values that ship in the repository. Do not use a value copied from any
documentation page: anything published is known to everyone.
Response Format
Success Response (HTTP 200)
{
"output": "command output here"
}
If the underlying command’s stdout starts with { or [ (e.g. commands run with a JSON output option), the API returns that parsed JSON directly instead of wrapping it in "output".
Error Responses
There is no single error shape. It depends on where the request failed:
The underlying lws.py command ran and exited non-zero (HTTP 500):
{
"error": "Command execution failed",
"details": "stderr from the command",
"output": "stdout from the command, if any",
"return_code": 1
}
Request validation failed in the route handler itself - missing/invalid body fields (HTTP 400), most endpoints below:
{
"error": "Missing 'field_name' in request body"
}
No details or return_code - just error.
Missing/invalid API key (HTTP 401), a non-numeric instance_id path segment (HTTP 400, see note below), or any other Flask HTTPException:
{
"code": 401,
"name": "Unauthorized",
"description": "Unauthorized: Invalid or missing API key."
}
A different shape again - code/name/description, not error.
Route not found (HTTP 404) or an unhandled exception (HTTP 500):
{
"error": "Not Found",
"message": "The requested URL was not found on the server."
}
error/message, not error/details.
Note: a global
before_requesthook rejects any request whose<instance_id>URL path segment isn’t purely numeric, with HTTP 400 in thecode/name/descriptionshape above, before the route handler runs at all. This affects every endpoint below with{instance_id}in its path. The same validation (validate_instance_ids_list) also applies to theinstance_idsJSON body field on the bulk endpoints (/lxc/instances/start,/stop,/status,/scale,/terminate,/reboot,/lxc/instances/app/remove): every element must be numeric, or the whole request is rejected with 400 in theerror-only shape above.
Note:
run_lws_commandbuilds the underlying CLI call from two sources: a handler-built positionalcmd_partslist, and whatever is left in the request body/query string after the handler pops out the keys it already placed positionally (viaconsumed_keys). Earlier versions didn’t pop those keys, so several endpoints below sent the same value twice — once positionally, once as a nonexistent--key valueflag — and failed with a Click “no such option” error. That’s fixed; it’s noted on the endpoints below only where it’s useful to know a field is positional rather than an option.
API Endpoints
Health & Status
GET /health
Check API health status (no authentication required).
curl http://localhost:8080/api/v1/health
Response:
{
"status": "ok"
}
Configuration Endpoints
GET /conf
Show current configuration (masked).
curl -H "X-API-Key: your-key" \
http://localhost:8080/api/v1/conf
POST /conf/validate
Validate configuration.
curl -X POST \
-H "X-API-Key: your-key" \
http://localhost:8080/api/v1/conf/validate
POST /conf/backup
Back up the current configuration to a file. Unlike most POST handlers, this one builds --timestamp/--compress flags manually instead of forwarding the whole body.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"destination_path": "/backup/lws-config.yaml",
"timestamp": true,
"compress": true
}' \
http://localhost:8080/api/v1/conf/backup
Proxmox Host Endpoints
GET /px/hosts
List all Proxmox hosts.
curl -H "X-API-Key: your-key" \
http://localhost:8080/api/v1/px/hosts
Query Parameters:
region- Filter by region
GET /px/status
Get Proxmox host resource usage.
curl -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/px/status?region=eu-south-1&az=az1"
POST /px/reboot
Reboot a Proxmox host.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"region": "eu-south-1", "az": "az1", "confirm": true}' \
http://localhost:8080/api/v1/px/reboot
GET /px/templates
List available templates.
curl -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/px/templates?region=eu-south-1&az=az1"
GET /px/clusters
List all clusters in the Proxmox environment.
curl -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/px/clusters?region=eu-south-1&az=az1"
POST /px/update
Run apt-get update and apt-get dist-upgrade on Proxmox hosts. Without a body, every configured host is updated; region, and region with az, limit it. The request is the confirmation: the API always passes --yes.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"region": "eu-south-1", "az": "az1"}' \
http://localhost:8080/api/v1/px/update
Upgrading a host can take many minutes. The request waits for it, up to api.command_timeout seconds.
POST /px/cluster/start / /px/cluster/stop / /px/cluster/restart
Start, stop, or restart cluster services (pve-cluster, corosync) on a Proxmox host.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"region": "eu-south-1", "az": "az1"}' \
http://localhost:8080/api/v1/px/cluster/start
POST /px/backup-lxc
Back up a single LXC container via vzdump on the Proxmox host. vmid and storage are both required (400 if either is missing); vmid is positional in px backup-lxc, storage/mode are forwarded as options.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"vmid": "100", "storage": "local-lvm", "mode": "snapshot"}' \
http://localhost:8080/api/v1/px/backup-lxc
POST /px/backup
Back up Proxmox host configuration (/etc/pve) to a local .tar.gz. backup_dir is positional.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"backup_dir": "/backups/px", "region": "eu-south-1", "az": "az1"}' \
http://localhost:8080/api/v1/px/backup
POST /px/image
Create a template image from an LXC container. instance_id and template_name are both positional.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"instance_id": "100", "template_name": "my-template"}' \
http://localhost:8080/api/v1/px/image
DELETE /px/image/{template_name}
Delete a template image from the Proxmox template cache. template_name comes from the URL.
curl -X DELETE -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/px/image/my-template?region=eu-south-1&az=az1"
GET /px/security-groups
List the security groups of the cluster firewall, with their rules, through the Proxmox API (pvesh).
curl -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/px/security-groups?region=eu-south-1&az=az1"
POST /px/security-groups
Create a security group. group_name is positional.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"group_name": "web", "description": "Web tier"}' \
http://localhost:8080/api/v1/px/security-groups
DELETE /px/security-groups/{group_name}
Delete a security group. group_name comes from the URL. A group that still has rules is not deleted unless the query string has force=true, which deletes its rules first.
curl -X DELETE -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/px/security-groups/web?region=eu-south-1&az=az1&force=true"
POST /px/security-groups/{group_name}/rules / DELETE .../rules
Add or remove a firewall rule within an existing security group. group_name comes from the URL; the rule fields (direction, action, protocol, source_ip, source_port, destination_ip, destination_port) are all real options on px security-group-rule-add/rule-rm. protocol, the IP/CIDR fields, and the port fields are validated server-side (allow-listed characters, or parsed as an IP/CIDR) before being used. DELETE removes the rules whose fields match the request exactly; fields left out must be unset in the rule too.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"direction": "IN", "protocol": "tcp", "destination_port": "443"}' \
http://localhost:8080/api/v1/px/security-groups/web/rules
POST /px/security-groups/attach / /px/security-groups/detach
Attach or detach a security group from a container’s firewall config. group_name and vmid are both positional; both are validated server-side (group_name against an allow-listed charset, vmid as numeric).
The rules of an attached group apply only while the container’s firewall is on. "enable_firewall": true on attach turns it on, and sets firewall=1 on the container’s network interfaces; without it, the response warns when the firewall is off.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"group_name": "web", "vmid": "100", "enable_firewall": true}' \
http://localhost:8080/api/v1/px/security-groups/attach
POST /px/upload
Upload an LXC template to a Proxmox host. local_path and remote_template_name are both positional.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"local_path": "./ubuntu-22.04.tar.gz", "remote_template_name": "ubuntu-22.04"}' \
http://localhost:8080/api/v1/px/upload
POST /px/exec
Execute a command on a Proxmox host over SSH. command is a string or a list of strings; region and az are optional. Other body fields are ignored.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"command": "df -h /var/lib/vz"}' \
http://localhost:8080/api/v1/px/exec
The API refuses command arguments that contain any of ; & | ` $ ( ) { }. This check is a guard against mistakes, not a sandbox: the command runs on the host’s shell, as root, and any command can be sent through this endpoint. For pipes and command lists, use the CLI or a script on the host.
LXC Container Endpoints
POST /lxc/instances
Create and start LXC containers.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"image_id": "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst",
"size": "small",
"count": 1,
"hostname": "web-server",
"region": "eu-south-1",
"az": "az1"
}' \
http://localhost:8080/api/v1/lxc/instances
Request Body:
{
"image_id": "string (required)",
"size": "string (default: small)",
"count": "integer (default: 1)",
"hostname": "string",
"password": "string",
"ip": "string",
"netmask": "string (default: 24)",
"gateway": "string",
"dns": "string",
"dhcp": "boolean",
"storage_size": "string, root disk in GiB (uses default_storage)",
"features": "string, e.g. nesting=1,keyctl=1",
"unprivileged": "boolean",
"region": "string",
"az": "string"
}
size must be one of the instance_sizes in config.yaml.
GET /lxc/instances
List all LXC containers.
curl -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/lxc/instances?region=eu-south-1&az=az1"
GET /lxc/instances/{instance_id}
Get details of a specific container.
curl -H "X-API-Key: your-key" \
http://localhost:8080/api/v1/lxc/instances/100
POST /lxc/instances/start
Start containers. instance_ids is a list of positional arguments; every element must be numeric (Proxmox container IDs always are) or the request is rejected with 400.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"instance_ids": ["100", "101", "102"],
"region": "eu-south-1",
"az": "az1"
}' \
http://localhost:8080/api/v1/lxc/instances/start
POST /lxc/instances/stop
Stop containers. Same instance_ids handling as /lxc/instances/start above.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"instance_ids": ["100", "101"],
"region": "eu-south-1",
"az": "az1"
}' \
http://localhost:8080/api/v1/lxc/instances/stop
POST /lxc/instances/terminate
Terminate (destroy) containers. Same instance_ids handling as /lxc/instances/start above.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"instance_ids": ["100"],
"region": "eu-south-1",
"az": "az1"
}' \
http://localhost:8080/api/v1/lxc/instances/terminate
POST /lxc/instances/reboot
Reboot running containers. Same instance_ids handling as /lxc/instances/start above.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"instance_ids": ["100", "101"], "region": "eu-south-1", "az": "az1"}' \
http://localhost:8080/api/v1/lxc/instances/reboot
POST /lxc/instances/status
A one-shot resource snapshot (load average, memory, disk, swap) for one or more containers. Same instance_ids handling as /lxc/instances/start above.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"instance_ids": ["100", "101"], "region": "eu-south-1", "az": "az1"}' \
http://localhost:8080/api/v1/lxc/instances/status
POST /lxc/instances/scale
Scale container resources. Same instance_ids handling as /lxc/instances/start above. The fields are memory (MB), cpulimit, cpucores, storage_size and net_limit (MB/s), as in lxc scale. storage_size grows the root disk with pct resize ("64G", or "+8G" to add 8 GiB); disks cannot shrink. disk_read_limit and disk_write_limit are refused: Proxmox has no disk bandwidth limits for containers.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"instance_ids": ["100"],
"memory": 4096,
"cpulimit": 4,
"storage_size": "64G",
"region": "eu-south-1",
"az": "az1"
}' \
http://localhost:8080/api/v1/lxc/instances/scale
POST /lxc/instances/clone
Clone an LXC container. source_instance_id and target_instance_id are both positional in lxc clone.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"source_instance_id": "100", "target_instance_id": "200", "full": true}' \
http://localhost:8080/api/v1/lxc/instances/clone
POST /lxc/instances/{instance_id}/exec
Execute a command in a container. command is a string, split into words like a shell would split it, or a list of strings; it runs in the container without a shell.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"command": "apt-get -y upgrade",
"region": "eu-south-1",
"az": "az1"
}' \
http://localhost:8080/api/v1/lxc/instances/100/exec
As with /px/exec, command arguments that contain any of ; & | ` $ ( ) { } are refused. Send one request per command, or run a script that is already in the container.
POST /lxc/instances/{instance_id}/snapshots
Create a snapshot. snapshot_name is a positional argument in lxc snapshot-add.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"snapshot_name": "before-update",
"region": "eu-south-1",
"az": "az1"
}' \
http://localhost:8080/api/v1/lxc/instances/100/snapshots
DELETE /lxc/instances/{instance_id}/snapshots/{snapshot_name}
Delete a snapshot.
curl -X DELETE \
-H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/lxc/instances/100/snapshots/before-update?region=eu-south-1&az=az1"
GET /lxc/instances/{instance_id}/snapshots
List all snapshots of a container.
curl -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/lxc/instances/100/snapshots?region=eu-south-1&az=az1"
POST /lxc/instances/{instance_id}/volumes/attach
Attach a storage volume. All three fields are required (400 if any is missing). volume_name and volume_size are both positional in lxc volume-attach; mount_point is a real option, enforced by a manual check in that command rather than by Click.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"volume_name": "local-lvm", "volume_size": "16G", "mount_point": "/mnt/data"}' \
http://localhost:8080/api/v1/lxc/instances/100/volumes/attach
POST /lxc/instances/{instance_id}/volumes/detach
Detach a storage volume — this always removes the container’s mp0 mount, regardless of volume_name. volume_name is positional in lxc volume-detach.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"volume_name": "local-lvm"}' \
http://localhost:8080/api/v1/lxc/instances/100/volumes/detach
POST /lxc/instances/{instance_id}/service
Run a systemctl action against a service inside a container. action and service_name are both positional in lxc service.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"action": "restart", "service_name": "nginx"}' \
http://localhost:8080/api/v1/lxc/instances/100/service
POST /lxc/instances/{instance_id}/migrate
Migrate a container to another node of the same Proxmox cluster. target_host is the node name as the cluster knows it, not a zone from config.yaml. "restart": true moves a running container by stopping it and starting it on the target; target_storage puts its disks on another storage there.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"target_host": "pve2", "restart": true}' \
http://localhost:8080/api/v1/lxc/instances/100/migrate
GET /lxc/instances/{instance_id}/storage
List storage usage inside a container.
curl -H "X-API-Key: your-key" \
http://localhost:8080/api/v1/lxc/instances/100/storage
GET /lxc/instances/{instance_id}/scale-check
Read-only scaling recommendation based on the scaling thresholds in config.yaml.
curl -H "X-API-Key: your-key" \
http://localhost:8080/api/v1/lxc/instances/100/scale-check
GET /lxc/instances/{instance_id}/net-check
Check whether a TCP/UDP port is open on a container. protocol and port are both positional in lxc net.
curl -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/lxc/instances/100/net-check?protocol=tcp&port=443"
GET /lxc/instances/{instance_id}/info
Retrieve IP address(es), in-container hostname, DNS servers, and the container’s Proxmox-side hostname.
curl -H "X-API-Key: your-key" \
http://localhost:8080/api/v1/lxc/instances/100/info
GET /lxc/instances/{instance_id}/public-ip
Retrieve the public IP address(es) of a container.
curl -H "X-API-Key: your-key" \
http://localhost:8080/api/v1/lxc/instances/100/public-ip
GET /lxc/instances/{instance_id}/health-check
Run health checks on a container.
curl -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/lxc/instances/100/health-check"
?fix=true adds --fix: when the disk is over 80% full, files older than 7 days in /tmp and /var/tmp are deleted, and when DNS fails, networking is restarted. In every query string, true and false are read as flags: true adds the option, false leaves it out.
POST /lxc/instances/{instance_id}/restore
Restore a vzdump backup with pct restore. backup_file is a path on the Proxmox host or a volume ID (local:backup/...). Optional fields: storage for the restored disks (default: default_storage from config.yaml; the request fails when neither is set), no_start to leave the container stopped.
The CLI asks for confirmation before it restores, and the API cannot answer: send "force": true, or the request fails. If the container exists, force replaces it, and its current disks are destroyed. The backup file itself is kept.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"backup_file": "/var/lib/vz/dump/vzdump-lxc-100-2026_10_10-02_00_00.tar.zst", "force": true}' \
http://localhost:8080/api/v1/lxc/instances/100/restore
POST /lxc/instances/{instance_id}/backup
Create a vzdump backup of a container. The fields are the options of lxc backup-create: destination (a directory on the host, default /var/lib/vz/dump) or storage (a Proxmox backup storage), mode (snapshot, suspend or stop) and compress (zstd, gzip, lzo or none). An empty body uses the defaults. The response includes the archive name.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"storage": "backups", "mode": "snapshot", "compress": "zstd"}' \
http://localhost:8080/api/v1/lxc/instances/100/backup
download copies the archive to the machine that runs the API, into its working directory.
GET /lxc/instances/{instance_id}/report
Generate a comprehensive report about a container.
curl -H "X-API-Key: your-key" \
http://localhost:8080/api/v1/lxc/instances/100/report
GET /lxc/instances/{instance_id}/resources
Monitor container resources.
curl -H "X-API-Key: your-key" \
http://localhost:8080/api/v1/lxc/instances/100/resources
Docker/App Endpoints
POST /lxc/instances/{instance_id}/app/setup
Install Docker and Docker Compose in a running container. "enable_nesting": true first sets the LXC features Docker needs (nesting=1, and keyctl=1 for unprivileged containers) and restarts the container.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"enable_nesting": true,
"region": "eu-south-1",
"az": "az1"
}' \
http://localhost:8080/api/v1/lxc/instances/100/app/setup
POST /lxc/instances/{instance_id}/app/run
Run docker run in a container. docker_command holds the arguments of docker run, as a list or as one string.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"docker_command": ["-d", "-p", "80:80", "nginx"],
"region": "eu-south-1",
"az": "az1"
}' \
http://localhost:8080/api/v1/lxc/instances/100/app/run
POST /lxc/instances/{instance_id}/app/deploy
Manage a Compose application. action is one of install, uninstall, start, stop, restart and status. compose_file is a path on the machine that runs the API, or a URL; it is copied into the container under /opt/lws/apps/<app>/, where <app> is the first service name. auto_start (with install) adds a systemd unit in the container that starts the application at boot.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"action": "install",
"compose_file": "/path/to/docker-compose.yml",
"auto_start": true,
"region": "eu-south-1",
"az": "az1"
}' \
http://localhost:8080/api/v1/lxc/instances/100/app/deploy
POST /lxc/instances/{instance_id}/app/update
Copy a new version of the Compose file into the container, pull its images and recreate the services that changed. compose_file is required.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"compose_file": "/path/to/docker-compose.yml"}' \
http://localhost:8080/api/v1/lxc/instances/100/app/update
GET /lxc/instances/{instance_id}/app/logs/{container_name}
Get Docker logs.
curl -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/lxc/instances/100/app/logs/nginx?tail=100"
tail is the number of lines from the end of the log, or all (the default). Streaming (docker logs --follow) is not available: the request returns the log as it is when the request arrives.
GET /lxc/instances/{instance_id}/app/containers
List Docker containers running inside an LXC container.
curl -H "X-API-Key: your-key" \
http://localhost:8080/api/v1/lxc/instances/100/app/containers
POST /lxc/instances/app/remove
Uninstall Docker and Compose from one or more containers. instance_ids is a list of positional arguments; every element must be numeric or the request is rejected with 400. "purge": true first removes all Docker containers, images, volumes and networks.
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"instance_ids": ["100", "101"]}' \
http://localhost:8080/api/v1/lxc/instances/app/remove
Security Endpoints
GET /sec/discovery
Discover reachable hosts. sec discovery’s lxc_id argument is optional and, when present, positional — as in the example below with ?lxc_id=....
curl -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/sec/discovery?lxc_id=100"
GET /lxc/instances/{instance_id}/sec/scan
Security scan a container.
curl -H "X-API-Key: your-key" \
"http://localhost:8080/api/v1/lxc/instances/100/sec/scan?scan_type=full"
Web UI
The API includes a simple web interface at the root URL:
http://localhost:8080/
The Web UI provides:
- API key configuration
- Quick access to common operations
- Response visualization
Swagger Documentation
Interactive API documentation is available at:
http://localhost:8080/api/v1/docs
Features:
- Interactive endpoint testing
- Request/response schemas
- Authentication testing
Error Codes
| Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Invalid or missing API key |
| 404 | Not Found - Resource doesn’t exist |
| 500 | Internal Server Error - Command execution failed |
Rate Limiting
Currently, there are no rate limits. For production use, consider implementing rate limiting via a reverse proxy (nginx, Caddy).
CORS Configuration
Configure allowed origins in config.yaml. Omitting allowed_origins denies all cross-origin browser access by default — this only affects browser-based JS; curl/scripts/server-to-server callers are never subject to CORS:
api:
allowed_origins:
- "https://dashboard.example.net"
The web UI at / is served by the API itself, so it needs no entry.
Production Deployment
api.py uses waitress, a production WSGI server, whenever api.debug is false (the default). api.debug: true switches to Flask’s development server, meant for local use; the interactive debugger and the auto-reloader stay off either way, because Werkzeug’s debugger allows arbitrary code execution.
Running the API in production covers the reverse proxy, TLS, a systemd unit and the Docker image.
Example: Complete Workflow
1. Check API Health
curl http://localhost:8080/api/v1/health
2. List Proxmox Hosts
curl -H "X-API-Key: your-key" \
http://localhost:8080/api/v1/px/hosts
3. Create Container
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"image_id": "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst",
"size": "small",
"hostname": "api-test"
}' \
http://localhost:8080/api/v1/lxc/instances
4. Monitor Container
curl -H "X-API-Key: your-key" \
http://localhost:8080/api/v1/lxc/instances/100/resources
5. Execute Command
curl -X POST \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"command": "uptime"}' \
http://localhost:8080/api/v1/lxc/instances/100/exec
Python Client Example
import requests
class LWSClient:
def __init__(self, base_url, api_key):
self.base_url = base_url
self.headers = {'X-API-Key': api_key}
def list_containers(self):
url = f"{self.base_url}/api/v1/lxc/instances"
response = requests.get(url, headers=self.headers)
return response.json()
def create_container(self, image_id, size='small'):
url = f"{self.base_url}/api/v1/lxc/instances"
data = {'image_id': image_id, 'size': size}
response = requests.post(url, json=data, headers=self.headers)
return response.json()
# Usage
client = LWSClient('http://localhost:8080', 'your-api-key')
containers = client.list_containers()
print(containers)