Skip to content

Deploy a pay-as-you-go Cloud VPS

5 steps · Checked against the API code 3 November 2026

A guide does one task from start to finish. Every endpoint, with all its fields and cases, is in the API reference.

There are two ways to get a VPS through the API:

  • Order a VPS: a fixed price per month or year. You pay an invoice first. You choose the operating system and an SSH key in the order.
  • Deploy a Cloud VPS (this guide): billed by the hour, with no invoice up front. The account must be verified and have paid Hostup at least 2,000 SEK. You cannot choose the operating system or an SSH key at deploy.

You choose the CPU cores, memory and disk. CPU and memory are billed by the hour while the server runs. The disk is billed for as long as the server exists. Data transfer is billed per GB. One public IPv4 address is included.

  • Your API key in $HOSTUP_API_KEY, as set in the Quickstart. It needs these scopes (Keys and scopes):
    • read:vm to read the options, the limits and the server.
    • write:billing to deploy and to ask for a higher limit. It also covers read:billing for the monthly estimate after the steps.
  • An account that may use Cloud VPS. It needs a verified identity, Cloud VPS turned on, a country in the account details and at least 2,000 SEK paid to Hostup. In some countries it also needs a saved card. Step 1 shows what is missing.
  • jq for the loop in step 4.

Get Cloud VPS deployment options says whether this account can deploy now. It also gives the hourly rates, the sizes one server can have and the regions.

GET /api/v2/vps/payg/deployments
curl "https://cloud.hostup.se/api/v2/vps/payg/deployments" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"available": true,
"reason": null,
"access": {
"verified": true,
"cloudAccessEnabled": true,
"trustedHistoryEligible": true,
"blockerReasons": [],
"…": "…"
},
"actions": {
"canDeploy": { "allowed": true, "reason": null }
},
"pricing": {
"currencyCode": "SEK",
"cpuPerCoreHour": 0.015,
"memoryPerGbHour": 0.004,
"storagePerGbHour": 0.00025,
"bandwidthPerGb": 0.01,
"ipPerHour": 0.03
},
"defaults": {
"regionId": "stockholm",
"resources": { "cpuCores": 2, "memoryGb": 2, "storageGb": 50 }
},
"limits": {
"cpuCores": { "min": 1, "max": 32 },
"memoryGb": { "min": 1, "max": 256 },
"storageGb": { "min": 50, "max": 2000 }
},
"regions": [
{ "id": "stockholm", "name": "Stockholm, Sweden", "available": true }
]
}

pricing holds the hourly rates in the account’s currency (currencyCode): cpuPerCoreHour, memoryPerGbHour and storagePerGbHour. bandwidthPerGb is the rate per GB of data transfer. ipPerHour is the rate for each extra IPv4 address. The included address is not billed.

limits is the size range for one server. regions lists where it can run.

canDeploy does not show an overdue Cloud VPS invoice. The deploy refuses that with its own error, payg_payment_overdue.

reason is a short general text about one blocker. It does not always say what to do. access.blockerReasons lists every blocker as a code. Clear each one:

Get Cloud VPS account limits shows how much the account may use across all its Cloud VPS together. The limits in step 1 are for one server. These are for the whole account.

GET /api/v2/vps/payg/limits
curl "https://cloud.hostup.se/api/v2/vps/payg/limits" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"current": {
"cpuCores": 2,
"memoryGb": 4,
"storageGb": 80,
"instanceCount": 1,
"…": "…"
},
"max": {
"cpuCores": 8,
"memoryGb": 16,
"storageGb": 500,
"instanceCount": 3,
"…": "…"
},
"remaining": {
"cpuCores": 6,
"memoryGb": 12,
"storageGb": 420,
"instanceCount": 2,
"…": "…"
},
"hasPendingRequests": false,
"pendingRequests": [],
"…": "…"
}

max is the account’s limit. current is what its Cloud VPS use now. remaining is what is left. Plan the new server inside remaining.

The deploy itself does not check these limits today. A server past them is still created and billed. Later resizes and extra disks are then refused.

Ask for a higher limit before you deploy. Request a higher Cloud VPS limit asks for one limit at a time.

POST /api/v2/vps/payg/limits/requests
curl -X POST "https://cloud.hostup.se/api/v2/vps/payg/limits/requests" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"limitType": "cpuCores",
"currentLimit": 8,
"requestedLimit": 16,
"reason": "Load tests for the new web shop need 16 cores"
}'
Response 200
{
"id": "payglim_01hxa3b4c5d6e7f8g9h0j1k2m3",
"status": "submitted"
}
  • limitType is cpuCores, memoryGb, storageGb or vmCount for the limits above. The number of servers is called vmCount here and instanceCount in the limits. The reference lists the other limit types.
  • currentLimit is the limit now: the matching value in max above. Leave it out and the request records 0.
  • requestedLimit is the limit you want. It must be higher than currentLimit.
  • reason needs at least 10 characters.

Hostup reviews each request. The account holder gets an e-mail with the decision. When a request for cores, memory, disk or servers is approved, max in the limits above shows the new limit. Until the decision the request is listed in pendingRequests. Only one request per limit can wait at a time.

Create a Cloud VPS creates the server. The request has no field for the operating system or for an SSH key. The server starts without a key. You log in with the root password from step 5. To run another system, reinstall the server once it is ready.

Set the server’s name. The request sends it and step 4 finds the server by it:

export VPS_NAME="app-01.example.com" # the host name you want
  • name is the server’s host name: 3 to 253 characters, with letters, digits, hyphens and dots. It is stored in lower case. Leave it out and Hostup picks a name for you.
  • cpuCores is 1 to 32. memoryGb is 1 to 256. storageGb is 50 to 2000. All three are required whole numbers.
  • region is required. Use an id from regions in step 1. Today the API does not check the value against that list. It does not change where the server is built.
  • Any other field gets 400.
POST /api/v2/vps/payg/deployments
curl -X POST "https://cloud.hostup.se/api/v2/vps/payg/deployments" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "'$VPS_NAME'",
"cpuCores": 2,
"memoryGb": 4,
"storageGb": 80,
"region": "stockholm"
}'
Response 201
{
"status": "provisioning",
"vpsId": "vps_01hxa3b4c5d6e7f8g9h0j1k2m3",
"orderId": "ord_01hxa3b4c5d6e7f8g9h0j1k2m3",
"invoiceId": "inv_01hxa3b4c5d6e7f8g9h0j1k2m3",
"deployment": {
"name": "app-01.example.com",
"region": "stockholm",
"resources": { "cpuCores": 2, "memoryGb": 4, "storageGb": 80 }
}
}

The call takes a few seconds. The build starts at once. It does not wait for a payment.

deployment.name is the name step 4 looks for. When you left name out, it is the name Hostup picked. Set VPS_NAME to it then. vpsId can be null. Then the server was not in the account yet when the call returned. Step 4 gets the ID either way.

The deployment gives you no job to follow. Follow the server in List VPS instances, filtered by its name. data is empty until the server shows up in the account. While the server is being built, serviceStatus is pending. It is ready when serviceStatus is active and primaryIp is set.

GET /api/v2/vps
curl "https://cloud.hostup.se/api/v2/vps?name=$VPS_NAME" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"data": [
{
"id": "vps_01hxa3b4c5d6e7f8g9h0j1k2m3",
"name": "app-01.example.com",
"primaryIp": "192.0.2.10",
"serviceStatus": "active",
"powerState": "running",
"…": "…"
}
],
"hasMore": false,
"nextCursor": null
}

The same as a loop, every 30 seconds:

GET /api/v2/vps
for attempt in $(seq 1 60); do
VPS=$(curl -s "https://cloud.hostup.se/api/v2/vps?name=$VPS_NAME" \
-H "Authorization: Bearer $HOSTUP_API_KEY" | jq '.data[0]')
echo "$VPS" | jq -r '[.id, .serviceStatus, .primaryIp] | @tsv'
[ "$(echo "$VPS" | jq -r '.serviceStatus')" = "active" ] && \
[ "$(echo "$VPS" | jq -r '.primaryIp')" != "null" ] && break
sleep 30
done

id is the server’s ID. Use it for every later call on this server.

export VPS_ID="vps_…" # the id the loop printed

The loop stops after 60 tries, about 30 minutes. If serviceStatus is still pending by then, contact Hostup support with the id.

The deploy installs no SSH key. Get VPS details and action gates has the login.

GET /api/v2/vps/{id}/details
curl "https://cloud.hostup.se/api/v2/vps/$VPS_ID/details" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"id": "vps_01hxa3b4c5d6e7f8g9h0j1k2m3",
"ipv4": "192.0.2.10",
"powerState": "running",
"operatingSystem": {
"displayName": "Ubuntu 24.04",
"family": "ubuntu",
"version": "24.04",
"variant": null
},
"sshKeys": [],
"access": {
"username": "root",
"rootPassword": "example-root-password"
},
"…": "…"
}

Log in as access.username with the password in access.rootPassword. operatingSystem shows the system the server got. Connect to the IP address:

To log in with a key instead, add a saved key with Change the SSH keys that can log in to a VPS.

Get a VPS cost breakdown estimates what this server costs in a whole calendar month (UTC) if it runs every hour. It counts the full month, also the hours before the server was built. It is not what has been charged.

GET /api/v2/vps/{id}/billing-breakdown
curl "https://cloud.hostup.se/api/v2/vps/$VPS_ID/billing-breakdown" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"available": true,
"kind": "payg_estimate",
"estimate": {
"basis": "max_24_7",
"currencyCode": "SEK",
"estimatedMonthlyAmount": 44.64,
"lineItems": [
{ "type": "cpu", "quantity": 2, "estimatedAmount": 21.6, "…": "…" },
{ "type": "memory", "quantity": 4, "estimatedAmount": 11.52, "…": "…" },
{ "type": "storage", "quantity": 80, "estimatedAmount": 11.52, "…": "…" },
{ "type": "ipv4", "quantity": 0, "estimatedAmount": 0, "…": "…" }
],
"…": "…"
},
"…": "…"
}

estimate.estimatedMonthlyAmount is the total. Each line item has its rate and estimatedAmount. The ipv4 line counts extra addresses only. Data transfer is not in the estimate.

A VPS with a fixed price can move to Cloud VPS billing without a new server. Check a switch to Cloud VPS (same IP, no restart) says whether it can. Switch a VPS to Cloud VPS billing makes the change at the end of the period you have paid for.

  • Money. The server has no price to pay up front. Its usage is metered from when it is built. Usage from all Cloud VPS on the account is billed together on one usage invoice each month. Break down a Cloud VPS usage invoice splits such an invoice per server. That includes servers you have deleted since. orderId and invoiceId from step 3 point to the order the deployment created.
  • Stopping and deleting. A stopped server costs no CPU or memory. Its disk is billed until you delete the server.
  • The e-mail. When the server is up, the account holder gets an e-mail that it is ready. It holds the server name, the operating system and the IP address. The password is not in it. Step 5 reads it through the API.

Next: Point a domain at your server

  • 403 not_verified, no_cloud_access, insufficient_trusted_history, no_card_on_file or country_not_supported: the account cannot deploy yet. Clear the blockers as step 1 shows. The limits in step 2 also answer 403 for an account without Cloud VPS access.
  • 403 payg_payment_overdue: a Cloud VPS invoice is overdue. New deployments are refused until every overdue Cloud VPS invoice is paid. The block lifts on its own after payment. Pay an invoice, then deploy again.
  • 400 invalid_request: a size is out of range, region is missing or the body has a field the API does not know. errors[].pointer names the field. A limit request also gets 400 when a request for the same limit is still waiting. It gets the same when requestedLimit is not higher than currentLimit. Those two answers have no errors[].pointer and only a general detail. Check pendingRequests and max in step 2 first.
  • 401 or 403 without one of the codes above: the key was refused. When a key is refused gives each cause and what to do.