Docs / API Reference

REST API reference

Authenticate with an API key and manage your Piedmont Hosting servers, firewall rules, snapshots and backups over the REST API.

API Overview

The Piedmont Hosting REST API lets you script day-to-day operations on your existing VPS servers: power actions, cloud firewall rules, snapshots and backups. It covers VPS services only, and it can't create, resize, reinstall or delete servers — do those in the portal.

Base URL

https://vps.piedmonthosting.com/api/v1

Response Format

All responses are JSON. Most successful responses wrap their data in a named object — for example {"server": {…}} or {"servers": […]} — and status values are lowercase. Times are ISO 8601 in UTC. Errors return an error field with a human-readable message.

Success Response — GET /api/v1/servers/:id
{
  "server": {
    "id": "clx0abc123def456",
    "name": "my-server (2vCPU/4GB/40GB)",
    "hostname": "my-server",
    "status": "active",
    "os": "ubuntu24",
    "vcpus": 2,
    "memory_mb": 4096,
    "disk_mb": 40960,
    "bandwidth_mbps": 250,
    "ip_address": "203.0.113.40",
    "private_ip": "10.10.50.40",
    "monthly_price": 19,
    "created_at": "2026-10-01T12:00:00.000Z",
    "updated_at": "2026-10-01T12:05:00.000Z"
  }
}
Error Response
{
  "error": "Server not found"
}

Status Codes

CodeMeaning
200, 201, 202Success. 201 means something was created; 202 means the job was started and finishes in the background.
400The request was invalid — the error says what to fix
401Missing, invalid, revoked or expired API key
403The key doesn't have the permission this action needs (a manual backup also returns 403 while backups aren't enabled)
404No such server on your account — or, for most changes, the server isn't active
409A limit was reached, or something is already in progress
500Something went wrong on our side

Authentication

API requests are authenticated with API keys. Create one on the API Keys page. A key looks like ph_live_ followed by 32 hex characters and is shown only once, so copy it when you create it. You can have up to 10 active keys.

Using Your API Key

Include your API key in the Authorization header of every request:

Request Header
Authorization: Bearer ph_live_your_api_key_here
cURL Example
curl -H "Authorization: Bearer ph_live_your_api_key_here" \
  https://vps.piedmonthosting.com/api/v1/servers

Permissions

Every key can call the read (GET) endpoints. A Read Only key can do only that; to make changes, give the key the matching permission — or choose Full Access, which allows everything. A request outside a key's permissions returns 403 with {"error": "Insufficient permissions"}.

PermissionAPI valueAllows
Full Access*Everything below, plus creating and revoking API keys over the API
Read OnlyreadRead endpoints only
PowerpowerPower actions
FirewallfirewallAdding, changing and deleting firewall rules, and applying presets
SnapshotssnapshotsCreating, restoring and deleting snapshots
BackupsbackupsManual backups, restores and backup settings — including turning the paid backup add-on on or off
Warning
Keep your API keys secret — don't share them or commit them to version control. If a key is compromised, revoke it on the API Keys page and create a new one.

Managing Keys over the API

Creating or revoking a key needs a Full Access key (or a signed-in portal session); any other key gets 403. Listing keys works with any key.

GET /api/v1/api-keys
curl -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/api-keys

Returns {"api_keys": […]} — your active keys with their name, prefix, permissions, last use and expiry. The keys themselves are never shown again.

POST /api/v1/api-keys
curl -X POST -H "Authorization: Bearer ph_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Backup script",
    "permissions": ["backups"],
    "expires_in_days": 90
  }' \
  https://vps.piedmonthosting.com/api/v1/api-keys

name is required (up to 100 characters). permissions is a list of the API values above and defaults to ["*"] (Full Access) if you leave it out, so list only what the key needs. expires_in_days is optional; keys created on the API Keys page don't expire. Returns 201 with the key details in api_key and the key itself in key — the only time it's shown. An eleventh active key returns 400.

DELETE /api/v1/api-keys/:keyId
curl -X DELETE -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/api-keys/KEY_ID

Revokes the key immediately and returns {"message": "API key revoked"}.

Server Endpoints

In the paths below, SERVER_ID is a server's id from GET /api/v1/servers — it's also the last part of the server's page address in the portal (/services/SERVER_ID). Game servers and web hosting aren't available through the API and return 404.

List All Servers

GET /api/v1/servers
curl -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/servers

Returns {"servers": […]} — every VPS on your account that hasn't been terminated, newest first. Each server has id, name, hostname, status (lowercase, e.g. active or suspended), os, vcpus, memory_mb, disk_mb, bandwidth_mbps (the port-speed cap), ip_address (public IPv4), private_ip (the server's internal 10.10.50.x address, not its VPC address), monthly_price, created_at and updated_at.

Get Server Details

GET /api/v1/servers/:id
curl -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID

Returns {"server": {…}} with the same fields — see the example under API Overview.

Get Server Power Status

GET /api/v1/servers/:id/status
curl -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/status

Returns {"server_id", "vm_name", "state"}, where state is the hypervisor's live state — for example running, shut off or paused (unknown if it can't be read).

Power Actions

POST /api/v1/servers/:id/power
curl -X POST -H "Authorization: Bearer ph_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"action": "reboot"}' \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/power

Valid actions: start, shutdown, reboot, reset. shutdown and reboot ask the operating system to shut down or restart cleanly; reset is an immediate hard restart. Requires the Power permission. The command is sent right away and the response — {"action", "server_id", "message"} — doesn't wait for it to finish; check /status to see the result.

Firewall Endpoints

Warning
These endpoints drive the same cloud firewall as the portal. A server with no rules allows all traffic; your first inbound rule switches inbound traffic to allow-list mode, so anything you haven't accepted — SSH included — is dropped. Add an SSH rule (or apply a preset) before anything else.

List Firewall Rules

GET /api/v1/servers/:id/firewall
curl -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/firewall

Returns {"rules": […], "presets": […]}. Each rule has id, direction, protocol, port_range, source_cidr, action, description, priority, enabled and synced.

Add Firewall Rule

POST /api/v1/servers/:id/firewall
curl -X POST -H "Authorization: Bearer ph_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "direction": "INBOUND",
    "protocol": "TCP",
    "port_range": "443",
    "source_cidr": "0.0.0.0/0",
    "action": "ACCEPT",
    "description": "Allow HTTPS"
  }' \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/firewall
FieldValuesDefault
directionINBOUND or OUTBOUNDINBOUND
protocolTCP, UDP, ICMP or ANYTCP
port_rangeA port ("443") or a range ("1000-2000"), as a string. Required for TCP and UDP rules — a TCP or UDP rule without one isn't applied. Leave it out for ICMP; for ANY, leaving it out matches all traffic from source_cidr.—
source_cidrWho the rule applies to: 0.0.0.0/0 for anyone, or an IPv4 address or range such as 203.0.113.0/240.0.0.0/0
actionACCEPT or DROPACCEPT
descriptionYour own label, shown in the portal—
priorityLower numbers are checked first100

Returns 201 with the new rule in {"rule": {…}}. The server must be active, each server can have up to 50 rules (409 after that), and the Firewall permission is required.

Update Firewall Rule

PUT /api/v1/servers/:id/firewall/:ruleId
curl -X PUT -H "Authorization: Bearer ph_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}' \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/firewall/RULE_ID

Send any of direction, protocol, port_range, source_cidr, action, description, priority and enabled, in the same formats as when adding a rule. Returns the updated rule.

Delete Firewall Rule

DELETE /api/v1/servers/:id/firewall/:ruleId
curl -X DELETE -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/firewall/RULE_ID

Apply Firewall Preset

POST /api/v1/servers/:id/firewall/preset
curl -X POST -H "Authorization: Bearer ph_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"preset": "web-server"}' \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/firewall/preset

Available presets: web-server (Web Server), game-server (Game Server), proxmox-node (Proxmox VE Node), ssh-only (SSH Only), all-open (All Open). Applying a preset deletes every existing rule on the server — including rules the App Marketplace added for your apps — and replaces them with the preset's rules. Returns {"message", "rules_count"}.

Snapshot Endpoints

Important
Creating a snapshot of a running server finishes with a brief forced power-off and restart. Restoring one forcibly powers the server off, replaces its disk with the snapshot and starts it again — anything written after the snapshot was taken is lost, and this can't be undone. See Snapshots.

List Snapshots

GET /api/v1/servers/:id/snapshots
curl -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/snapshots

Returns {"snapshots": […], "limit": 3, "used": 1}. Each snapshot has id, name, status (creating, available, restoring or failed), size_bytes, created_at and completed_at. GET /api/v1/servers/:id/snapshots/:snapshotId returns a single snapshot.

Create Snapshot

POST /api/v1/servers/:id/snapshots
curl -X POST -H "Authorization: Bearer ph_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"name": "before-upgrade"}' \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/snapshots

Returns 202 with {"snapshot": {"id", "name", "status": "creating"}}; poll the list until it's available. Names keep only letters, numbers, - and _ (other characters are removed) and are cut to 64 characters. The server must be active; creating one more snapshot than the limit returns 409.

Restore Snapshot

POST /api/v1/servers/:id/snapshots/:snapshotId/restore
curl -X POST -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/snapshots/SNAPSHOT_ID/restore

Returns 202. The snapshot must be available and the server active.

Delete Snapshot

DELETE /api/v1/servers/:id/snapshots/:snapshotId
curl -X DELETE -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/snapshots/SNAPSHOT_ID

A snapshot that's still being created or restored can't be deleted (409). All snapshot changes require the Snapshots permission.

Backup Endpoints

These endpoints manage the paid automated backups add-on. All changes require the Backups permission.

List Backups

GET /api/v1/servers/:id/backups
curl -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/backups

Returns {"backups": […], "config": {…}} — up to the 30 most recent backups that haven't expired (status is running, completed or failed) and the current backup settings.

Start a Manual Backup

POST /api/v1/servers/:id/backups
curl -X POST -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/backups

Starts a backup immediately and returns 202. Backups must be enabled first (403 otherwise), and only one can run at a time (409).

Configure Backups

PUT /api/v1/servers/:id/backup-config
curl -X PUT -H "Authorization: Bearer ph_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "retention_days": 14,
    "schedule_hour_utc": 4
  }' \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/backup-config

Every field is optional. retention_days (1–30, default 7) sets how long backups are kept, and schedule_hour_utc (0–23, default 3) sets the hour the daily backup runs. Setting enabled to true adds the automated-backup add-on (10% of the server's price) to your subscription, prorated; false removes it. GET /api/v1/servers/:id/backup-config returns the current settings.

Restore Backup

POST /api/v1/servers/:id/backups/:backupId/restore
curl -X POST -H "Authorization: Bearer ph_live_xxx" \
  https://vps.piedmonthosting.com/api/v1/servers/SERVER_ID/backups/BACKUP_ID/restore

Returns 202. The backup must be completed, and a restore can't start while a backup is running (409).

Important
Restoring a backup powers the server off and replaces all of its current data, which can't be undone. The backup is checked before your server is powered off.