The API
Everything you do in the console, a script can do through the API: servers and their parts, the other products of your account and a read-only view of your money. It speaks JSON over HTTPS and follows the same rules as the console: your level, your credit and the systems and locations that can be built.
Make a token
- Open Access & security in the console and choose API tokens.
- Choose Add, give the token a name, choose its access (Read and write, or Read only for a script that only looks) and, if you like, the addresses it may be used from (one address or range per line). Leave that empty to allow every address. Choose Generate token.
- Copy the token. It is shown once; we keep only a hash of it.
A token acts as you. Disable it when you want a pause, and revoke it when it should never work again. Both are in the same list, and take effect at once.
Call the API
The API lives at https://api.stavane.com/v1, the first server of the OpenAPI description. The same API also answers at /api/v1 on the console, for example https://console.stavane.com/api/v1, with the same tokens and rules; scripts that already use that address keep working. Send the token as a Bearer token:
curl -H "Authorization: Bearer stv_..." https://api.stavane.com/v1/servers
A token can make 120 requests per minute. Beyond that the API answers 429 with a Retry-After header.
What you can do
| Method and path | What it does |
|---|---|
GET /servers |
Your servers |
GET /servers/{id} |
One server |
POST /servers |
Build a server (see below) |
POST /servers/{id}/actions/start |
Start a stopped server |
POST /servers/{id}/actions/stop |
Stop a running server |
POST /servers/{id}/actions/reboot |
Restart a running server |
DELETE /servers/{id} |
Remove a server and everything on it |
PATCH /servers/{id} |
Give a server a new name (name) |
POST /servers/{id}/actions/resize |
Move a server to another plan of its family (plan); a running server stops for a moment and starts again by itself |
POST /servers/{id}/actions/rebuild |
Put a fresh system on a server (image, ssh_keys, confirm, optional root_password_mail); everything on the disk goes |
GET /servers/{id}/settings |
The settings of a server: protection, console, boot device and media |
PATCH /servers/{id}/settings |
Change them, for example {"protected": false} |
POST /servers/{id}/actions/reset-password |
Give root a new password; the answer holds it once |
GET /servers/{id}/reverse-names |
What each address of a server answers when it is looked up backwards, and under request a name that waits for approval (pending) or was refused (rejected, with the reason) |
PUT /servers/{id}/reverse-names |
Point one address at a name (address, name) |
GET /servers/{id}/traffic |
The traffic of a server this month, in and out in GB, as the server page shows it; unlimited under fair use, so never a bill |
GET /servers/{id}/addresses |
The extra IPv4 addresses of a server |
POST /servers/{id}/addresses |
Add an extra IPv4 address; needs an Idempotency-Key |
POST /servers/{id}/addresses/{address}/move |
Move an address to another of your servers in the same location (server) |
DELETE /servers/{id}/addresses/{address} |
Give an address back; billing for it stops |
GET /plans |
The plans you can order, with the price per month in your currency |
GET /regions |
The locations you can build in |
GET /images |
The systems you can build |
GET /ssh-keys |
Your SSH keys, with the id to build with |
POST /ssh-keys |
Add an SSH key (public_key, optional name) |
PATCH /ssh-keys/{id} |
Give an SSH key another name (name) |
DELETE /ssh-keys/{id} |
Remove an SSH key from your account; it stays on servers that already have it |
GET /account |
Your credit in cents (balance_cents), the currency, and what this month cost so far and will cost |
GET /addresses |
Every extra IPv4 address of your account, with the server it is on and its price per month |
GET /openapi.json |
The whole API as an OpenAPI description; no token needed |
Build a server
A request that builds needs an Idempotency-Key header: a string of your own, 8 to 100 characters. Sending the same request with the same key again gives back the same server instead of a second one, so a retry after a timeout is safe. Use a new key for every new request: the same key with other fields answers 422 with the error under Idempotency-Key, and nothing is built. A key belongs to one path, and a request we refused (an answer from 400 to 499) leaves the key free, so you can fix the fields and send it again under the same key.
curl -X POST https://api.stavane.com/v1/servers \
-H "Authorization: Bearer stv_..." \
-H "Idempotency-Key: build-web-1-2026-09-23" \
-H "Content-Type: application/json" \
-d '{"plan":"S-2","region":"ams","image":"ubuntu:24.04","name":"web-1","ssh_keys":["<id from GET /ssh-keys>"]}'
The fields are plan, region, image (as GET /images gives it) and name, and optionally ssh_keys (ids from GET /ssh-keys), root_password_mail (the root password by e-mail, as in the console; on unless you send false, and then at least one key is needed), cloud_init (a cloud-config document) or user_script (the id of a saved script), timezone (Europe/Amsterdam, America/New_York or UTC; without it the time zone of the location), project (the id of a project) and protected (protection against removing and rebuilding; on unless you send false).
The answer is 202 with the server. It is built in the background: read it again with GET /servers/{id} until its status is running.
Calls that wipe data
Rebuilding a server removes what is on the disk now. Send the name of the server as confirm, exactly as you would type it in the console, so a wrong id in a script cannot wipe the wrong machine.
A new server is protected against removing and rebuilding, as in the console. DELETE /servers/{id} and a rebuild of a protected server answer 422; send PATCH /servers/{id}/settings with {"protected": false} first.
Extra addresses
An extra address is billed by the hour from the moment it exists, exactly as in the console. The same Idempotency-Key twice gives the same address, so a retry never adds a second one. The guides of other parts of a server list their own calls under "Through the API".
The other products
Each product below is part of the API while it is part of your account. A call that makes something billed needs an Idempotency-Key, as building a server does. Removing something asks for its name as confirm, exactly as you would type it in the console. Each product has an options call with what can be made and its price per month in your currency.
Every call of every product is in the OpenAPI description at /openapi.json, which only lists what your account can use.
Your balance, usage and invoices
A token of an owner reads your money; a token of a member gets 403 here, and the billing role makes no tokens. A token can never change your money: topping up, cards, automatic top-ups and refunds stay in the console. Amounts are whole cents in the currency of your account.
| Method and path | What it does |
|---|---|
GET /billing/balance |
Your credit per pot (paid_cents, bonus_cents, and promotional_cents for welcome and referral credit) and the estimated balance the console shows |
GET /billing/usage |
What this month cost so far and will cost at today's prices, in total and per resource |
GET /billing/transactions |
Your credit ledger, newest first: top-ups, bonus credit, usage per day, refunds |
GET /billing/invoices |
The invoices, credit notes and usage statements we issued to you, newest first |
GET /billing/invoices/{id} |
One document with its lines |
GET /billing/invoices/{id}/pdf |
The document as the PDF we sent |
These lists come a page at a time, see "Lists" below.
Support tickets
Ask us something from a script, and read our answer, as in Support in the console. Tickets are plain text; files cannot be attached. A ticket goes by its reference, such as T-123456. Opening a ticket and replying need an Idempotency-Key, so a retry never sends your message twice.
| Method and path | What it does |
|---|---|
GET /tickets/options |
What you can ask about, and with a service contract the priorities |
GET /tickets |
Your tickets, the latest conversation first, a page at a time (see "Lists" below) |
POST /tickets |
Open a ticket (subject, category, body, optional server) |
GET /tickets/{reference} |
One ticket with the whole conversation |
POST /tickets/{reference}/replies |
Reply (body); a reply to a closed ticket opens it again |
POST /tickets/{reference}/close |
Close a ticket |
What stays in the console
The screen of a server needs a browser, so it is only in the console. See The console of your server.
Lists
A list that can grow without end comes a page at a time: your transactions, invoices, tickets and service contracts. A page has 25 rows unless you ask for up to 100 with limit. While there are more, the answer has a next_cursor: send it as cursor to read the next page; it is null on the last page. Every other list holds everything of your account, in the same order on every call, except the activity of a server, which gives the latest rows only (limit, at most 100). What we read from the systems that run your servers (firewall rules, reverse names and the like) comes in the order those systems give.
Answers and errors
Every answer is JSON with the result under data. When something is refused:
400: a call that needs anIdempotency-Keycame without one.401: no token, or a token that is unknown, disabled or revoked.403: the token may not be used from this address, it is a read-only token and the call changes something, or your role in the organisation does not allow the call (for example billing).404: there is no such thing of yours, or it belongs to a product that is not part of your account.422: a field is missing or refused, for example a plan your level does not open yet or credit that does not cover the first hours. The answer says which field and why, undererrors, with the name of the field you sent (size_gb, ortargets.0.portfor a field inside a list). A refusal that is about the thing itself and not a field you sent is under the name of its path parameter, such asserver. A field the call does not take is refused too, by its name, before anything happens: a typo such as"port"where the call takes"from"never passes unnoticed. The sameIdempotency-Keywith other fields than its first request answers422underIdempotency-Key.429: too many requests.