API guide
Get started with the AtlasNodes API: authentication, request rules and copy-paste examples.
Base URL
Every endpoint uses HTTPS and JSON (UTF-8) under the base URL below. The API version is part of the path (v1); incompatible changes will ship under a new version.
https://app.atlasnodes.com/api/v1Every response carries an X-Request-Id header. Include its value when you contact support.
Authentication
Send the API key in the Authorization header with the Bearer scheme. Keys start with cvps_live_ and are shown only once when created — keep them in a secret manager, never in a repository.
curl -s "$CLOUDVPS_API/regions" \
-H "Authorization: Bearer $CLOUDVPS_API_KEY"An invalid, expired or revoked key → 401 unauthenticated. API keys only work on Customer API endpoints (catalog, wallet, billing, SSH keys, servers, invoices, account limits); other panel endpoints (account, sessions, API keys) → 403 forbidden.
Creating an API key
- Open API Keys → Create API key and give it a name that says who uses it (e.g. "terraform-prod").
- Expiry (optional, at most 2 years): the key is rejected after that date.
- Access: Full for automation that creates or changes VPS, or Read-only for monitoring and reporting. Read-only keys may only use
GET(and cannot retrieve root passwords); other requests →403 api_key_read_only. - Restrict IPs (optional): one IPv4/IPv6 address or CIDR per line, up to 20 (e.g.
203.0.113.10,198.51.100.0/24,2001:db8::/48). Empty = any IP. Requests from other IPs →403 api_key_ip_not_allowed;details.ipholds your IP as seen by AtlasNodes — the list itself is never returned. - Confirm with your account password (and 2FA code when enabled). The name, access level and IP list can be changed at any time with Edit; changes apply to the next request. Up to 10 active keys per account.
The account owner is notified when a key is used from an IP outside its list (at most once per hour per key).
Rate limits
The quota is counted per API key over a sliding 60-second window (60 requests/minute by default; the actual value is in X-RateLimit-Limit). Requests rejected because the endpoint is panel-only or the key is read-only count too; IP allowlist rejections are counted per caller IP, not per key.
- X-RateLimit-Limit
- Quota per minute.
- X-RateLimit-Remaining
- Quota left in the current window.
- X-RateLimit-Reset
- Time (epoch seconds) when one quota slot becomes available again.
When the quota runs out: 429 rate_limited with a Retry-After header and error.details.retry_after (seconds). Wait that long before retrying; use exponential backoff for other retries. Separately from the key quota, more than 30 failed authentications per minute from one IP (invalid key, suspended account, or IP outside the list; IPv6 counted per /64) also get 429 rate_limited with Retry-After, without X-RateLimit-* headers.
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1791181212
{"error":{"code":"rate_limited","message":"Too many requests. Try again later.","details":{"retry_after":12},"request_id":"0b7c9d2e4f614a3b8c5d6e7f8a9b0c1d"}}Idempotency-Key
Requests that create resources or charge your balance MUST send an Idempotency-Key header (missing → 428 idempotency_key_required):
POST /serversPOST /servers/{id}/reinstallPOST /servers/{id}/reset-passwordPOST /servers/{id}/resizePOST /servers/{id}/ip-addonsPOST /servers/{id}/volumesPATCH /servers/{id}/volumes/{volumeId}POST /invoices/{id}/pay
Other mutating requests (POST/DELETE) may send it; the same rules apply when they do.
- Use a unique value per operation (UUID v4 recommended), 1–255 ASCII characters without spaces. A key is valid for 24 hours per account and endpoint.
- Retrying with the same key AND body returns the stored response (same status,
Idempotent-Replayed: trueheader) without creating a second VPS or charging twice. - The same key with a different body →
422 idempotency_key_mismatch. The first request still running →409 idempotency_in_progress(retry a few seconds later). - Error responses are not stored: after an error (e.g.
422 insufficient_balance) you may retry with the same key once you have fixed the cause.
Error format
All errors share one format. code is stable (English) — use it in program logic; message follows the Accept-Language: id or en header. request_id equals the X-Request-Id header.
{
"error": {
"code": "api_key_ip_not_allowed",
"message": "Your IP address is not allowed to use this API key.",
"details": { "ip": "203.0.113.7" },
"request_id": "0b7c9d2e4f614a3b8c5d6e7f8a9b0c1d"
}
}| Status | Code | Meaning |
|---|---|---|
| 400 | validation_error | Invalid body/query; per-field details in details.fields. |
| 401 | unauthenticated | The API key is missing, invalid, expired or revoked. |
| 403 | forbidden | The endpoint cannot be used with an API key (panel only). |
| 403 | account_suspended | The account is suspended. |
| 403 | email_not_verified | Verify your email address before creating a VPS. |
| 403 | server_suspended | The VPS is suspended (e.g. an unpaid invoice). |
| 403 | api_key_ip_not_allowed | The caller IP is not on this key’s IP list (details.ip). |
| 403 | api_key_read_only | A read-only key was used for a non-GET request. |
| 404 | not_found | The resource does not exist or is not yours. |
| 409 | server_busy | Another action is still running for this VPS. |
| 409 | invalid_state | The action is not allowed in the VPS’s current state. |
| 409 | price_changed | The price changed since you saw it; fetch the latest price and resend. |
| 409 | idempotency_in_progress | See the Idempotency-Key section. |
| 422 | insufficient_balance | Insufficient balance (details.required, details.available). |
| 422 | limit_exceeded | An account limit was reached (e.g. number of VPS). |
| 422 | out_of_stock | The plan is out of stock in that region — check GET /catalog/availability and choose another region/plan. |
| 422 | plan_unavailable · region_unavailable · image_unavailable | The plan, region or image is not available. |
| 422 | idempotency_key_mismatch | See the Idempotency-Key section. |
| 428 | idempotency_key_required | See the Idempotency-Key section. |
| 429 | rate_limited | Quota exhausted; wait Retry-After seconds. |
| 503 | upstream_unavailable | The VPS infrastructure is temporarily unreachable; try again later. |
Money
Amounts are always 2-decimal strings with a currency (e.g. "6.00" + "USD"), never numbers. Hourly rates use 4–6 decimals (e.g. "0.0090"). Store and compute them as decimals, not floats.
Pagination
These list endpoints accept page (from 1) and per_page (default 20, max 100) and return meta:
GET /serversGET /servers/{id}/actionsGET /servers/{id}/resizesGET /wallet/transactionsGET /invoices
curl -s "$CLOUDVPS_API/servers?page=2&per_page=50&status=running" \
-H "Authorization: Bearer $CLOUDVPS_API_KEY"
# { "data": [ ... ], "meta": { "page": 2, "per_page": 50, "total": 73, "total_pages": 2 } }Catalog lists (/regions, /tiers, /plans, /images) and /ssh-keys are returned in full without meta.
Asynchronous actions
Creating a VPS, power actions, reinstall, resize, root password reset and delete run in the background: the 202 response contains an action object with status pending. Poll that action every 3–5 seconds until its status is completed, failed or cancelled.
A VPS runs one action at a time; a new action while another is running → 409 server_busy. The server’s pending_action_id field shows the running action.
curl examples
The examples use the shell variables below. Replace IDs with values from earlier responses.
export CLOUDVPS_API="https://app.atlasnodes.com/api/v1"
export CLOUDVPS_API_KEY="cvps_live_..." # dari halaman API Keys / from the API Keys pageCatalog: regions, server tiers, plans and OS images
curl -s "$CLOUDVPS_API/regions" -H "Authorization: Bearer $CLOUDVPS_API_KEY"
curl -s "$CLOUDVPS_API/tiers" -H "Authorization: Bearer $CLOUDVPS_API_KEY"
curl -s "$CLOUDVPS_API/plans" -H "Authorization: Bearer $CLOUDVPS_API_KEY"
curl -s "$CLOUDVPS_API/images?region_id=REGION_ID" -H "Authorization: Bearer $CLOUDVPS_API_KEY"
curl -s "$CLOUDVPS_API/catalog/availability" -H "Authorization: Bearer $CLOUDVPS_API_KEY"/images?region_id= only lists operating systems available in that region. /catalog/availability shows stock per region & plan (available; remaining only when a stock limit is set).
Create a VPS (Idempotency-Key required)
curl -s -X POST "$CLOUDVPS_API/servers" \
-H "Authorization: Bearer $CLOUDVPS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"hostname": "web-production",
"region_id": "REGION_ID",
"plan_id": "PLAN_ID",
"image_id": "IMAGE_ID",
"ssh_key_ids": ["SSH_KEY_ID"]
}'The 202 response contains data.server (status pending) and data.action. ssh_key_ids is optional (at most 1, from GET /ssh-keys). Keep the same Idempotency-Key if you need to retry this request.
Poll the action until it finishes
# SERVER_ID = data.server.id, ACTION_ID = data.action.id
while true; do
STATUS=$(curl -s "$CLOUDVPS_API/servers/SERVER_ID/actions/ACTION_ID" \
-H "Authorization: Bearer $CLOUDVPS_API_KEY" | jq -r '.data.status')
echo "$STATUS"
case "$STATUS" in completed|failed|cancelled) break ;; esac
sleep 5
doneRetrieve the root password (once)
curl -s "$CLOUDVPS_API/servers/SERVER_ID/initial-credentials" \
-H "Authorization: Bearer $CLOUDVPS_API_KEY"
# { "data": { "username": "root", "password": "...", "expires_at": "..." } }Available after the create/reinstall action completes and can be retrieved only once within 60 minutes. Requires a full-access key.
Console link (noVNC)
curl -s -X POST "$CLOUDVPS_API/servers/SERVER_ID/console-link" \
-H "Authorization: Bearer $CLOUDVPS_API_KEY"
# { "data": { "url": "https://…/console-link#…", "expires_at": "..." } }
# Buka data.url di tab baru dalam 60 detik (sekali pakai).A console page link for a running VPS, valid for 60 seconds and single-use — suitable for handing to reseller customers. Open it in a new tab (cannot be iframed). Requires a full-access key.
Power actions
# start | shutdown | stop | restart
curl -s -X POST "$CLOUDVPS_API/servers/SERVER_ID/restart" \
-H "Authorization: Bearer $CLOUDVPS_API_KEY"start powers on, shutdown shuts down gracefully (ACPI), stop forces power off, restart restarts. Empty body.
Reinstall the OS
curl -s "$CLOUDVPS_API/servers/SERVER_ID/images" -H "Authorization: Bearer $CLOUDVPS_API_KEY"
curl -s -X POST "$CLOUDVPS_API/servers/SERVER_ID/reinstall" \
-H "Authorization: Bearer $CLOUDVPS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "image_id": "IMAGE_ID" }'image_id must be one of GET /servers/{id}/images. All data on the disk is erased.
Change the plan (resize)
curl -s "$CLOUDVPS_API/servers/SERVER_ID/resize-options" \
-H "Authorization: Bearer $CLOUDVPS_API_KEY"
curl -s -X POST "$CLOUDVPS_API/servers/SERVER_ID/resize" \
-H "Authorization: Bearer $CLOUDVPS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"plan_id": "TARGET_PLAN_ID",
"expected_price_hourly": "0.0179",
"expected_monthly_cap": "12.00",
"accept_downtime": true
}'Only for VPS on hourly billing. Take the target plan’s price_hourly and monthly_cap from resize-options and send them as expected_price_hourly / expected_monthly_cap with accept_downtime: true. Disks are never shrunk.
Delete a VPS
curl -s -X DELETE "$CLOUDVPS_API/servers/SERVER_ID" \
-H "Authorization: Bearer $CLOUDVPS_API_KEY"202 response with a delete action. Deletion is permanent, including backups.
Wallet balance
curl -s "$CLOUDVPS_API/wallet" -H "Authorization: Bearer $CLOUDVPS_API_KEY"
# { "data": { "currency": "USD", "balance": "20.00", "available_balance": "20.00", ... } }balance, available_balance and other amounts are decimal strings in currency.
Full reference
Every endpoint, parameter, response schema and per-endpoint error code is in the interactive reference. The machine-readable spec (OpenAPI 3) can be used to generate client SDKs.
- Interactive reference (Swagger UI)
https://app.atlasnodes.com/api/docs - OpenAPI specification (JSON)
https://app.atlasnodes.com/api/v1/openapi.json - OpenAPI specification (YAML)
https://app.atlasnodes.com/api/docs-yaml