Hosting APIEverything the configurator does, over HTTP.

Bearer tokens with scopes you choose, idempotency on every write, cursor pagination, and a status endpoint that needs no key at all — because the moment you most need to poll it is the moment your account might be the thing that is broken.

endpoints13

Quickstart

No SDK required · No versioning surprises · No rate limit you will meet

Quickstart

A server, from nothing, in two calls.

Create a key in the client area, choose its scopes, and it is shown once. There is no master key on this account and no way to widen a key after the fact — make a new one instead.

1 — see what you can buy

curl -s https://api.dediprivacy.com/v1/plans?family=vps \
  -H "Authorization: Bearer $DP_KEY"

2 — deploy it

curl -s -X POST https://api.dediprivacy.com/v1/servers \
  -H "Authorization: Bearer $DP_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
        "plan":   "vps-4",
        "region": "ams",
        "image":  "debian-13",
        "cycle":  "12"
      }'

Provisioning draws on the account balance. A call that would overdraw it fails with 402 and the exact shortfall rather than building half a machine — there is no partially provisioned state to clean up, and no invoice arrives later.

Conventions

The parts that are the same on every call, decided once so you never have to look them up twice.

Base URL
https://api.dediprivacy.com/v1 TLS only, HTTP/2, and no unencrypted port to redirect from. A plaintext request is refused rather than upgraded, because an upgrade means the request already crossed the network once.
Authentication
Bearer token, per key Keys are created in the client area with the scopes you choose, shown once, and stored hashed. There is no master key and no password-based authentication on this API.
Content type
application/json On requests and responses. Times are RFC 3339 in UTC, money is an integer count of cents, and there are no locale-dependent formats anywhere.
Idempotency
Idempotency-Key on every POST A repeated key returns the original response rather than provisioning twice. Keys are remembered for 24 hours, which is longer than any retry loop should run.
Rate limit
600 requests a minute Per key, returned in X-RateLimit-Remaining. Provisioning calls are limited separately at 60 an hour; ask if that is genuinely in your way.
Pagination
Cursor, not page number A next_cursor in every list response. Offset pagination silently skips rows when the underlying set changes and we would rather not ship that bug to you.

Identifiers

Read from the same catalogue the public price lists and the configurator are built from. A region added to the estate appears here without anybody editing this page.

region

  • kefReykjavík
  • otpBucharest
  • sofSofia
  • kivChișinău
  • zrhZurich
  • amsAmsterdam
  • ptyPanama City
  • sinSingapore

family

  • vpsCloud VPS
  • rdpWindows RDP
  • dedicatedDedicated Servers
  • gpuGPU Compute
  • private-cloudPrivate Cloud
  • web-hostingWeb Hosting
  • storageObject & Block Storage
  • colocationColocation
  • customCustom build

cycle

  • 11 month
  • 33 months, −5 %
  • 1212 months, −20 %
  • 2424 months, −35 %

Endpoints

Catalogue

Everything that can be ordered, with live pricing. No authentication needed — these are the same figures the public price lists are built from.

GET /regions List regions

Every region with its identifier, city, country and current operational state.

Response

{
  "data": [
    { "id": "ams", "city": "Amsterdam", "country": "Netherlands",
      "status": "operational", "uplink_gbit": 600 }
  ]
}
GET /plans List plans

Filter by family with ?family=vps. Prices are per month in cents, before any cycle discount.

Response

{
  "data": [
    { "id": "vps-4", "family": "vps", "cores": 4, "ram_gb": 8,
      "disk_gb": 160, "price_cents": 1100, "regions": ["ams","kef"] }
  ],
  "next_cursor": null
}
GET /images List images

Operating system images available for a family, with the licence position of each.

Response

{
  "data": [
    { "id": "debian-13", "name": "Debian 13", "licence": "included" },
    { "id": "win-2025", "name": "Windows Server 2025", "licence": "included" }
  ]
}

Endpoints

Servers

Create, inspect and control machines. Provisioning draws on the account balance; a request that would overdraw it fails with 402 and the exact shortfall rather than half-building something.

POST /servers Deploy a server

Returns immediately with status "provisioning". Poll the resource or take a webhook.

Request

{
  "plan": "vps-4",
  "region": "ams",
  "image": "debian-13",
  "cycle": "12",
  "label": "edge-01",
  "ssh_keys": ["ssh-ed25519 AAAA..."]
}

Response

{
  "id": "srv_8Kq2",
  "status": "provisioning",
  "region": "ams",
  "ipv4": null,
  "charged_cents": 10560,
  "renews_at": "2027-07-28T00:00:00Z"
}
GET /servers List servers

Everything on the account, newest first.

Response

{
  "data": [
    { "id": "srv_8Kq2", "label": "edge-01", "status": "active",
      "region": "ams", "ipv4": "203.0.113.10",
      "ipv6": "2001:db8:1234:5678::2" }
  ],
  "next_cursor": null
}
GET /servers/{id} Retrieve a server

Full detail including specification, addresses and the next renewal.

Response

{
  "id": "srv_8Kq2",
  "status": "active",
  "plan": "vps-4",
  "image": "debian-13",
  "cycle": "12",
  "renews_at": "2027-07-28T00:00:00Z"
}
POST /servers/{id}/actions Act on a server

One endpoint, an action field: reboot, shutdown, start, rebuild, resize, rdns.

Request

{
  "action": "rebuild",
  "image": "ubuntu-26-04"
}

Response

{
  "id": "act_3Xf9",
  "action": "rebuild",
  "status": "running"
}
DELETE /servers/{id} Cancel a server

Runs to the end of the term already paid for. Pass ?immediate=true to destroy it now and stop paying from today.

Response

{
  "id": "srv_8Kq2",
  "status": "cancelled",
  "ends_at": "2027-07-28T00:00:00Z"
}

Endpoints

Billing

One balance funds everything. There is no invoice object because there is nothing to chase — a service draws from the balance on its renewal date.

GET /balance Retrieve the balance

Current balance, what is committed to renewals, and how long that lasts at the present burn.

Response

{
  "balance_cents": 48250,
  "committed_cents": 12400,
  "runway_days": 117
}
POST /topups Open a top-up

Returns a payment address for the chosen asset. Any bonus is applied at the moment the payment confirms.

Request

{
  "amount_cents": 100000,
  "asset": "XMR"
}

Response

{
  "id": "top_5Wc1",
  "asset": "XMR",
  "address": "4A...",
  "amount": "3.417",
  "bonus_cents": 30000,
  "expires_at": "2026-07-28T18:00:00Z"
}
GET /ledger List ledger entries

Every credit and debit, with the service each one relates to. This is the whole of what we hold about your payments.

Response

{
  "data": [
    { "at": "2026-07-28T16:04:00Z", "cents": -1100,
      "kind": "renewal", "ref": "srv_8Kq2" }
  ]
}

Endpoints

Status

Public and unauthenticated, so it can be polled by something that is not holding your key — and so it still answers if your account is the thing that is broken.

GET /status Current state

The same figures as the status page, computed from the same incident record.

Response

{
  "state": "operational",
  "open_incidents": 0,
  "uptime_90d": 99.9971,
  "regions": { "ams": "operational", "kef": "operational" }
}
GET /incidents List incidents

The record itself, filterable by region and date.

Response

{
  "data": [
    { "id": "inc_71", "region": "otp", "severity": "partial",
      "started_at": "2026-06-07T02:14:00Z",
      "unreachable_seconds": 1080 }
  ]
}

Errors

Every failure carries a stable code and a message written for a person. Match on the code; the message is allowed to improve.

400 invalid_request The body did not parse, or a field is the wrong type. The message names the field.
401 unauthenticated No key, a malformed key, or a key that has been revoked.
403 scope_missing The key is valid but was not created with the scope this call needs. Scopes are chosen per key and cannot be widened after the fact.
402 insufficient_funds The balance would not cover it. The response carries required_cents and balance_cents so you can act without a second call.
404 not_found No such resource on this account. Deliberately identical to the response for a resource on somebody else's account.
409 conflict The resource is busy — usually an action already running on that server.
422 unavailable Valid, but not currently possible: the plan is out of stock in that region, or the image is not offered for that family.
429 rate_limited Retry-After is set, and it is accurate rather than a constant.
5xx server_error Ours. Safe to retry with the same Idempotency-Key — that is precisely what it is for.

A 404 for a resource on somebody else’s account is byte for byte the same as one for a resource that never existed. That is deliberate: distinguishable responses turn an API into an oracle for enumerating other people’s infrastructure.