Create a VM
Creates a server and starts it, usually in about a minute; poll Get a VM until its status is active. You log in as scamp, with sudo, with the SSH key, the password or both. With assign_public_ips the server gets a public IPv4 and IPv6 once it is up. The first hour is charged at once and then every hour after it. Send an Idempotency-Key header to retry safely: the same key within 24 hours answers with the same server instead of making a second one.
Parameters
| Name | In | Description |
|---|---|---|
| Idempotency-Key | header, string, optional | Any string of yours, up to 200 characters. The same key within 24 hours answers with the server the first request made instead of making a second one. |
Request body
| Field | Type | Description |
|---|---|---|
| display_name | string, optional | Name of the server, up to 100 characters |
| vm_class_id | integerrequired | Plan id, from List plans |
| storage_class_id | integerrequired | Disk id of the root disk, from List disks |
| vm_template_id | integer, optional | Image id, from List images. Give this, or source_snapshot_id or source_backup_id |
| source_snapshot_id | string, optional | Make the server from a snapshot of another one |
| source_backup_id | string, optional | Make the server from a backup |
| network_uuid | stringrequired | Private network, from List networks |
| sg_uuid | string, optional | Security group, from List security groups; the default group of the account when left out |
| ssh_key_id | integer, optional | SSH key of the scamp user, from List SSH keys |
| disk_gb | integerrequired | Root disk size, GB. The first 25 GB of a standard disk are free |
| os_password | string, optional | Password of the scamp user, 8 to 64 characters; generated and returned when left out |
| assign_public_ips | boolean | Give the server a public IPv4 and IPv6 address. The network has to be on a router |
| tags | map of string, optional | Your tags, up to 10: key up to 64 characters, value up to 256 |
| monitoring | boolean, optional | The guest agent and monitoring; on when left out. Disk auto-extend needs them |
| autoextend | VMAutoextendIn, optional | Grow the root disk and its filesystem before it fills up |
| autoextend.enabled | boolean | Auto-extend on |
| autoextend.at_pct | integer | Grow when the root filesystem is this full, percent |
| autoextend.step_pct | integer | Grow by this share of the current disk size: 10, 20, 30 or 50 percent. Rounded to 5 GB, at least 5 GB. |
| autoextend.max_gb | integerrequired | Never grow the disk past this size, GB |
| user_data | string, optional | cloud-init user data: #cloud-config or a script starting with #!, up to 64 KB. Runs once at first boot, after the platform's own setup |
Example request
curl -X POST "https://platform.serverscamp.com/api/v1/vms" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "web-1",
"vm_class_id": 49,
"vm_template_id": 5,
"storage_class_id": 18,
"disk_gb": 50,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"ssh_key_id": 42,
"assign_public_ips": true
}'import os, requests
token = os.environ["API_TOKEN"]
r = requests.post(
f"https://platform.serverscamp.com/api/v1/vms",
headers={"Authorization": f"Bearer {token}"},
json={
"display_name": "web-1",
"vm_class_id": 49,
"vm_template_id": 5,
"storage_class_id": 18,
"disk_gb": 50,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"ssh_key_id": 42,
"assign_public_ips": True
},
)
r.raise_for_status()
print(r.json())const r = await fetch(`https://platform.serverscamp.com/api/v1/vms`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.API_TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
"display_name": "web-1",
"vm_class_id": 49,
"vm_template_id": 5,
"storage_class_id": 18,
"disk_gb": 50,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"ssh_key_id": 42,
"assign_public_ips": true
}),
});
console.log(r.status, await r.json());curl -X POST "https://platform.serverscamp.com/api/v1/vms" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "test-1",
"vm_class_id": 49,
"vm_template_id": 5,
"storage_class_id": 18,
"disk_gb": 50,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"assign_public_ips": true,
"os_password": "change-me-to-a-long-one"
}'import os, requests
token = os.environ["API_TOKEN"]
r = requests.post(
f"https://platform.serverscamp.com/api/v1/vms",
headers={"Authorization": f"Bearer {token}"},
json={
"display_name": "test-1",
"vm_class_id": 49,
"vm_template_id": 5,
"storage_class_id": 18,
"disk_gb": 50,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"assign_public_ips": True,
"os_password": "change-me-to-a-long-one"
},
)
r.raise_for_status()
print(r.json())const r = await fetch(`https://platform.serverscamp.com/api/v1/vms`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.API_TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
"display_name": "test-1",
"vm_class_id": 49,
"vm_template_id": 5,
"storage_class_id": 18,
"disk_gb": 50,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"assign_public_ips": true,
"os_password": "change-me-to-a-long-one"
}),
});
console.log(r.status, await r.json());curl -X POST "https://platform.serverscamp.com/api/v1/vms" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "web-2",
"vm_class_id": 49,
"vm_template_id": 5,
"storage_class_id": 18,
"disk_gb": 50,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"ssh_key_id": 42,
"assign_public_ips": true,
"user_data": "#cloud-config\npackages:\n - nginx\nruncmd:\n - systemctl enable --now nginx\n"
}'import os, requests
token = os.environ["API_TOKEN"]
r = requests.post(
f"https://platform.serverscamp.com/api/v1/vms",
headers={"Authorization": f"Bearer {token}"},
json={
"display_name": "web-2",
"vm_class_id": 49,
"vm_template_id": 5,
"storage_class_id": 18,
"disk_gb": 50,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"ssh_key_id": 42,
"assign_public_ips": True,
"user_data": "#cloud-config\npackages:\n - nginx\nruncmd:\n - systemctl enable --now nginx\n"
},
)
r.raise_for_status()
print(r.json())const r = await fetch(`https://platform.serverscamp.com/api/v1/vms`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.API_TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
"display_name": "web-2",
"vm_class_id": 49,
"vm_template_id": 5,
"storage_class_id": 18,
"disk_gb": 50,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"ssh_key_id": 42,
"assign_public_ips": true,
"user_data": "#cloud-config\npackages:\n - nginx\nruncmd:\n - systemctl enable --now nginx\n"
}),
});
console.log(r.status, await r.json());curl -X POST "https://platform.serverscamp.com/api/v1/vms" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "db-1",
"vm_class_id": 66,
"vm_template_id": 5,
"storage_class_id": 20,
"disk_gb": 200,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"ssh_key_id": 42,
"assign_public_ips": true
}'import os, requests
token = os.environ["API_TOKEN"]
r = requests.post(
f"https://platform.serverscamp.com/api/v1/vms",
headers={"Authorization": f"Bearer {token}"},
json={
"display_name": "db-1",
"vm_class_id": 66,
"vm_template_id": 5,
"storage_class_id": 20,
"disk_gb": 200,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"ssh_key_id": 42,
"assign_public_ips": True
},
)
r.raise_for_status()
print(r.json())const r = await fetch(`https://platform.serverscamp.com/api/v1/vms`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.API_TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
"display_name": "db-1",
"vm_class_id": 66,
"vm_template_id": 5,
"storage_class_id": 20,
"disk_gb": 200,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"ssh_key_id": 42,
"assign_public_ips": true
}),
});
console.log(r.status, await r.json());curl -X POST "https://platform.serverscamp.com/api/v1/vms" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Idempotency-Key: web-1-2026-11-01" \
-H "Content-Type: application/json" \
-d '{
"display_name": "web-1",
"vm_class_id": 49,
"vm_template_id": 5,
"storage_class_id": 18,
"disk_gb": 50,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"ssh_key_id": 42,
"assign_public_ips": true
}'import os, requests
token = os.environ["API_TOKEN"]
r = requests.post(
f"https://platform.serverscamp.com/api/v1/vms",
headers={"Authorization": f"Bearer {token}", "Idempotency-Key": "web-1-2026-11-01"},
json={
"display_name": "web-1",
"vm_class_id": 49,
"vm_template_id": 5,
"storage_class_id": 18,
"disk_gb": 50,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"ssh_key_id": 42,
"assign_public_ips": True
},
)
r.raise_for_status()
print(r.json())const r = await fetch(`https://platform.serverscamp.com/api/v1/vms`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.API_TOKEN}`, 'Content-Type': 'application/json', 'Idempotency-Key': 'web-1-2026-11-01' },
body: JSON.stringify({
"display_name": "web-1",
"vm_class_id": 49,
"vm_template_id": 5,
"storage_class_id": 18,
"disk_gb": 50,
"network_uuid": "a3e1f0c2-7d4b-4c6e-8f9a-0b1c2d3e4f50",
"ssh_key_id": 42,
"assign_public_ips": true
}),
});
console.log(r.status, await r.json());Got no answer, a timeout or a dropped connection? Send the same request with the same key: it answers with the server the first one made, and no second server is created.
Answer
Status 201 with this body.
| Field | Type | Description |
|---|---|---|
| vm_uuid | stringrequired | Server id |
| display_name | string, optional | The name you gave |
| status | stringrequired | Always queued: poll Get a VM until status is active |
| os_user | stringrequired | User to log in as: scamp, with sudo |
| os_password | stringrequired | Its password, yours or generated |
{
"vm_uuid": "6f1c2a9e-4b3d-4e8a-9c71-2d5e8f0a1b34",
"display_name": "web-1",
"status": "queued",
"os_user": "scamp",
"os_password": "Rk7vQ2mXp9LwT4sN"
}Wait until it is ready
The answer comes at once and the server is made in the background, usually in about a minute. Get a VM shows how far it got in its status field.
Check the status
Ask Get a VM with the server's id; the field to watch is status:
curl "https://platform.serverscamp.com/api/v1/vms/$VM_UUID" \
-H "Authorization: Bearer $API_TOKEN"| status | What it means |
|---|---|
pending | The server is being made: the disk, the network and the system |
active | Ready. SSH answers once the system has booted, a few seconds later |
failed | Something went wrong; the error field says what. Delete the server and create it again |
Wait in a loop
Ask every few seconds until it gets there, and stop on an error.
# VM_UUID: vm_uuid of the server, from the answer above
while true; do
VM=$(curl -s "https://platform.serverscamp.com/api/v1/vms/$VM_UUID" -H "Authorization: Bearer $API_TOKEN")
STATUS=$(echo "$VM" | jq -r .status)
echo "$STATUS"
[ "$STATUS" = active ] && break
case "$STATUS" in failed|error) echo "$VM" | jq -r .error; exit 1;; esac
sleep 5
done
curl -s "https://platform.serverscamp.com/api/v1/vms/$VM_UUID" -H "Authorization: Bearer $API_TOKEN" | jq -r '"ssh scamp@" + .network.public_ip_v4'# vm_uuid: vm_uuid of the server, from the answer above
import os, time, requests
headers = {"Authorization": f"Bearer {os.environ['API_TOKEN']}"}
while True:
vm = requests.get(f"https://platform.serverscamp.com/api/v1/vms/{vm_uuid}", headers=headers).json()
print(vm["status"])
if vm["status"] == "active":
break
if vm["status"] in ("failed", "error"):
raise SystemExit(vm["error"])
time.sleep(5)
print(f"ssh scamp@{vm['network']['public_ip_v4']}")// vmUuid: vm_uuid of the server, from the answer above
const headers = { Authorization: `Bearer ${process.env.API_TOKEN}` };
let vm;
for (;;) {
vm = await (await fetch(`https://platform.serverscamp.com/api/v1/vms/${vmUuid}`, { headers })).json();
console.log(vm.status);
if (vm.status === 'active') break;
if (vm.status === 'failed' || vm.status === 'error') throw new Error(vm.error);
await new Promise((ok) => setTimeout(ok, 5000));
}
console.log(`ssh scamp@${vm.network.public_ip_v4}`);The bash version needs jq.
Errors
| Status | When |
|---|---|
400 | A plan, disk, size or value that is not accepted, or a public address on a network without a router |
401 | No token, or a token that is not valid |
402 | The balance is too low |
403 | The server limit of the account is reached |
404 | No such network or SSH key in your account |
409 | The plan has no free capacity right now |
422 | The body or a parameter is not what the operation takes |
Every operation can also answer 401 without a valid token. All errors