NexNodeReseller API v1.1.0

Quickstart

From zero to a server

From an API key to a running server in five requests. The examples read your key from NEXNODE_TOKEN.

Base URL https://api.nexnode.cloud/api/v1 https://api.nexnode.ir/api/v1Inside Iran
  1. 1Pick a plan
  2. 2Check the price
  3. 3Order it
  4. 4Wait for it
  5. 5Connect

Step 1/5

Pick a plan

List the plans you can order, filtered by country_code or datacenter_id. Add lang=en for English names.

From the plan you choose, keep its id, the id of one of its templates (the operating system) and the ids of any features you want.

curl -G "https://api.nexnode.cloud/api/v1/catalog/servers" \
  -d country_code=DE \
  -d lang=en \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os
import requests

BASE = "https://api.nexnode.cloud/api/v1"
api = requests.Session()
api.headers["X-API-Token"] = os.environ["NEXNODE_TOKEN"]

params = {"country_code": "DE", "lang": "en"}
response = api.get(f"{BASE}/catalog/servers", params=params)
plan = response.json()["data"][0]
<?php

const BASE = 'https://api.nexnode.cloud/api/v1';

function nexnode(
    string $method,
    string $path,
    ?array $body = null,
    array $headers = [],
): array {
    $options = [
        CURLOPT_CUSTOMREQUEST => $method,
        CURLOPT_RETURNTRANSFER => true,
    ];
    $headers[] = 'X-API-Token: ' . getenv('NEXNODE_TOKEN');
    if ($body !== null) {
        $headers[] = 'Content-Type: application/json';
        $options[CURLOPT_POSTFIELDS] = json_encode($body);
    }
    $options[CURLOPT_HTTPHEADER] = $headers;
    $ch = curl_init(BASE . $path);
    curl_setopt_array($ch, $options);
    return json_decode(curl_exec($ch), true);
}

$query = http_build_query(['country_code' => 'DE', 'lang' => 'en']);
$plan = nexnode('GET', "/catalog/servers?$query")['data'][0];
Response
{
  "success": true,
  "data": [
    {
      "id": 42,
      "name": "CX22",
      "cpu": 2,
      "memory": 4,
      "disk": 40,
      "location": {"id": 3, "name": "Falkenstein"},
      "prices": [
        {"term": "hourly", "price": 0.0097, "currency": "USD"}
      ],
      "templates": [
        {"id": 7, "name": "Ubuntu 24.04", "os_type": "linux"}
      ],
      "features": [
        {"id": 12, "name": "Backups", "price": 0.0015}
      ]
    }
  ]
}

Step 2/5

Check the price

Send your choice to POST /quote before ordering. cost is the first charge and sufficient_balance tells you whether the wallet covers it; when it does not, difference is what is missing.

Orders that are still building hold their price, shown as reserved, so two orders at once need room for both.

curl -X POST "https://api.nexnode.cloud/api/v1/quote" \
  -H "X-API-Token: $NEXNODE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "server_id": 42,
    "template_id": 7,
    "term": "hourly",
    "active_features": ["12"]
  }'
choice = {
    "server_id": plan["id"],
    "template_id": plan["templates"][0]["id"],
    "term": "hourly",
    "active_features": ["12"],
}
quote = api.post(f"{BASE}/quote", json=choice).json()["data"]
if not quote["sufficient_balance"]:
    raise SystemExit(f"Top up {quote['difference']} USD first")
$choice = [
    'server_id' => $plan['id'],
    'template_id' => $plan['templates'][0]['id'],
    'term' => 'hourly',
    'active_features' => ['12'],
];
$quote = nexnode('POST', '/quote', $choice)['data'];
if (!$quote['sufficient_balance']) {
    exit("Top up {$quote['difference']} USD first\n");
}
Response
{
  "success": true,
  "data": {
    "server_id": 42,
    "term": "hourly",
    "active_features": ["12"],
    "cost": 0.0112,
    "creation_fee": 0.0,
    "recurring": 0.0112,
    "balance": 8.42,
    "reserved": 0.0,
    "sufficient_balance": true,
    "difference": 0.0
  }
}

Step 3/5

Order it

POST /instances takes the same fields. Always send an Idempotency-Key: if the connection drops and you retry with the same key, you get the first order back instead of a second server.

Keep the order_id.

curl -X POST "https://api.nexnode.cloud/api/v1/instances" \
  -H "X-API-Token: $NEXNODE_TOKEN" \
  -H "Idempotency-Key: 5f0c7a52-3a1b-4c55-9d8e-2b7e4f1a9c03" \
  -H "Content-Type: application/json" \
  -d '{
    "server_id": 42,
    "template_id": 7,
    "term": "hourly",
    "active_features": ["12"]
  }'
import uuid

# Keep the key: a retry must send the same one.
key = str(uuid.uuid4())
headers = {"Idempotency-Key": key}
response = api.post(f"{BASE}/instances", json=choice, headers=headers)
order_id = response.json()["data"]["order_id"]
// Keep the key: a retry must send the same one.
$key = bin2hex(random_bytes(16));
$headers = ["Idempotency-Key: $key"];
$order = nexnode('POST', '/instances', $choice, $headers)['data'];
$orderId = $order['order_id'];
Response
{
  "success": true,
  "data": {"order_id": 9001, "price": 0.0112, "apps": []},
  "meta": {
    "note": "Poll GET /orders/{order_id} until status leaves pending"
  }
}

Step 4/5

Wait for it

A server takes from a few seconds to a few minutes to build, depending on the datacenter. Poll GET /orders/{order_id} every few seconds until status is no longer pending, or register a webhook and wait for order.deployed.

When the status is deployed, server.id is your instance id. An order that ends timed_out created nothing and charged nothing.

curl "https://api.nexnode.cloud/api/v1/orders/9001" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import time

while True:
    status = api.get(f"{BASE}/orders/{order_id}").json()["data"]
    if status["status"] != "pending":
        break
    time.sleep(5)

if status["status"] != "deployed":
    raise SystemExit("The order timed out; nothing was charged")
instance_id = status["server"]["id"]
while (true) {
    $status = nexnode('GET', "/orders/$orderId")['data'];
    if ($status['status'] !== 'pending') {
        break;
    }
    sleep(5);
}

if ($status['status'] !== 'deployed') {
    exit("The order timed out; nothing was charged\n");
}
$instanceId = $status['server']['id'];
Response
{
  "success": true,
  "data": {
    "order_id": 9001,
    "status": "deployed",
    "success": true,
    "created_at": "2026-09-16T10:00:00+00:00",
    "finished_at": "2026-09-16T10:01:12+00:00",
    "server": {
      "id": 123,
      "name": "Nex1-123",
      "status": "online",
      "ipv4": "203.0.113.7"
    }
  }
}
Webhook
{
  "id": "6f1c2a1e-4b7d-4c2e-9f3a-2d1b3c4d5e6f",
  "event": "order.deployed",
  "created_at": "2026-09-16T10:01:12+00:00",
  "data": {
    "order_id": 9001,
    "status": "deployed",
    "instance_id": 123,
    "server_id": 42,
    "price": 0.0112
  }
}

Step 5/5

Connect

GET /instances/{id} returns the address and the login. credentials.pending stays true until the datacenter has handed over the password and IP, which can take a few minutes after the order completes.

From here, POST /instances/{id}/actions powers the server on and off, reboots it or reinstalls the operating system, and DELETE /instances/{id} deletes it and stops its billing.

curl "https://api.nexnode.cloud/api/v1/instances/123" \
  -H "X-API-Token: $NEXNODE_TOKEN"
server = api.get(f"{BASE}/instances/{instance_id}").json()["data"]
login = server["credentials"]
print(f"ssh -p {login['port']} {login['username']}@{login['ipv4']}")
$server = nexnode('GET', "/instances/$instanceId")['data'];
$login = $server['credentials'];
echo "ssh -p {$login['port']} {$login['username']}@{$login['ipv4']}\n";
Response
{
  "success": true,
  "data": {
    "id": 123,
    "name": "Nex1-123",
    "status": "online",
    "ipv4": "203.0.113.7",
    "renewal_due": "2026-09-16T11:01:12+00:00",
    "credentials": {
      "username": "root",
      "password": "hX7kq2Lp9vQe",
      "port": 22,
      "ipv4": "203.0.113.7",
      "pending": false
    }
  }
}

Essentials

Authentication

NexNode support issues your key and shows it once. We keep only a hash of it, so a lost key cannot be recovered: ask support for a new one and the old one stops working at once.

Send the key on every request, in either header. GET /health is the only route that works without one, and GET /account shows the first characters of the key you are using.

The base URL is https://api.nexnode.cloud/api/v1. From inside Iran, https://api.nexnode.ir/api/v1 reaches the same API and the same account.

Headers
X-API-Token: sk_...
Authorization: Bearer sk_...

Try it in the browser

Every route in the reference has a Try it button. It sends the request from this page to the API with your key and shows the real response.

Your key stays in this browser tab and is forgotten when you close it. Reads run at once. Anything that costs money or changes a server asks you first, because there is no test mode: a server ordered here is a real server, paid from your wallet.

To work in Postman or Insomnia instead, import the OpenAPI document; every route, field and example comes with it.

Import into Postman or Insomnia
https://api.nexnode.cloud/openapi.json

Responses and errors

Every response has the same envelope. Branch on error.code, which is stable; error.message is for people and may change.

Every response carries an X-Request-ID header. Quote it when you contact support. You can also send your own, up to 64 characters of A-Za-z0-9._-, and it is echoed back.

A failure the datacenter caused (provider_error, deploy_rejected) also carries error.reference, a short code such as NX-7K3F2M; an order that ends timed_out carries the same kind of code as reference on the order and in its webhook. It is the code the bot and the panel show your customer and it finds the failure in our logs at once: pass it on to them, and quote it to support.

Anything that belongs to another account answers 404, never 403, so a response never tells you whether an id exists.

code status meaning
validation_error 400 the body or query is malformed; message names the field
unauthorized 401 missing or unknown key
insufficient_balance 402 top up the wallet; pending orders count against it
forbidden 403 banned account, restricted datacenter, or KYC required
not_found 404 unknown id, or one that belongs to another account
instance_locked 409 the instance is suspended: debt, traffic overage or cancelation
rate_limited 429 wait for Retry-After seconds
server_error 500 our fault; send us the request id
provider_error 502 the datacenter failed; try again later, and quote error.reference if it keeps failing
Success
{"success": true, "data": {"id": 123}, "meta": {}}
Error
{
  "success": false,
  "error": {
    "code": "not_found",
    "message": "Instance not found",
    "request_id": "7c9e6679a1b2"
  }
}

Duplicate orders

POST /instances creates a server and reserves money, so a retry after a dropped connection could buy two. Send an Idempotency-Key header with any unique string up to 128 characters; a UUID is fine.

A retry with the same key and the same body returns the original order with meta.idempotent_replay: true and creates nothing.

  • The same key with a different body is 422 idempotency_key_reused.
  • A retry while the first attempt is still running is 409 idempotency_in_progress.
  • Keys are kept for 24 hours.
  • Failed requests are not remembered, so you can fix the body or top up and retry with the same key.
Replayed order
{
  "success": true,
  "data": {"order_id": 9001, "price": 0.0112, "apps": []},
  "meta": {"idempotent_replay": true}
}

Lists and language

List routes return everything by default. To page, add limit (1 to 500) and optionally offset; the response then carries meta.page.

Names and descriptions are in Persian by default. Add lang=en or an Accept-Language: en header for English.

curl -G "https://api.nexnode.cloud/api/v1/instances" \
  -d limit=50 \
  -d offset=100 \
  -H "X-API-Token: $NEXNODE_TOKEN"
params = {"limit": 50, "offset": 100}
page = api.get(f"{BASE}/instances", params=params).json()
total = page["meta"]["page"]["total"]
$query = http_build_query(['limit' => 50, 'offset' => 100]);
$page = nexnode('GET', "/instances?$query");
$total = $page['meta']['page']['total'];
Response
{
  "success": true,
  "data": [],
  "meta": {"page": {"total": 128, "limit": 50, "offset": 100}}
}

Rate limits

Limits are per API key:

  • reads: 60 to 120 requests a minute per route
  • POST /instances: 10 a minute
  • instance actions: 30 a minute
  • webhook changes: 10 a minute

Past the limit you get 429 with a Retry-After header in seconds.

Response
HTTP/1.1 429 Too Many Requests
Retry-After: 18

{
  "success": false,
  "error": {"code": "rate_limited", "message": "Too many requests"}
}

Webhooks

Register one HTTPS URL and a secret with PUT /account/webhook instead of polling. Every event is a POST with these headers:

  • X-Nexnode-Event: the event name
  • X-Nexnode-Delivery: a UUID; retries reuse it, so deduplicate on it
  • X-Nexnode-Signature: t=<unix seconds>,v1=<hex>

Check the signature before anything else: v1 must equal HMAC_SHA256(secret, "<t>." + raw_body), computed over the raw body rather than re-encoded JSON, and t must be within a few minutes of now.

Answer with any 2xx within 15 seconds. A failed delivery is retried after 30 seconds and again after 5 minutes, then dropped. Deliveries come from our relay, not the API host, so trust the signature rather than the IP. Every event and its body is listed under Webhooks in the reference.

import hashlib
import hmac
import time


def verify(secret: str, header: str, body: bytes,
           tolerance: int = 300) -> bool:
    fields = dict(part.split("=", 1) for part in header.split(","))
    ts, given = int(fields["t"]), fields["v1"]
    if abs(time.time() - ts) > tolerance:
        return False
    signed = f"{ts}.".encode() + body
    digest = hmac.new(secret.encode(), signed, hashlib.sha256)
    return hmac.compare_digest(given, digest.hexdigest())
<?php

function verify(
    string $secret,
    string $header,
    string $body,
    int $tolerance = 300,
): bool {
    $fields = [];
    foreach (explode(',', $header) as $part) {
        [$name, $value] = explode('=', $part, 2) + [1 => ''];
        $fields[$name] = $value;
    }
    $ts = (int) ($fields['t'] ?? 0);
    if (abs(time() - $ts) > $tolerance) {
        return false;
    }
    $expected = hash_hmac('sha256', "$ts.$body", $secret);
    return hash_equals($expected, $fields['v1'] ?? '');
}

// In your endpoint: the raw body, not a re-encoded array.
$body = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_NEXNODE_SIGNATURE'] ?? '';
if (!verify(getenv('NEXNODE_WEBHOOK_SECRET'), $header, $body)) {
    http_response_code(401);
    exit;
}
Register the webhook
curl -X PUT "https://api.nexnode.cloud/api/v1/account/webhook" \
  -H "X-API-Token: $NEXNODE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.example.com/nexnode",
    "secret": "a-long-random-secret"
  }'

API reference

Every route, its fields and an example call.

Catalogue

Plans, datacenters, locations and one-click apps this account may order, and the exact price of a configuration. Prices are hourly USD.

Server catalog

GET/catalog/servers

Every orderable plan with hourly prices, templates, features and the actions its instances support. Filter by datacenter_id and country_code.

Query parameters

  • lang string or null
    fa (default) or en for names and descriptions. Accept-Language is honoured too.
  • limit integer or null
    Page size (1-500). Omit for everything.
  • offset integer default 0
  • datacenter_id integer or null
  • country_code string or null
    ISO country code filter.
  • term string or null
    Only hourly is accepted.
Response fields 30

data[]

  • id integer required
  • name string required
  • type string required
  • cpu integer required
  • memory integer required
    GB
  • disk integer required
    GB
  • term_filter string required
    Billing term the prices and features are for. Only hourly is offered.
  • prices array of objects required
  • creation_fee number required
    One-time fee added to the first charge; 0 when none.
  • templates array of objects required
  • features array of objects required
  • actions array of strings required
    Provider actions instances of this plan support.
  • datacenter object or null
  • datacenter.id integer required
  • datacenter.name string required
  • datacenter.description string or null
  • datacenter.status string or null
  • datacenter.enabled boolean
  • datacenter.terms array of objects
    Enabled billing terms.
  • datacenter.require_kyc boolean
  • location object or null
  • location.id integer required
  • location.datacenter_id integer required
  • location.name string required
  • location.country string required
  • location.country_code string required
  • location.flag string or null
  • location.active boolean
  • location.status string or null
  • traffic object or null

Errors400401429

curl "https://api.nexnode.cloud/api/v1/catalog/servers" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/catalog/servers",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/catalog/servers');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": [
    {
      "id": 123,
      "name": "Nex1-123",
      "type": "cloud",
      "cpu": 2,
      "memory": 4,
      "disk": 40,
      "term_filter": "hourly",
      "prices": [
        {}
      ],
      "creation_fee": 0.0112,
      "templates": [
        {}
      ],
      "features": [
        {}
      ],
      "actions": ["reboot"],
      "datacenter": {
        "id": 1,
        "name": "EU Central",
        "description": "Example DC",
        "status": "available",
        "enabled": true,
        "terms": [
          {}
        ],
        "require_kyc": false
      },
      "location": {
        "id": 3,
        "datacenter_id": 1,
        "name": "Nex1-123",
        "country": "Germany",
        "country_code": "DE",
        "flag": "🇩🇪",
        "active": true,
        "status": "online"
      },
      "traffic": {}
    }
  ]
}

One catalog server

GET/catalog/servers/{server_id}

Path parameters

  • server_id integer required

Query parameters

  • lang string or null
    fa (default) or en for names and descriptions. Accept-Language is honoured too.
Response fields 30

data

  • id integer required
  • name string required
  • type string required
  • cpu integer required
  • memory integer required
    GB
  • disk integer required
    GB
  • term_filter string required
    Billing term the prices and features are for. Only hourly is offered.
  • prices array of objects required
  • creation_fee number required
    One-time fee added to the first charge; 0 when none.
  • templates array of objects required
  • features array of objects required
  • actions array of strings required
    Provider actions instances of this plan support.
  • datacenter object or null
  • datacenter.id integer required
  • datacenter.name string required
  • datacenter.description string or null
  • datacenter.status string or null
  • datacenter.enabled boolean
  • datacenter.terms array of objects
    Enabled billing terms.
  • datacenter.require_kyc boolean
  • location object or null
  • location.id integer required
  • location.datacenter_id integer required
  • location.name string required
  • location.country string required
  • location.country_code string required
  • location.flag string or null
  • location.active boolean
  • location.status string or null
  • traffic object or null

Errors401404

curl "https://api.nexnode.cloud/api/v1/catalog/servers/42" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/catalog/servers/42",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/catalog/servers/42');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "id": 123,
    "name": "Nex1-123",
    "type": "cloud",
    "cpu": 2,
    "memory": 4,
    "disk": 40,
    "term_filter": "hourly",
    "prices": [
      {}
    ],
    "creation_fee": 0.0112,
    "templates": [
      {}
    ],
    "features": [
      {}
    ],
    "actions": ["reboot"],
    "datacenter": {
      "id": 1,
      "name": "EU Central",
      "description": "Example DC",
      "status": "available",
      "enabled": true,
      "terms": [
        {}
      ],
      "require_kyc": false
    },
    "location": {
      "id": 3,
      "datacenter_id": 1,
      "name": "Nex1-123",
      "country": "Germany",
      "country_code": "DE",
      "flag": "🇩🇪",
      "active": true,
      "status": "online"
    },
    "traffic": {}
  }
}

List datacenters

GET/datacenters

Datacenters the account may order from (account restrictions applied). One whose status is not available takes no orders for now.

Query parameters

  • lang string or null
    fa (default) or en for names and descriptions. Accept-Language is honoured too.
Response fields 7

data[]

  • id integer required
  • name string required
  • description string or null
  • status string or null
  • enabled boolean
  • terms array of objects
    Enabled billing terms.
  • require_kyc boolean

Errors401429

curl "https://api.nexnode.cloud/api/v1/datacenters" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/datacenters",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/datacenters');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": [
    {
      "id": 1,
      "name": "EU Central",
      "description": "Example DC",
      "status": "available",
      "enabled": true,
      "terms": [
        {}
      ],
      "require_kyc": false
    }
  ]
}

List locations

GET/locations

Optional filter: datacenter_id.

Query parameters

  • lang string or null
    fa (default) or en for names and descriptions. Accept-Language is honoured too.
  • datacenter_id integer or null
Response fields 8

data[]

  • id integer required
  • datacenter_id integer required
  • name string required
  • country string required
  • country_code string required
  • flag string or null
  • active boolean
  • status string or null

Errors401429

curl "https://api.nexnode.cloud/api/v1/locations" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/locations",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/locations');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": [
    {
      "id": 123,
      "datacenter_id": 1,
      "name": "Nex1-123",
      "country": "Germany",
      "country_code": "DE",
      "flag": "🇩🇪",
      "active": true,
      "status": "online"
    }
  ]
}

One-click apps

GET/catalog/apps

The whole catalogue, or with server_id and template_id only the apps that plan and template can run. Empty when apps are not open to this account.

Query parameters

  • lang string or null
    fa (default) or en for names and descriptions. Accept-Language is honoured too.
  • server_id integer or null
    With template_id: only apps that plan + template can run.
  • template_id integer or null
Response fields 17

data[]

  • id string required
  • name string required
  • category string required
  • description string or null required
  • requires array of strings required
  • conflicts array of strings required
  • ports array of strings required
  • min_memory_mb integer required
  • min_disk_gb integer required
  • os_families array of strings required
  • inputs array of objects required
  • inputs[].key string required
  • inputs[].label string required
  • inputs[].kind string required
  • inputs[].required boolean
  • inputs[].default string or null
  • inputs[].help string or null

Errors401404

curl "https://api.nexnode.cloud/api/v1/catalog/apps" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/catalog/apps",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/catalog/apps');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": [
    {
      "id": "n8n",
      "name": "Nex1-123",
      "category": "automation",
      "description": null,
      "requires": [],
      "conflicts": [],
      "ports": [],
      "min_memory_mb": 1,
      "min_disk_gb": 1,
      "os_families": [],
      "inputs": [
        {
          "key": "DOMAIN",
          "label": "Domain",
          "kind": "text",
          "required": false,
          "default": null,
          "help": null
        }
      ]
    }
  ]
}

Price quote

POST/quote

The exact first charge for a plan, features and template, and whether the wallet (minus reserved pending orders) covers it.

Body

  • server_id integer required
  • term string default hourly
  • template_id integer or null
    Apply the same forced-feature rules as a deploy (a Windows template always bills IPv4).
  • active_features array of strings
Response fields 10

data

  • server_id integer required
  • term string required
  • active_features array of strings required
    Echoed back; forced features may add ids you did not send.
  • cost number required
    First charge: one term plus the creation fee.
  • creation_fee number required
  • recurring number required
    Charged per term after the first.
  • balance number required
  • reserved number required
  • sufficient_balance boolean required
  • difference number required
    How much is missing when sufficient_balance is false.

Errors400401404

curl -X POST "https://api.nexnode.cloud/api/v1/quote" \
  -H "X-API-Token: $NEXNODE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "server_id": 42,
    "term": "hourly"
  }'
import os

import requests

response = requests.post(
    "https://api.nexnode.cloud/api/v1/quote",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
    json={
        "server_id": 42,
        "term": "hourly",
    },
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/quote');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'server_id' => 42,
        'term' => 'hourly',
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "server_id": 42,
    "term": "hourly",
    "active_features": [],
    "cost": 0.0112,
    "creation_fee": 0.0112,
    "recurring": 0.0112,
    "balance": 0.0112,
    "reserved": 0.0112,
    "sufficient_balance": true,
    "difference": 0.0112
  }
}

Instances

Servers you own: order one, read it with its credentials, rename it, run power and rebuild actions, resize it, delete it.

Create instance

POST/instances

Starts a deploy and returns an order id to poll. Send an Idempotency-Key header: a retry with the same key and body returns the original order instead of creating a second server.

Body

  • server_id integer required
    Catalog server id from GET /catalog/servers.
  • template_id integer required
    OS template id for the chosen server. Ignored when snapshot_id is set: the snapshot's OS is used.
  • term string required
    Billing term. Only hourly is currently supported.
  • snapshot_id integer or null
    Build the server from one of your snapshots instead of a template. The snapshot must be available and compatible with the plan (same datacenter, disk at least the snapshot's, same architecture, and the snapshot's location unless the datacenter's snapshot config has cross_location). apps cannot be combined with it.
  • active_features array of strings
    Feature ids to enable (features[].id from GET /catalog/servers). Features whose required_for_os_types includes the template's OS are enabled and billed even if omitted.
  • hostname string or null
    Optional hostname (RFC 1123 labels, max 63 chars). Generated when omitted.
  • apps array of strings
    One-click apps (ids from GET /catalog/apps) to install once the server is up. Dependencies are added automatically.
  • app_inputs object
    Per-app inputs, {app_id: {KEY: value}}. Every input is optional and has a documented default.
Response fields 3

data

  • order_id integer required
    Poll GET /orders/{order_id} until status leaves pending.
  • price number required
    What this order charges when it completes.
  • apps array of strings required
    Apps queued for install, in install order.

Errors400401402403404409422429502

curl -X POST "https://api.nexnode.cloud/api/v1/instances" \
  -H "X-API-Token: $NEXNODE_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "server_id": 42,
    "template_id": 7,
    "term": "hourly",
    "active_features": ["12", "14"],
    "apps": ["docker", "n8n"],
    "app_inputs": {
      "n8n": {
        "DOMAIN": "n8n.example.com"
      }
    }
  }'
import os
import uuid

import requests

response = requests.post(
    "https://api.nexnode.cloud/api/v1/instances",
    headers={
        "X-API-Token": os.environ["NEXNODE_TOKEN"],
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "server_id": 42,
        "template_id": 7,
        "term": "hourly",
        "active_features": ["12", "14"],
        "apps": ["docker", "n8n"],
        "app_inputs": {
            "n8n": {
                "DOMAIN": "n8n.example.com",
            },
        },
    },
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/instances');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
        'Idempotency-Key: ' . bin2hex(random_bytes(16)),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'server_id' => 42,
        'template_id' => 7,
        'term' => 'hourly',
        'active_features' => ['12', '14'],
        'apps' => ['docker', 'n8n'],
        'app_inputs' => [
            'n8n' => [
                'DOMAIN' => 'n8n.example.com',
            ],
        ],
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response201
{
  "success": true,
  "data": {
    "order_id": 9001,
    "price": 0.0112,
    "apps": []
  }
}

List instances

GET/instances

Active servers by default; only_active=false includes canceled and expired ones. Newest first.

Query parameters

  • limit integer or null
    Page size (1-500). Omit for everything.
  • offset integer default 0
  • only_active boolean default true
    False includes canceled and expired instances.
Response fields 22

data[]

  • id integer required
  • name string required
  • status string or null required
    online / offline / provider-specific; refreshed on GET /instances/{id}.
  • active boolean required
  • hostname string or null required
  • server_product_id integer required
  • ipv4 string or null
  • ipv6 string or null
  • term string or null
  • renewal_due string or null
  • renew boolean
  • locked boolean
  • lock_reason string or null
  • created_at string or null
  • expired_at string or null
  • datacenter_id integer or null
  • location_id integer or null
  • template_id integer or null
  • costs number or null
    Extra recurring costs (USD per term).
  • paid_amount number or null
    Total charged so far (USD).
  • actions array of strings
  • snapshots boolean
    Whether snapshots can be taken of this instance (/instances/{id}/snapshots).

Errors400401429

curl "https://api.nexnode.cloud/api/v1/instances" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/instances",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/instances');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": [
    {
      "id": 123,
      "name": "Nex1-123",
      "status": "online",
      "active": true,
      "hostname": "Nex1-123",
      "server_product_id": 42,
      "ipv4": "203.0.113.7",
      "ipv6": "2001:db8::7",
      "term": "hourly",
      "renewal_due": "2026-09-16T10:00:00+00:00",
      "renew": true,
      "locked": false,
      "lock_reason": null,
      "created_at": "2026-09-16T10:00:00+00:00",
      "expired_at": "2026-09-16T10:00:00+00:00",
      "datacenter_id": 1,
      "location_id": 3,
      "template_id": 7,
      "costs": 0.0112,
      "paid_amount": 0.0112,
      "actions": ["reboot"],
      "snapshots": false
    }
  ]
}

Get instance

GET/instances/{instance_id}

Full detail including credentials, traffic and queued apps. refresh=true asks the provider for the live status first.

Path parameters

  • instance_id integer required

Query parameters

  • refresh boolean default false
    Ask the provider for the current usage first (slower).
Response fields 57

data

  • id integer required
  • name string required
  • status string or null required
    online / offline / provider-specific; refreshed on GET /instances/{id}.
  • active boolean required
  • hostname string or null required
  • server_product_id integer required
  • ipv4 string or null
  • ipv6 string or null
  • term string or null
  • renewal_due string or null
  • renew boolean
  • locked boolean
  • lock_reason string or null
  • created_at string or null
  • expired_at string or null
  • datacenter_id integer or null
  • location_id integer or null
  • template_id integer or null
  • costs number or null
    Extra recurring costs (USD per term).
  • paid_amount number or null
    Total charged so far (USD).
  • actions array of strings
  • snapshots boolean
    Whether snapshots can be taken of this instance (/instances/{id}/snapshots).
  • server_product object or null
  • location object or null
  • location.id integer required
  • location.datacenter_id integer required
  • location.name string required
  • location.country string required
  • location.country_code string required
  • location.flag string or null
  • location.active boolean
  • location.status string or null
  • datacenter object or null
  • datacenter.id integer required
  • datacenter.name string required
  • datacenter.description string or null
  • datacenter.status string or null
  • datacenter.enabled boolean
  • datacenter.terms array of objects
    Enabled billing terms.
  • datacenter.require_kyc boolean
  • template object or null
  • traffic object or null
  • traffic.enabled boolean required
  • traffic.included integer required
    GB per 30 days; 0 means unlimited.
  • traffic.used integer required
  • traffic.overage integer required
  • traffic.allow_overage boolean required
  • traffic.cycle_start_date string or null required
  • credentials object or null
  • credentials.username string or null required
  • credentials.password string or null required
  • credentials.port integer or null required
    SSH or RDP port.
  • credentials.ipv4 string or null required
  • credentials.ipv6 string or null required
  • credentials.pending boolean required
    True while the provider has not delivered a password or an address yet.
  • apps array of objects
  • ip_change_cost number default 0.0
    USD charged for one change_ip on this instance; 0 where the change is free. Where it is set, the new address is a clean one, there is no wait between changes, and the action must carry accept_cost.

Errors401404429

curl "https://api.nexnode.cloud/api/v1/instances/123" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/instances/123",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/instances/123');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "id": 123,
    "name": "Nex1-123",
    "status": "online",
    "active": true,
    "hostname": "Nex1-123",
    "server_product_id": 42,
    "ipv4": "203.0.113.7",
    "ipv6": "2001:db8::7",
    "term": "hourly",
    "renewal_due": "2026-09-16T10:00:00+00:00",
    "renew": true,
    "locked": false,
    "lock_reason": null,
    "created_at": "2026-09-16T10:00:00+00:00",
    "expired_at": "2026-09-16T10:00:00+00:00",
    "datacenter_id": 1,
    "location_id": 3,
    "template_id": 7,
    "costs": 0.0112,
    "paid_amount": 0.0112,
    "actions": ["reboot"],
    "snapshots": false,
    "server_product": {},
    "location": {
      "id": 3,
      "datacenter_id": 1,
      "name": "Nex1-123",
      "country": "Germany",
      "country_code": "DE",
      "flag": "🇩🇪",
      "active": true,
      "status": "online"
    },
    "datacenter": {
      "id": 1,
      "name": "EU Central",
      "description": "Example DC",
      "status": "available",
      "enabled": true,
      "terms": [
        {}
      ],
      "require_kyc": false
    },
    "template": {},
    "traffic": {
      "enabled": true,
      "included": 2000,
      "used": 10,
      "overage": 0,
      "allow_overage": true,
      "cycle_start_date": "2026-09-16"
    },
    "credentials": {
      "username": "root",
      "password": "hX7kq2Lp9vQe",
      "port": 22,
      "ipv4": "203.0.113.7",
      "ipv6": "2001:db8::7",
      "pending": true
    },
    "apps": [
      {}
    ],
    "ip_change_cost": 0.0
  }
}

Update instance

PATCH/instances/{instance_id}

Rename, set a note, or switch auto-renew. Returns the updated instance.

Path parameters

  • instance_id integer required

Body

  • name string or null
    Display name (max 64 chars).
  • note string or null
    Free-form note (max 512 chars).
  • renew boolean or null
    Auto-renew. False lets the instance expire at renewal_due.
Response fields 57

data

  • id integer required
  • name string required
  • status string or null required
    online / offline / provider-specific; refreshed on GET /instances/{id}.
  • active boolean required
  • hostname string or null required
  • server_product_id integer required
  • ipv4 string or null
  • ipv6 string or null
  • term string or null
  • renewal_due string or null
  • renew boolean
  • locked boolean
  • lock_reason string or null
  • created_at string or null
  • expired_at string or null
  • datacenter_id integer or null
  • location_id integer or null
  • template_id integer or null
  • costs number or null
    Extra recurring costs (USD per term).
  • paid_amount number or null
    Total charged so far (USD).
  • actions array of strings
  • snapshots boolean
    Whether snapshots can be taken of this instance (/instances/{id}/snapshots).
  • server_product object or null
  • location object or null
  • location.id integer required
  • location.datacenter_id integer required
  • location.name string required
  • location.country string required
  • location.country_code string required
  • location.flag string or null
  • location.active boolean
  • location.status string or null
  • datacenter object or null
  • datacenter.id integer required
  • datacenter.name string required
  • datacenter.description string or null
  • datacenter.status string or null
  • datacenter.enabled boolean
  • datacenter.terms array of objects
    Enabled billing terms.
  • datacenter.require_kyc boolean
  • template object or null
  • traffic object or null
  • traffic.enabled boolean required
  • traffic.included integer required
    GB per 30 days; 0 means unlimited.
  • traffic.used integer required
  • traffic.overage integer required
  • traffic.allow_overage boolean required
  • traffic.cycle_start_date string or null required
  • credentials object or null
  • credentials.username string or null required
  • credentials.password string or null required
  • credentials.port integer or null required
    SSH or RDP port.
  • credentials.ipv4 string or null required
  • credentials.ipv6 string or null required
  • credentials.pending boolean required
    True while the provider has not delivered a password or an address yet.
  • apps array of objects
  • ip_change_cost number default 0.0
    USD charged for one change_ip on this instance; 0 where the change is free. Where it is set, the new address is a clean one, there is no wait between changes, and the action must carry accept_cost.

Errors400401404409

curl -X PATCH "https://api.nexnode.cloud/api/v1/instances/123" \
  -H "X-API-Token: $NEXNODE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "web-01",
    "note": "Customer 4411, production",
    "renew": true
  }'
import os

import requests

response = requests.patch(
    "https://api.nexnode.cloud/api/v1/instances/123",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
    json={
        "name": "web-01",
        "note": "Customer 4411, production",
        "renew": True,
    },
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/instances/123');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'name' => 'web-01',
        'note' => 'Customer 4411, production',
        'renew' => true,
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "id": 123,
    "name": "Nex1-123",
    "status": "online",
    "active": true,
    "hostname": "Nex1-123",
    "server_product_id": 42,
    "ipv4": "203.0.113.7",
    "ipv6": "2001:db8::7",
    "term": "hourly",
    "renewal_due": "2026-09-16T10:00:00+00:00",
    "renew": true,
    "locked": false,
    "lock_reason": null,
    "created_at": "2026-09-16T10:00:00+00:00",
    "expired_at": "2026-09-16T10:00:00+00:00",
    "datacenter_id": 1,
    "location_id": 3,
    "template_id": 7,
    "costs": 0.0112,
    "paid_amount": 0.0112,
    "actions": ["reboot"],
    "snapshots": false,
    "server_product": {},
    "location": {
      "id": 3,
      "datacenter_id": 1,
      "name": "Nex1-123",
      "country": "Germany",
      "country_code": "DE",
      "flag": "🇩🇪",
      "active": true,
      "status": "online"
    },
    "datacenter": {
      "id": 1,
      "name": "EU Central",
      "description": "Example DC",
      "status": "available",
      "enabled": true,
      "terms": [
        {}
      ],
      "require_kyc": false
    },
    "template": {},
    "traffic": {
      "enabled": true,
      "included": 2000,
      "used": 10,
      "overage": 0,
      "allow_overage": true,
      "cycle_start_date": "2026-09-16"
    },
    "credentials": {
      "username": "root",
      "password": "hX7kq2Lp9vQe",
      "port": 22,
      "ipv4": "203.0.113.7",
      "ipv6": "2001:db8::7",
      "pending": true
    },
    "apps": [
      {}
    ],
    "ip_change_cost": 0.0
  }
}

Instance actions

POST/instances/{instance_id}/actions

power_on, power_off, reboot, rebuild (template_id, optional apps), reset_password, change_ip, console. actions on the instance says which ones this plan supports. Where the instance's ip_change_cost is not 0, change_ip gives a clean address, is charged, and needs accept_cost.

Path parameters

  • instance_id integer required

Body

  • action string required
    One of: power_on, power_off, reboot, rebuild, reset_password, change_ip, console, resize, update_metadata.
  • template_id integer or null
    Required for rebuild.
  • server_id integer or null
    Required for resize: the target plan, one of GET /instances/{id}/resize-options.
  • accept_cost number or null
    change_ip on an instance whose ip_change_cost is not 0: the price you agree to, at least ip_change_cost. Without it the call answers 409 cost_unconfirmed and changes nothing.
  • apps array of strings
    rebuild only: apps to install on the new disk.
  • app_inputs object
    rebuild only.
  • name string or null
    update_metadata only (prefer PATCH).
  • note string or null
    update_metadata only (prefer PATCH).
Response fields 11

data

  • action string required
  • success boolean
  • password string or null
    reset_password: the new password.
  • ipv4 string or null
    change_ip: the new address.
  • clean boolean or null
    change_ip where it is paid for: whether the new address came from the clean pool. false means none was ready, the instance got an ordinary one and nothing was charged; the next free one waits out the window.
  • debt number or null
    change_ip: USD booked as debt because the wallet no longer covered the price once the address was changed.
  • console_url string or null
    console: short-lived URL.
  • console_password string or null
  • queued_apps array of strings or null
    rebuild: apps queued for the new disk.
  • charged number or null
    resize: USD charged now for the rest of the current term. change_ip where it is paid for: USD charged for the new address.
  • server_product_id integer or null
    resize: the plan the instance is on now.

Errors400401402404409429502503

curl -X POST "https://api.nexnode.cloud/api/v1/instances/123/actions" \
  -H "X-API-Token: $NEXNODE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "reboot"
  }'
import os

import requests

response = requests.post(
    "https://api.nexnode.cloud/api/v1/instances/123/actions",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
    json={
        "action": "reboot",
    },
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/instances/123/actions');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'action' => 'reboot',
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "action": "reboot",
    "success": true,
    "password": "hX7kq2Lp9vQe",
    "ipv4": "203.0.113.7",
    "clean": true,
    "debt": 1.5,
    "console_url": null,
    "console_password": null,
    "queued_apps": [],
    "charged": 0.0112,
    "server_product_id": 42
  }
}

Resize options

GET/instances/{instance_id}/resize-options

The plans this instance can move to (bigger in every dimension, same location), each with what it costs now and per term after. Empty when the plan cannot be resized.

Path parameters

  • instance_id integer required
Response fields 14

data

  • instance_id integer required
  • current_server_product_id integer required
  • term string required
  • options array of objects required
  • options[].id integer required
  • options[].name string required
  • options[].type string required
  • options[].cpu integer required
  • options[].memory number required
  • options[].disk integer required
  • options[].price number or null required
    Plan price on the instance's own billing term.
  • options[].term string required
  • options[].charge_now number required
    USD charged at once: the price difference prorated over what is left of the current term.
  • options[].new_cost number required
    What the instance will cost per term from the next renewal, features included.

Errors401404

curl "https://api.nexnode.cloud/api/v1/instances/123/resize-options" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/instances/123/resize-options",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/instances/123/resize-options');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "instance_id": 123,
    "current_server_product_id": 1,
    "term": "hourly",
    "options": [
      {
        "id": 123,
        "name": "Nex1-123",
        "type": "cloud",
        "cpu": 2,
        "memory": 1.5,
        "disk": 40,
        "price": 0.0112,
        "term": "hourly",
        "charge_now": 0.0112,
        "new_cost": 0.0112
      }
    ]
  }
}

Traffic usage

GET/instances/{instance_id}/traffic

Included, used and overage GB for the current cycle. refresh=true asks the provider for the current counter first.

Path parameters

  • instance_id integer required

Query parameters

  • refresh boolean default false
    Ask the provider for the current usage first (slower).
Response fields 6

data

  • enabled boolean required
  • included integer required
    GB per 30 days; 0 means unlimited.
  • used integer required
  • overage integer required
  • allow_overage boolean required
  • cycle_start_date string or null required

Errors401404

curl "https://api.nexnode.cloud/api/v1/instances/123/traffic" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/instances/123/traffic",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/instances/123/traffic');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "enabled": true,
    "included": 2000,
    "used": 10,
    "overage": 0,
    "allow_overage": true,
    "cycle_start_date": "2026-09-16"
  }
}

Delete instance

DELETE/instances/{instance_id}

Cancels the instance: billing stops and the provider-side deletion is queued. Returns once the instance is inactive. Idempotent.

Path parameters

  • instance_id integer required
Response fields 3

data

  • id integer required
  • status string required
    The instance is inactive and its deletion is queued at the provider.
  • active boolean

Errors401404429500

curl -X DELETE "https://api.nexnode.cloud/api/v1/instances/123" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.delete(
    "https://api.nexnode.cloud/api/v1/instances/123",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/instances/123');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "id": 123,
    "status": "canceled",
    "active": false
  }
}

Orders

Deploys in flight. An order becomes an instance when the datacenter finishes building it.

List orders

GET/orders

This account's API orders still pending or finished in the last 24 hours, newest first.

Response fields 64

data

  • order_id integer required
  • status string requiredpending, deployed, timed_out
  • success boolean required
    True only when status is deployed.
  • created_at string or null
  • finished_at string or null
  • server object or null
  • server.id integer required
  • server.name string required
  • server.status string or null required
    online / offline / provider-specific; refreshed on GET /instances/{id}.
  • server.active boolean required
  • server.hostname string or null required
  • server.server_product_id integer required
  • server.ipv4 string or null
  • server.ipv6 string or null
  • server.term string or null
  • server.renewal_due string or null
  • server.renew boolean
  • server.locked boolean
  • server.lock_reason string or null
  • server.created_at string or null
  • server.expired_at string or null
  • server.datacenter_id integer or null
  • server.location_id integer or null
  • server.template_id integer or null
  • server.costs number or null
    Extra recurring costs (USD per term).
  • server.paid_amount number or null
    Total charged so far (USD).
  • server.actions array of strings
  • server.snapshots boolean
    Whether snapshots can be taken of this instance (/instances/{id}/snapshots).
  • server.server_product object or null
  • server.location object or null
  • server.location.id integer required
  • server.location.datacenter_id integer required
  • server.location.name string required
  • server.location.country string required
  • server.location.country_code string required
  • server.location.flag string or null
  • server.location.active boolean
  • server.location.status string or null
  • server.datacenter object or null
  • server.datacenter.id integer required
  • server.datacenter.name string required
  • server.datacenter.description string or null
  • server.datacenter.status string or null
  • server.datacenter.enabled boolean
  • server.datacenter.terms array of objects
    Enabled billing terms.
  • server.datacenter.require_kyc boolean
  • server.template object or null
  • server.traffic object or null
  • server.traffic.enabled boolean required
  • server.traffic.included integer required
    GB per 30 days; 0 means unlimited.
  • server.traffic.used integer required
  • server.traffic.overage integer required
  • server.traffic.allow_overage boolean required
  • server.traffic.cycle_start_date string or null required
  • server.credentials object or null
  • server.credentials.username string or null required
  • server.credentials.password string or null required
  • server.credentials.port integer or null required
    SSH or RDP port.
  • server.credentials.ipv4 string or null required
  • server.credentials.ipv6 string or null required
  • server.credentials.pending boolean required
    True while the provider has not delivered a password or an address yet.
  • server.apps array of objects
  • server.ip_change_cost number default 0.0
    USD charged for one change_ip on this instance; 0 where the change is free. Where it is set, the new address is a clean one, there is no wait between changes, and the action must carry accept_cost.
  • reference string or null
    On timed_out: the support reference to quote; the same code the customer sees in Telegram.

Errors401

curl "https://api.nexnode.cloud/api/v1/orders" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/orders",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/orders');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "order_id": 9001,
    "status": "pending",
    "success": true,
    "created_at": "2026-09-16T10:00:00+00:00",
    "finished_at": "2026-09-16T10:00:00+00:00",
    "server": {
      "id": 42,
      "name": "Nex1-123",
      "status": "online",
      "active": true,
      "hostname": "Nex1-123",
      "server_product_id": 42,
      "ipv4": "203.0.113.7",
      "ipv6": "2001:db8::7",
      "term": "hourly",
      "renewal_due": "2026-09-16T10:00:00+00:00",
      "renew": true,
      "locked": false,
      "lock_reason": null,
      "created_at": "2026-09-16T10:00:00+00:00",
      "expired_at": "2026-09-16T10:00:00+00:00",
      "datacenter_id": 1,
      "location_id": 3,
      "template_id": 7,
      "costs": 0.0112,
      "paid_amount": 0.0112,
      "actions": ["reboot"],
      "snapshots": false,
      "server_product": {},
      "location": {
        "id": 3,
        "datacenter_id": 1,
        "name": "Nex1-123",
        "country": "Germany",
        "country_code": "DE",
        "flag": "🇩🇪",
        "active": true,
        "status": "online"
      },
      "datacenter": {
        "id": 1,
        "name": "EU Central",
        "description": "Example DC",
        "status": "available",
        "enabled": true,
        "terms": [
          {}
        ],
        "require_kyc": false
      },
      "template": {},
      "traffic": {
        "enabled": true,
        "included": 2000,
        "used": 10,
        "overage": 0,
        "allow_overage": true,
        "cycle_start_date": "2026-09-16"
      },
      "credentials": {
        "username": "root",
        "password": "hX7kq2Lp9vQe",
        "port": 22,
        "ipv4": "203.0.113.7",
        "ipv6": "2001:db8::7",
        "pending": true
      },
      "apps": [
        {}
      ],
      "ip_change_cost": 0.0
    },
    "reference": null
  }
}

Order status

GET/orders/{order_id}

Poll while status is pending; when deployed, server is the full instance. Terminal orders stay readable for 24 hours, then 404.

Path parameters

  • order_id integer required
Response fields 64

data

  • order_id integer required
  • status string requiredpending, deployed, timed_out
  • success boolean required
    True only when status is deployed.
  • created_at string or null
  • finished_at string or null
  • server object or null
  • server.id integer required
  • server.name string required
  • server.status string or null required
    online / offline / provider-specific; refreshed on GET /instances/{id}.
  • server.active boolean required
  • server.hostname string or null required
  • server.server_product_id integer required
  • server.ipv4 string or null
  • server.ipv6 string or null
  • server.term string or null
  • server.renewal_due string or null
  • server.renew boolean
  • server.locked boolean
  • server.lock_reason string or null
  • server.created_at string or null
  • server.expired_at string or null
  • server.datacenter_id integer or null
  • server.location_id integer or null
  • server.template_id integer or null
  • server.costs number or null
    Extra recurring costs (USD per term).
  • server.paid_amount number or null
    Total charged so far (USD).
  • server.actions array of strings
  • server.snapshots boolean
    Whether snapshots can be taken of this instance (/instances/{id}/snapshots).
  • server.server_product object or null
  • server.location object or null
  • server.location.id integer required
  • server.location.datacenter_id integer required
  • server.location.name string required
  • server.location.country string required
  • server.location.country_code string required
  • server.location.flag string or null
  • server.location.active boolean
  • server.location.status string or null
  • server.datacenter object or null
  • server.datacenter.id integer required
  • server.datacenter.name string required
  • server.datacenter.description string or null
  • server.datacenter.status string or null
  • server.datacenter.enabled boolean
  • server.datacenter.terms array of objects
    Enabled billing terms.
  • server.datacenter.require_kyc boolean
  • server.template object or null
  • server.traffic object or null
  • server.traffic.enabled boolean required
  • server.traffic.included integer required
    GB per 30 days; 0 means unlimited.
  • server.traffic.used integer required
  • server.traffic.overage integer required
  • server.traffic.allow_overage boolean required
  • server.traffic.cycle_start_date string or null required
  • server.credentials object or null
  • server.credentials.username string or null required
  • server.credentials.password string or null required
  • server.credentials.port integer or null required
    SSH or RDP port.
  • server.credentials.ipv4 string or null required
  • server.credentials.ipv6 string or null required
  • server.credentials.pending boolean required
    True while the provider has not delivered a password or an address yet.
  • server.apps array of objects
  • server.ip_change_cost number default 0.0
    USD charged for one change_ip on this instance; 0 where the change is free. Where it is set, the new address is a clean one, there is no wait between changes, and the action must carry accept_cost.
  • reference string or null
    On timed_out: the support reference to quote; the same code the customer sees in Telegram.

Errors401404429

curl "https://api.nexnode.cloud/api/v1/orders/9001" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/orders/9001",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/orders/9001');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "order_id": 9001,
    "status": "pending",
    "success": true,
    "created_at": "2026-09-16T10:00:00+00:00",
    "finished_at": "2026-09-16T10:00:00+00:00",
    "server": {
      "id": 42,
      "name": "Nex1-123",
      "status": "online",
      "active": true,
      "hostname": "Nex1-123",
      "server_product_id": 42,
      "ipv4": "203.0.113.7",
      "ipv6": "2001:db8::7",
      "term": "hourly",
      "renewal_due": "2026-09-16T10:00:00+00:00",
      "renew": true,
      "locked": false,
      "lock_reason": null,
      "created_at": "2026-09-16T10:00:00+00:00",
      "expired_at": "2026-09-16T10:00:00+00:00",
      "datacenter_id": 1,
      "location_id": 3,
      "template_id": 7,
      "costs": 0.0112,
      "paid_amount": 0.0112,
      "actions": ["reboot"],
      "snapshots": false,
      "server_product": {},
      "location": {
        "id": 3,
        "datacenter_id": 1,
        "name": "Nex1-123",
        "country": "Germany",
        "country_code": "DE",
        "flag": "🇩🇪",
        "active": true,
        "status": "online"
      },
      "datacenter": {
        "id": 1,
        "name": "EU Central",
        "description": "Example DC",
        "status": "available",
        "enabled": true,
        "terms": [
          {}
        ],
        "require_kyc": false
      },
      "template": {},
      "traffic": {
        "enabled": true,
        "included": 2000,
        "used": 10,
        "overage": 0,
        "allow_overage": true,
        "cycle_start_date": "2026-09-16"
      },
      "credentials": {
        "username": "root",
        "password": "hX7kq2Lp9vQe",
        "port": 22,
        "ipv4": "203.0.113.7",
        "ipv6": "2001:db8::7",
        "pending": true
      },
      "apps": [
        {}
      ],
      "ip_change_cost": 0.0
    },
    "reference": null
  }
}

Apps

One-click applications installed over SSH after the server is up.

App installs

GET/instances/{instance_id}/apps

Every queued, running, finished or failed one-click install on the instance, newest first, with its stage and log tail.

Path parameters

  • instance_id integer required
Response fields 13

data

  • server_id integer required
  • supported boolean required
  • installs array of objects required
  • installs[].app_id string required
  • installs[].name string required
  • installs[].status string required
    queued / installing / installed / failed
  • installs[].stage string required
    waiting / preparing / running / done / failed / auth_failed
  • installs[].attempts integer required
  • installs[].result object required
    What the install reported (URLs, generated credentials).
  • installs[].error string required
  • installs[].log_tail string required
    Tail of the install log; the only place a failure explains itself.
  • installs[].started_at string or null required
  • installs[].finished_at string or null required

Errors401404

curl "https://api.nexnode.cloud/api/v1/instances/123/apps" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/instances/123/apps",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/instances/123/apps');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "server_id": 42,
    "supported": true,
    "installs": [
      {
        "app_id": "n8n",
        "name": "Nex1-123",
        "status": "online",
        "stage": "done",
        "attempts": 1,
        "result": {},
        "error": "The server did not accept SSH",
        "log_tail": "n8n is listening on 5678",
        "started_at": "2026-09-16T10:00:00+00:00",
        "finished_at": "2026-09-16T10:00:00+00:00"
      }
    ]
  }
}

Install apps

POST/instances/{instance_id}/apps

Queue one-click apps on a running instance. The stored root password must still be valid; a changed one fails the install with auth_failed (see GET /instances/{id}/apps).

Path parameters

  • instance_id integer required

Body

  • apps array of strings required
    App ids to install; dependencies are added automatically.
  • app_inputs object
    Per-app inputs, {app_id: {KEY: value}}. Every input is optional.
Response fields 2

data

  • server_id integer required
  • queued array of strings required

Errors400401403404409503

curl -X POST "https://api.nexnode.cloud/api/v1/instances/123/apps" \
  -H "X-API-Token: $NEXNODE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "apps": ["n8n"],
    "app_inputs": {
      "n8n": {
        "DOMAIN": "n8n.example.com"
      }
    }
  }'
import os

import requests

response = requests.post(
    "https://api.nexnode.cloud/api/v1/instances/123/apps",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
    json={
        "apps": ["n8n"],
        "app_inputs": {
            "n8n": {
                "DOMAIN": "n8n.example.com",
            },
        },
    },
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/instances/123/apps');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'apps' => ['n8n'],
        'app_inputs' => [
            'n8n' => [
                'DOMAIN' => 'n8n.example.com',
            ],
        ],
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response202
{
  "success": true,
  "data": {
    "server_id": 42,
    "queued": []
  }
}

Snapshots

Point-in-time images of an instance, billed hourly by size: restore in place, delete, or order new instances from one (POST /instances with snapshot_id).

Create snapshot

POST/instances/{instance_id}/snapshots

Starts a snapshot of the instance; the server stays online. Returns the snapshot in creating; poll GET /snapshots/{id} until it is available. The first term is charged then, from the real size.

Path parameters

  • instance_id integer required

Body

  • name string or null
    Display name (max 64 chars). Defaults to the instance name and the time.
Response fields 26

data

  • id integer required
  • name string required
  • status string required
    creating, available, restoring, failed, expired or deleted.
  • note string or null
  • locked boolean
  • lock_reason string or null
    unpaid when the balance was short at renewal; the snapshot cannot be used until it is paid.
  • size_gb number or null
  • price number or null
    USD per hour, fixed from the real size when the snapshot became available. Accrued every hour and charged to the wallet once the accrued amount reaches a tenth of a cent.
  • price_monthly number or null
    The hourly rate over a month, for display.
  • term string default hourly
  • accrued number or null
    USD accrued on the snapshot and not yet charged.
  • renewal_due string or null
    Start of the next hour to be accounted.
  • created_at string or null
  • available_at string or null
  • expired_at string or null
  • instance_id integer or null
    The instance it was taken from; null once that instance is gone.
  • datacenter_id integer or null
  • location_id integer or null
  • template_id integer or null
  • server_product_id integer or null
  • os_type string or null
  • source_name string or null
  • features array of strings
    Names of the features the instance had when the snapshot was taken (its IPv4 add-on, a licence, an extra disk). A clone carries them where its plan offers them, unless the request names its own active_features.
  • local boolean
    True when the snapshot lives on the instance's own disk (a restore point that is deleted with the instance).
  • restore boolean
    Whether POST /snapshots/{id}/restore is available on this datacenter. False where only cloning is possible.
  • paid_amount number or null

Errors400401402403404409502

curl -X POST "https://api.nexnode.cloud/api/v1/instances/123/snapshots" \
  -H "X-API-Token: $NEXNODE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "before-upgrade"
  }'
import os

import requests

response = requests.post(
    "https://api.nexnode.cloud/api/v1/instances/123/snapshots",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
    json={
        "name": "before-upgrade",
    },
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/instances/123/snapshots');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'name' => 'before-upgrade',
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response202
{
  "success": true,
  "data": {
    "id": 123,
    "name": "Nex1-123",
    "status": "online",
    "note": null,
    "locked": false,
    "lock_reason": null,
    "size_gb": 12.5,
    "price": 0.0112,
    "price_monthly": 0.0112,
    "term": "hourly",
    "accrued": 0.0112,
    "renewal_due": "2026-09-16T10:00:00+00:00",
    "created_at": "2026-09-16T10:00:00+00:00",
    "available_at": "2026-09-16T10:00:00+00:00",
    "expired_at": "2026-09-16T10:00:00+00:00",
    "instance_id": 123,
    "datacenter_id": 1,
    "location_id": 3,
    "template_id": 7,
    "server_product_id": 42,
    "os_type": "linux",
    "source_name": null,
    "features": [],
    "local": false,
    "restore": true,
    "paid_amount": 0.0112
  }
}

Instance snapshots

GET/instances/{instance_id}/snapshots

The snapshots taken from this instance. meta.settings carries the datacenter's price per GB, term length and per-instance cap.

Path parameters

  • instance_id integer required
Response fields 26

data[]

  • id integer required
  • name string required
  • status string required
    creating, available, restoring, failed, expired or deleted.
  • note string or null
  • locked boolean
  • lock_reason string or null
    unpaid when the balance was short at renewal; the snapshot cannot be used until it is paid.
  • size_gb number or null
  • price number or null
    USD per hour, fixed from the real size when the snapshot became available. Accrued every hour and charged to the wallet once the accrued amount reaches a tenth of a cent.
  • price_monthly number or null
    The hourly rate over a month, for display.
  • term string default hourly
  • accrued number or null
    USD accrued on the snapshot and not yet charged.
  • renewal_due string or null
    Start of the next hour to be accounted.
  • created_at string or null
  • available_at string or null
  • expired_at string or null
  • instance_id integer or null
    The instance it was taken from; null once that instance is gone.
  • datacenter_id integer or null
  • location_id integer or null
  • template_id integer or null
  • server_product_id integer or null
  • os_type string or null
  • source_name string or null
  • features array of strings
    Names of the features the instance had when the snapshot was taken (its IPv4 add-on, a licence, an extra disk). A clone carries them where its plan offers them, unless the request names its own active_features.
  • local boolean
    True when the snapshot lives on the instance's own disk (a restore point that is deleted with the instance).
  • restore boolean
    Whether POST /snapshots/{id}/restore is available on this datacenter. False where only cloning is possible.
  • paid_amount number or null

Errors401403404

curl "https://api.nexnode.cloud/api/v1/instances/123/snapshots" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/instances/123/snapshots",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/instances/123/snapshots');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": [
    {
      "id": 123,
      "name": "Nex1-123",
      "status": "online",
      "note": null,
      "locked": false,
      "lock_reason": null,
      "size_gb": 12.5,
      "price": 0.0112,
      "price_monthly": 0.0112,
      "term": "hourly",
      "accrued": 0.0112,
      "renewal_due": "2026-09-16T10:00:00+00:00",
      "created_at": "2026-09-16T10:00:00+00:00",
      "available_at": "2026-09-16T10:00:00+00:00",
      "expired_at": "2026-09-16T10:00:00+00:00",
      "instance_id": 123,
      "datacenter_id": 1,
      "location_id": 3,
      "template_id": 7,
      "server_product_id": 42,
      "os_type": "linux",
      "source_name": null,
      "features": [],
      "local": false,
      "restore": true,
      "paid_amount": 0.0112
    }
  ]
}

List snapshots

GET/snapshots

Every snapshot you own, newest first, including those whose instance is already gone (they can still be cloned).

Query parameters

  • include_inactive boolean default false
    Also list deleted, expired and failed snapshots.
Response fields 26

data[]

  • id integer required
  • name string required
  • status string required
    creating, available, restoring, failed, expired or deleted.
  • note string or null
  • locked boolean
  • lock_reason string or null
    unpaid when the balance was short at renewal; the snapshot cannot be used until it is paid.
  • size_gb number or null
  • price number or null
    USD per hour, fixed from the real size when the snapshot became available. Accrued every hour and charged to the wallet once the accrued amount reaches a tenth of a cent.
  • price_monthly number or null
    The hourly rate over a month, for display.
  • term string default hourly
  • accrued number or null
    USD accrued on the snapshot and not yet charged.
  • renewal_due string or null
    Start of the next hour to be accounted.
  • created_at string or null
  • available_at string or null
  • expired_at string or null
  • instance_id integer or null
    The instance it was taken from; null once that instance is gone.
  • datacenter_id integer or null
  • location_id integer or null
  • template_id integer or null
  • server_product_id integer or null
  • os_type string or null
  • source_name string or null
  • features array of strings
    Names of the features the instance had when the snapshot was taken (its IPv4 add-on, a licence, an extra disk). A clone carries them where its plan offers them, unless the request names its own active_features.
  • local boolean
    True when the snapshot lives on the instance's own disk (a restore point that is deleted with the instance).
  • restore boolean
    Whether POST /snapshots/{id}/restore is available on this datacenter. False where only cloning is possible.
  • paid_amount number or null

Errors401403

curl "https://api.nexnode.cloud/api/v1/snapshots" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/snapshots",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/snapshots');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": [
    {
      "id": 123,
      "name": "Nex1-123",
      "status": "online",
      "note": null,
      "locked": false,
      "lock_reason": null,
      "size_gb": 12.5,
      "price": 0.0112,
      "price_monthly": 0.0112,
      "term": "hourly",
      "accrued": 0.0112,
      "renewal_due": "2026-09-16T10:00:00+00:00",
      "created_at": "2026-09-16T10:00:00+00:00",
      "available_at": "2026-09-16T10:00:00+00:00",
      "expired_at": "2026-09-16T10:00:00+00:00",
      "instance_id": 123,
      "datacenter_id": 1,
      "location_id": 3,
      "template_id": 7,
      "server_product_id": 42,
      "os_type": "linux",
      "source_name": null,
      "features": [],
      "local": false,
      "restore": true,
      "paid_amount": 0.0112
    }
  ]
}

Snapshot detail

GET/snapshots/{snapshot_id}

Path parameters

  • snapshot_id integer required
Response fields 26

data

  • id integer required
  • name string required
  • status string required
    creating, available, restoring, failed, expired or deleted.
  • note string or null
  • locked boolean
  • lock_reason string or null
    unpaid when the balance was short at renewal; the snapshot cannot be used until it is paid.
  • size_gb number or null
  • price number or null
    USD per hour, fixed from the real size when the snapshot became available. Accrued every hour and charged to the wallet once the accrued amount reaches a tenth of a cent.
  • price_monthly number or null
    The hourly rate over a month, for display.
  • term string default hourly
  • accrued number or null
    USD accrued on the snapshot and not yet charged.
  • renewal_due string or null
    Start of the next hour to be accounted.
  • created_at string or null
  • available_at string or null
  • expired_at string or null
  • instance_id integer or null
    The instance it was taken from; null once that instance is gone.
  • datacenter_id integer or null
  • location_id integer or null
  • template_id integer or null
  • server_product_id integer or null
  • os_type string or null
  • source_name string or null
  • features array of strings
    Names of the features the instance had when the snapshot was taken (its IPv4 add-on, a licence, an extra disk). A clone carries them where its plan offers them, unless the request names its own active_features.
  • local boolean
    True when the snapshot lives on the instance's own disk (a restore point that is deleted with the instance).
  • restore boolean
    Whether POST /snapshots/{id}/restore is available on this datacenter. False where only cloning is possible.
  • paid_amount number or null

Errors401403404

curl "https://api.nexnode.cloud/api/v1/snapshots/15" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/snapshots/15",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/snapshots/15');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "id": 123,
    "name": "Nex1-123",
    "status": "online",
    "note": null,
    "locked": false,
    "lock_reason": null,
    "size_gb": 12.5,
    "price": 0.0112,
    "price_monthly": 0.0112,
    "term": "hourly",
    "accrued": 0.0112,
    "renewal_due": "2026-09-16T10:00:00+00:00",
    "created_at": "2026-09-16T10:00:00+00:00",
    "available_at": "2026-09-16T10:00:00+00:00",
    "expired_at": "2026-09-16T10:00:00+00:00",
    "instance_id": 123,
    "datacenter_id": 1,
    "location_id": 3,
    "template_id": 7,
    "server_product_id": 42,
    "os_type": "linux",
    "source_name": null,
    "features": [],
    "local": false,
    "restore": true,
    "paid_amount": 0.0112
  }
}

Update snapshot

PATCH/snapshots/{snapshot_id}

Rename or set a note.

Path parameters

  • snapshot_id integer required

Body

  • name string or null
    Display name (max 64 chars).
  • note string or null
    Free-form note (max 512 chars). Empty string clears it.
Response fields 26

data

  • id integer required
  • name string required
  • status string required
    creating, available, restoring, failed, expired or deleted.
  • note string or null
  • locked boolean
  • lock_reason string or null
    unpaid when the balance was short at renewal; the snapshot cannot be used until it is paid.
  • size_gb number or null
  • price number or null
    USD per hour, fixed from the real size when the snapshot became available. Accrued every hour and charged to the wallet once the accrued amount reaches a tenth of a cent.
  • price_monthly number or null
    The hourly rate over a month, for display.
  • term string default hourly
  • accrued number or null
    USD accrued on the snapshot and not yet charged.
  • renewal_due string or null
    Start of the next hour to be accounted.
  • created_at string or null
  • available_at string or null
  • expired_at string or null
  • instance_id integer or null
    The instance it was taken from; null once that instance is gone.
  • datacenter_id integer or null
  • location_id integer or null
  • template_id integer or null
  • server_product_id integer or null
  • os_type string or null
  • source_name string or null
  • features array of strings
    Names of the features the instance had when the snapshot was taken (its IPv4 add-on, a licence, an extra disk). A clone carries them where its plan offers them, unless the request names its own active_features.
  • local boolean
    True when the snapshot lives on the instance's own disk (a restore point that is deleted with the instance).
  • restore boolean
    Whether POST /snapshots/{id}/restore is available on this datacenter. False where only cloning is possible.
  • paid_amount number or null

Errors400401403404

curl -X PATCH "https://api.nexnode.cloud/api/v1/snapshots/15" \
  -H "X-API-Token: $NEXNODE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "before-upgrade",
    "note": "Taken before the 2.0 release"
  }'
import os

import requests

response = requests.patch(
    "https://api.nexnode.cloud/api/v1/snapshots/15",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
    json={
        "name": "before-upgrade",
        "note": "Taken before the 2.0 release",
    },
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/snapshots/15');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'name' => 'before-upgrade',
        'note' => 'Taken before the 2.0 release',
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "id": 123,
    "name": "Nex1-123",
    "status": "online",
    "note": null,
    "locked": false,
    "lock_reason": null,
    "size_gb": 12.5,
    "price": 0.0112,
    "price_monthly": 0.0112,
    "term": "hourly",
    "accrued": 0.0112,
    "renewal_due": "2026-09-16T10:00:00+00:00",
    "created_at": "2026-09-16T10:00:00+00:00",
    "available_at": "2026-09-16T10:00:00+00:00",
    "expired_at": "2026-09-16T10:00:00+00:00",
    "instance_id": 123,
    "datacenter_id": 1,
    "location_id": 3,
    "template_id": 7,
    "server_product_id": 42,
    "os_type": "linux",
    "source_name": null,
    "features": [],
    "local": false,
    "restore": true,
    "paid_amount": 0.0112
  }
}

Restore snapshot

POST/snapshots/{snapshot_id}/restore

Puts the snapshot back onto the instance it came from, in place. The instance is powered off during the restore and keeps its addresses; its disk and credentials become what the snapshot holds. Anything changed since the snapshot is lost.

Path parameters

  • snapshot_id integer required
Response fields 26

data

  • id integer required
  • name string required
  • status string required
    creating, available, restoring, failed, expired or deleted.
  • note string or null
  • locked boolean
  • lock_reason string or null
    unpaid when the balance was short at renewal; the snapshot cannot be used until it is paid.
  • size_gb number or null
  • price number or null
    USD per hour, fixed from the real size when the snapshot became available. Accrued every hour and charged to the wallet once the accrued amount reaches a tenth of a cent.
  • price_monthly number or null
    The hourly rate over a month, for display.
  • term string default hourly
  • accrued number or null
    USD accrued on the snapshot and not yet charged.
  • renewal_due string or null
    Start of the next hour to be accounted.
  • created_at string or null
  • available_at string or null
  • expired_at string or null
  • instance_id integer or null
    The instance it was taken from; null once that instance is gone.
  • datacenter_id integer or null
  • location_id integer or null
  • template_id integer or null
  • server_product_id integer or null
  • os_type string or null
  • source_name string or null
  • features array of strings
    Names of the features the instance had when the snapshot was taken (its IPv4 add-on, a licence, an extra disk). A clone carries them where its plan offers them, unless the request names its own active_features.
  • local boolean
    True when the snapshot lives on the instance's own disk (a restore point that is deleted with the instance).
  • restore boolean
    Whether POST /snapshots/{id}/restore is available on this datacenter. False where only cloning is possible.
  • paid_amount number or null

Errors401403404409502

curl -X POST "https://api.nexnode.cloud/api/v1/snapshots/15/restore" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.post(
    "https://api.nexnode.cloud/api/v1/snapshots/15/restore",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/snapshots/15/restore');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "id": 123,
    "name": "Nex1-123",
    "status": "online",
    "note": null,
    "locked": false,
    "lock_reason": null,
    "size_gb": 12.5,
    "price": 0.0112,
    "price_monthly": 0.0112,
    "term": "hourly",
    "accrued": 0.0112,
    "renewal_due": "2026-09-16T10:00:00+00:00",
    "created_at": "2026-09-16T10:00:00+00:00",
    "available_at": "2026-09-16T10:00:00+00:00",
    "expired_at": "2026-09-16T10:00:00+00:00",
    "instance_id": 123,
    "datacenter_id": 1,
    "location_id": 3,
    "template_id": 7,
    "server_product_id": 42,
    "os_type": "linux",
    "source_name": null,
    "features": [],
    "local": false,
    "restore": true,
    "paid_amount": 0.0112
  }
}

Delete snapshot

DELETE/snapshots/{snapshot_id}

Deletes it at the provider and stops its billing. Not refunded.

Path parameters

  • snapshot_id integer required
Response fields 26

data

  • id integer required
  • name string required
  • status string required
    creating, available, restoring, failed, expired or deleted.
  • note string or null
  • locked boolean
  • lock_reason string or null
    unpaid when the balance was short at renewal; the snapshot cannot be used until it is paid.
  • size_gb number or null
  • price number or null
    USD per hour, fixed from the real size when the snapshot became available. Accrued every hour and charged to the wallet once the accrued amount reaches a tenth of a cent.
  • price_monthly number or null
    The hourly rate over a month, for display.
  • term string default hourly
  • accrued number or null
    USD accrued on the snapshot and not yet charged.
  • renewal_due string or null
    Start of the next hour to be accounted.
  • created_at string or null
  • available_at string or null
  • expired_at string or null
  • instance_id integer or null
    The instance it was taken from; null once that instance is gone.
  • datacenter_id integer or null
  • location_id integer or null
  • template_id integer or null
  • server_product_id integer or null
  • os_type string or null
  • source_name string or null
  • features array of strings
    Names of the features the instance had when the snapshot was taken (its IPv4 add-on, a licence, an extra disk). A clone carries them where its plan offers them, unless the request names its own active_features.
  • local boolean
    True when the snapshot lives on the instance's own disk (a restore point that is deleted with the instance).
  • restore boolean
    Whether POST /snapshots/{id}/restore is available on this datacenter. False where only cloning is possible.
  • paid_amount number or null

Errors401403404409502

curl -X DELETE "https://api.nexnode.cloud/api/v1/snapshots/15" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.delete(
    "https://api.nexnode.cloud/api/v1/snapshots/15",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/snapshots/15');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "id": 123,
    "name": "Nex1-123",
    "status": "online",
    "note": null,
    "locked": false,
    "lock_reason": null,
    "size_gb": 12.5,
    "price": 0.0112,
    "price_monthly": 0.0112,
    "term": "hourly",
    "accrued": 0.0112,
    "renewal_due": "2026-09-16T10:00:00+00:00",
    "created_at": "2026-09-16T10:00:00+00:00",
    "available_at": "2026-09-16T10:00:00+00:00",
    "expired_at": "2026-09-16T10:00:00+00:00",
    "instance_id": 123,
    "datacenter_id": 1,
    "location_id": 3,
    "template_id": 7,
    "server_product_id": 42,
    "os_type": "linux",
    "source_name": null,
    "features": [],
    "local": false,
    "restore": true,
    "paid_amount": 0.0112
  }
}

Webhooks

One signed HTTPS endpoint per account, and the events it receives. How to check the signature is under Webhooks in the guide.

Register a webhook

PUT/account/webhook

One HTTPS URL per account. Deliveries are signed with secret; see the docs for the signature scheme. Replaces any previous registration.

Body

  • url string required
    HTTPS endpoint that receives every event for this account.
  • secret string required
    16 to 256 characters; used to sign deliveries (HMAC-SHA256). Never returned.
Response fields 3

data

  • url string or null required
  • configured boolean required
  • events array of strings required
    Event names this API can send.

Errors400401

curl -X PUT "https://api.nexnode.cloud/api/v1/account/webhook" \
  -H "X-API-Token: $NEXNODE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/nexnode/webhook",
    "secret": "a-long-random-string-you-generate"
  }'
import os

import requests

response = requests.put(
    "https://api.nexnode.cloud/api/v1/account/webhook",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
    json={
        "url": "https://example.com/nexnode/webhook",
        "secret": "a-long-random-string-you-generate",
    },
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/account/webhook');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'url' => 'https://example.com/nexnode/webhook',
        'secret' => 'a-long-random-string-you-generate',
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "url": "https://hooks.example.com/nexnode",
    "configured": true,
    "events": ["order.deployed"]
  }
}

Webhook settings

GET/account/webhook

The registered URL (the secret is never returned) and the event names.

Response fields 3

data

  • url string or null required
  • configured boolean required
  • events array of strings required
    Event names this API can send.

Errors401

curl "https://api.nexnode.cloud/api/v1/account/webhook" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/account/webhook",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/account/webhook');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "url": "https://hooks.example.com/nexnode",
    "configured": true,
    "events": ["order.deployed"]
  }
}

Send a test event

POST/account/webhook/test

Delivered once, without retries, and the endpoint's HTTP status is reported back.

Response fields 2

data

  • delivered boolean required
  • status integer or null required
    HTTP status the endpoint answered with, if it answered.

Errors400401503

curl -X POST "https://api.nexnode.cloud/api/v1/account/webhook/test" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.post(
    "https://api.nexnode.cloud/api/v1/account/webhook/test",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/account/webhook/test');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "delivered": true,
    "status": 1
  }
}

Remove the webhook

DELETE/account/webhook

Response fields 3

data

  • url string or null required
  • configured boolean required
  • events array of strings required
    Event names this API can send.

Errors401

curl -X DELETE "https://api.nexnode.cloud/api/v1/account/webhook" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.delete(
    "https://api.nexnode.cloud/api/v1/account/webhook",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/account/webhook');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "url": "https://hooks.example.com/nexnode",
    "configured": true,
    "events": ["order.deployed"]
  }
}

Events

Every delivery is a POST of {"id", "event", "created_at", "data"}, signed as described under Webhooks. The fields below are the ones inside data.

ping

Sent by POST /account/webhook/test.

data

  • account_id integer required
Payload
{
  "id": "6f1c2a1e-4b7d-4c2e-9f3a-2d1b3c4d5e6f",
  "event": "ping",
  "created_at": "2026-09-10T10:00:00+00:00",
  "data": {
    "account_id": 123456
  }
}
order.deployed

A POST /instances order finished and the instance exists. instance_id is the id for every /instances call.

data

  • order_id integer required
  • status string required
  • instance_id integer required
  • server_id integer required
    Catalogue plan id.
  • price number required
    What the order charged (USD).
Payload
{
  "id": "6f1c2a1e-4b7d-4c2e-9f3a-2d1b3c4d5e6f",
  "event": "order.deployed",
  "created_at": "2026-09-10T10:00:00+00:00",
  "data": {
    "order_id": 9001,
    "status": "deployed",
    "instance_id": 123,
    "server_id": 42,
    "price": 0.0097
  }
}
order.timed_out

The provider did not finish within the order window; nothing was created or charged.

data

  • order_id integer required
  • status string required
  • instance_id null required
  • server_id integer required
  • price number required
Payload
{
  "id": "6f1c2a1e-4b7d-4c2e-9f3a-2d1b3c4d5e6f",
  "event": "order.timed_out",
  "created_at": "2026-09-10T10:00:00+00:00",
  "data": {
    "order_id": 9002,
    "status": "timed_out",
    "instance_id": null,
    "server_id": 42,
    "price": 0.0097
  }
}
instance.canceled

The instance was canceled. reason is api for your own DELETE, system for expiry, unpaid debt, abuse or an admin.

data

  • instance_id integer required
  • status string required
    Why, as stored on the instance.
  • reason string requiredapi, system
Payload
{
  "id": "6f1c2a1e-4b7d-4c2e-9f3a-2d1b3c4d5e6f",
  "event": "instance.canceled",
  "created_at": "2026-09-10T10:00:00+00:00",
  "data": {
    "instance_id": 123,
    "status": "Expired",
    "reason": "system"
  }
}
instance.password_reset

The root password was reset through the API. Read it from GET /instances/{id}; it is not in the webhook.

data

  • instance_id integer required
Payload
{
  "id": "6f1c2a1e-4b7d-4c2e-9f3a-2d1b3c4d5e6f",
  "event": "instance.password_reset",
  "created_at": "2026-09-10T10:00:00+00:00",
  "data": {
    "instance_id": 123
  }
}
instance.ip_changed

The IPv4 address was changed through the API.

data

  • instance_id integer required
  • ipv4 string required
Payload
{
  "id": "6f1c2a1e-4b7d-4c2e-9f3a-2d1b3c4d5e6f",
  "event": "instance.ip_changed",
  "created_at": "2026-09-10T10:00:00+00:00",
  "data": {
    "instance_id": 123,
    "ipv4": "203.0.113.7"
  }
}
instance.resized

The instance was moved to another plan through the API. charged is what was taken now for the rest of the current term.

data

  • instance_id integer required
  • server_id integer required
    The new catalog server id.
  • charged number required
Payload
{
  "id": "6f1c2a1e-4b7d-4c2e-9f3a-2d1b3c4d5e6f",
  "event": "instance.resized",
  "created_at": "2026-09-10T10:00:00+00:00",
  "data": {
    "instance_id": 123,
    "server_id": 43,
    "charged": 0.005
  }
}
snapshot.created

A snapshot was requested through the API. It starts in creating; poll GET /snapshots/{id} until it is available.

data

  • snapshot_id integer required
  • instance_id integer required
Payload
{
  "id": "6f1c2a1e-4b7d-4c2e-9f3a-2d1b3c4d5e6f",
  "event": "snapshot.created",
  "created_at": "2026-09-10T10:00:00+00:00",
  "data": {
    "snapshot_id": 15,
    "instance_id": 123
  }
}
snapshot.restored

A snapshot was restored onto its instance through the API.

data

  • snapshot_id integer required
  • instance_id integer required
Payload
{
  "id": "6f1c2a1e-4b7d-4c2e-9f3a-2d1b3c4d5e6f",
  "event": "snapshot.restored",
  "created_at": "2026-09-10T10:00:00+00:00",
  "data": {
    "snapshot_id": 15,
    "instance_id": 123
  }
}
snapshot.deleted

A snapshot was deleted through the API.

data

  • snapshot_id integer required
Payload
{
  "id": "6f1c2a1e-4b7d-4c2e-9f3a-2d1b3c4d5e6f",
  "event": "snapshot.deleted",
  "created_at": "2026-09-10T10:00:00+00:00",
  "data": {
    "snapshot_id": 15
  }
}
balance.low

Sent at 24, 12, 6, 2 and 1 hours before renewals exhaust the wallet.

data

  • balance number required
  • hours_until_balance_zero number required
Payload
{
  "id": "6f1c2a1e-4b7d-4c2e-9f3a-2d1b3c4d5e6f",
  "event": "balance.low",
  "created_at": "2026-09-10T10:00:00+00:00",
  "data": {
    "balance": 0.31,
    "hours_until_balance_zero": 11.5
  }
}

Account

Who the key belongs to, wallet balance, reserved funds and payments.

Account

GET/account

The account behind the key, the key's hint and issue date, and the limits that apply. The key itself is never returned.

Response fields 7

data

  • id integer required
    Account id.
  • api_key_hint string or null required
    First characters of the key in use; the key itself is never returned.
  • api_key_created_at string or null required
  • webhook_configured boolean required
  • server_limit integer required
    Maximum active instances.
  • api_order_limit integer required
    Maximum orders provisioning at once.
  • language string required

Errors401

curl "https://api.nexnode.cloud/api/v1/account" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/account",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/account');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "id": 123,
    "api_key_hint": "sk_Ab12Cd34",
    "api_key_created_at": "2026-09-16T10:00:00+00:00",
    "webhook_configured": true,
    "server_limit": 1,
    "api_order_limit": 1,
    "language": "fa"
  }
}

Account balance

GET/account/balance

Wallet balance, debt, money reserved by pending orders, and the estimated hours until renewals exhaust the balance.

Response fields 7

data

  • balance number required
    Current wallet balance (USD).
  • debt number required
    Outstanding debt (USD).
  • reserved number required
    Sum of prices of this account's orders still provisioning; counted against the balance for new orders.
  • available number required
    balance - reserved: what a new order can draw on.
  • hours_until_balance_zero number or null
    Estimated hours until renewal charges exhaust the balance (same simulation as the bot); null if there are no renewable servers or the time cannot be estimated.
  • active_instances integer default 0
  • pending_orders integer default 0

Errors401429

curl "https://api.nexnode.cloud/api/v1/account/balance" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/account/balance",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/account/balance');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "balance": 100.5,
    "debt": 0.0,
    "reserved": 4.5,
    "available": 96.0,
    "hours_until_balance_zero": 48.25,
    "active_instances": 3,
    "pending_orders": 1
  }
}

Payments

GET/account/payments

Top-ups credited to the wallet, newest first.

Query parameters

  • limit integer or null
    Page size (1-500). Omit for everything.
  • offset integer default 0
Response fields 4

data[]

  • id integer required
  • amount number required
    Credited amount (USD).
  • payment_type string or null required
  • paid_at string or null required
    ISO 8601 timestamp.

Errors401429

curl "https://api.nexnode.cloud/api/v1/account/payments" \
  -H "X-API-Token: $NEXNODE_TOKEN"
import os

import requests

response = requests.get(
    "https://api.nexnode.cloud/api/v1/account/payments",
    headers={"X-API-Token": os.environ["NEXNODE_TOKEN"]},
)
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/account/payments');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => [
        'X-API-Token: ' . getenv('NEXNODE_TOKEN'),
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": [
    {
      "id": 123,
      "amount": 0.0112,
      "payment_type": "crypto",
      "paid_at": "2026-09-16T10:00:00+00:00"
    }
  ]
}

System

Health check. No authentication.

Health check

GET/health

No authentication. Same JSON envelope as other routes.

Response fields 2

data

  • status string required
  • api string required

Errors429

curl "https://api.nexnode.cloud/api/v1/health"
import requests

response = requests.get("https://api.nexnode.cloud/api/v1/health")
data = response.json()["data"]
<?php

$ch = curl_init('https://api.nexnode.cloud/api/v1/health');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true)['data'];
Response200
{
  "success": true,
  "data": {
    "status": "ok",
    "api": "reseller_v1"
  }
}