Getting started

API conventions

The rules every Serverside.com API endpoint shares: base URL, naming, what a success and a failure look like, pagination, error codes, retries and idempotency.

Base URL and versions

https://api.serverside.com

Every path starts with a version segment, /v1. Requests and responses are JSON over HTTPS; send Content-Type: application/json with any request that has a body. Every response carries an X-Request-Id header.

Names and values

WhereStyleExample
Query parameterscamelCase?pageSize=50&sortColumn=name
JSON request and response fieldscamelCase"offeringId", "primaryIpv4"
Allowed valuesUPPER_SNAKE_CASE strings"GRACEFUL_RESTART", "RAID10"

A field with a fixed set of values lists them under Allowed values on its endpoint page. The API matches them without regard to case, so per_hour reads as PER_HOUR. A value outside the list, a number in place of the name, or a comma-separated list is refused with 400, and the error names the values the field takes.

IDs are UUIDs. IP addresses and networks travel as strings, such as "192.0.2.10" and "192.0.2.0/28". Timestamps are ISO 8601 strings, and one you send has to carry its offset: 2026-09-20T00:00:00Z and 2026-09-20T02:00:00+02:00 are read as the same instant, and 2026-09-20T00:00:00 is refused. A JSON property the endpoint does not know is ignored, so a misspelled optional field is dropped without an error.

Success and error responses

The HTTP status tells the two shapes apart: a 2xx answer is a success, anything else is an error, and success repeats that in the body.

Success

{
  "success": true,
  "data": { },
  "pagination": {
    "totalRecords": 42,
    "currentPage": 1,
    "pageSize": 25,
    "totalPages": 2
  }
}
FieldMeaning
successtrue.
dataThe payload: an object, or an array on list endpoints. Endpoints that only confirm an action send {"success": true} with no data.
paginationOn lists that take page and pageSize (Pagination). Every other response leaves it out.

204 No Content answers have no body at all. A 202 Accepted answer comes from a call whose change is still being applied, such as attaching a virtual network or switching boot mode, and its data holds the change's progress: a status of APPLYING and since, the time the change was accepted. Download invoice PDF is the one endpoint that returns a file instead of JSON.

Error

{
  "success": false,
  "data": null,
  "code": "VALIDATION_ERROR",
  "message": "pageSize: Must be between 1 and 1000.",
  "traceId": "SRV-7F3A-0C21-9B4E-51D2",
  "details": [
    {
      "code": "INVALID_VALUE",
      "message": "Must be between 1 and 1000.",
      "field": "pageSize",
      "location": "query"
    }
  ]
}
FieldMeaning
successfalse.
datanull, except when an invoice payment is refused: then it holds the payment result (Billing).
codeWhat went wrong, as a stable UPPER_SNAKE_CASE code. Branch on it together with the status. A code your client does not know yet is handled by its status.
messageA sentence for people. Its wording can change, so don't parse it.
traceIdThe request's id, equal to the X-Request-Id header. Quote it when you contact support.
detailsOne entry per problem, on validation errors and some others. Each has its own code and message, the field it is about (null when the problem concerns the whole request) and that field's location: body, query, route or header.

The error body shares success and data with a success, so a client can read both with one type.

Pagination

A list whose endpoint page names page among its query parameters is paged, and all of them take the same set:

ParameterDefaultNotes
page11-based. A page past the last returns the last page.
pageSize251 to 1000. A value outside that range is a 400.
sortColumnnoneThe field to sort by.
ascendingtruefalse reverses the sort.
searchnoneFree text; leading and trailing spaces are dropped. Which fields it searches depends on the endpoint.
filter[<field>]noneOne parameter per filtered field.

The other lists, such as List OS images and List available datacenters, return every row in data at once and carry no pagination.

To read everything from a paged list, request pages until currentPage reaches totalPages. An empty list answers with totalPages 0 and currentPage 1, so check for 0 first. A page past the end returns the last page again, so a loop that waits for an empty page never stops on a list with rows in it.

curl --request GET \
  --url 'https://api.serverside.com/v1/services/baremetal?page=2&pageSize=100&sortColumn=name' \
  --header 'X-API-KEY: <api-key>'

A filter on a text field matches any value that contains it, ignoring case. A filter on a field with allowed values takes the value as the API writes it, filter[status]=CANCELLATION_REQUESTED, and a value the field does not have is a 400 whose detail names filter[status] as the field. A filter on a true-or-false field takes true or false; any other value is ignored, so the list comes back unfiltered on that field. Fields of other types cannot be filtered.

sortColumn and the names inside filter[...] are the stored property, which is not always spelled like the JSON field, and they are not part of the stable contract. A name the endpoint does not know is ignored without an error: a mistyped filter returns the list unfiltered, and a mistyped sortColumn returns it unsorted. Without sortColumn the order is not fixed either, since invoices come back sorted by currency and subscriptions in no set order, so pages can repeat or skip rows. Send sortColumn on every list you page through.

Errors

Statuses

StatusMeaningWhat to do
400The request could not be read: malformed JSON, a wrong type, a value outside the declared range, an unknown allowed value, a timestamp without an offset.Fix the request; the same request fails again.
401The key is missing, wrong, disabled or expired, or the endpoint takes a console session (Authentication).Send a valid key.
403The key is valid but lacks the permission the endpoint checks.Use a key that holds it.
404Nothing with that id exists in your organization, or no endpoint has that path. A resource of another organization gets the same answer.Check the id and the path.
405, 406, 408, 413, 415The method, Accept header, body timing, body size or content type is wrong.Fix the request.
409Something is in the way: a duplicate, a resource in use, an operation already running.Retry after Retry-After when the response has one; otherwise resolve the conflict.
422The request was read, and a rule of the resource or its current state refused it.Change what you asked for.
500A fault on our side.Retry later, and send the traceId to support if it persists.
502A system behind the API answered with an error.Retry later.
503A system behind the API cannot be reached, or a feature is paused. Always has Retry-After.Retry after Retry-After.
504A system behind the API did not answer in time.Retry later.

Retry-After is a number of seconds. Every 503 has it, and so does each 409 that clears by itself, such as a management controller that is restarting. Retrying a 500, 502 or 504 is safe for reads. Of the calls that create something, only the five listed under Idempotency take a key that makes a retry safe; before retrying any other, list what exists, since the first request may have gone through.

Validation

Two codes carry a list of details:

  • VALIDATION_ERROR, 400: the request could not be read. A detail's code is INVALID_BODY when the JSON body itself cannot be read: bad syntax, a wrong type, a missing required member, a value outside a field's allowed values. It is INVALID_VALUE for a query, route or header value, and for a body field that was read but breaks a declared limit such as a length or a range. VALUE_OUT_OF_RANGE marks a number the operation checks in its own code.
  • VALIDATION_FAILED, 422: the request was read, and one or more rules refused it. Each detail's code names the rule, such as BILLING_CYCLE_UNAVAILABLE on an order, and its location is null.

A body field is named by its JSON path, with indexes for arrays (services[0].offeringId, billingAddress.countryCode). A query field is named as you sent it, a route field by its parameter name and a header by its name. With one detail, message reads field: problem; with several, The request has N problems: followed by each of them.

An order on a billing cycle its plan is not sold on:

{
  "success": false,
  "data": null,
  "code": "VALIDATION_FAILED",
  "message": "cycle: This billing cycle is not offered.",
  "traceId": "SRV-1A2B-3C4D-5E6F-7A8B",
  "details": [
    {
      "code": "BILLING_CYCLE_UNAVAILABLE",
      "message": "This billing cycle is not offered.",
      "field": "cycle",
      "location": null
    }
  ]
}

An id in the body that matches nothing is treated by what it names. A plan, image, application or network segment id is a 422 with a detail on that field, such as OFFERING_NOT_FOUND. An id of something your organization owns (a server, a network, an SSH key) is a 404 ENTITY_NOT_FOUND with no field, so another organization's id looks the same as one that does not exist. An unknown datacenterId is 422 OFFERING_NOT_AVAILABLE.

Codes

Codes that any endpoint can answer:

CodeStatusMeaning
VALIDATION_ERROR400The request could not be read; see details.
BAD_REQUEST400The HTTP request itself could not be read.
UNAUTHENTICATED401See Authentication errors.
NO_PERMISSION403The key lacks the permission the endpoint names.
ENTITY_NOT_FOUND404No resource with that id in your organization.
ROUTE_NOT_FOUND404No endpoint has that path.
ENTITY_ALREADY_EXISTS409A resource with the same identity exists.
ENTITY_IN_USE409The resource is in use, so it cannot be deleted or moved.
ENTITY_STATE_INVALID422The resource's state does not allow the operation, where no more specific code exists.
VALIDATION_FAILED422A rule refused the request; see details.
INTERNAL_ERROR500A fault on our side.
UPSTREAM_ERROR502A system behind the API answered with an error.
UPSTREAM_UNAVAILABLE503A system behind the API cannot be reached. Retry-After: 30.
UPSTREAM_TIMEOUT504A system behind the API timed out.

Codes for bare metal servers:

CodeStatusMeaning
SERVICE_SUSPENDED422The service is suspended; nothing can change until it is reinstated.
SERVICE_LOCKED422The service is locked.
OPERATION_IN_PROGRESS409Another operation is running on the server.
BMC_RESET_IN_PROGRESS409The management controller is restarting. Retry-After: 30.
BMC_UNREACHABLE503The management controller is not responding. Retry-After: 30.
BMC_REQUEST_FAILED502The management controller returned an error.
KVM_SESSION_REQUEST_IN_PROGRESS409A console session request for the server is already running. Retry-After: 10.
OPERATION_NOT_SUPPORTED_PLATFORM422The server's hardware does not support the operation.
PRIMARY_IPV4_REQUIRED422A deployment needs a primary IPv4 address on the server.
ISO_LIMIT_REACHED422The organization holds as many ISOs as it may.
ISO_UPLOAD_INCOMPLETE422The ISO's upload has not finished or could not be verified.
VIRTUAL_MEDIA_EJECT_FAILED502The management controller could not eject the media.
VIRTUAL_MEDIA_ATTACH_TIMEOUT504Attaching virtual media timed out.
NETWORK_METRICS_UNAVAILABLE503Traffic metrics cannot be read for now. Retry-After: 30.

Codes for orders and billing:

CodeStatusMeaning
OFFERING_NOT_AVAILABLE422The plan cannot be ordered as asked: out of stock in that datacenter, or an unknown datacenterId.
USAGE_BILLING_DISABLED422Hourly billing is not enabled for the organization (Quickstart).
BILLING_ADDRESS_REQUIRED422The organization has no billing address.
PAYMENT_FAILED422The payment could not be taken: the card was declined, or the organization has no account credit; data holds the payment result.
INVOICE_CANNOT_ACCEPT_PAYMENT422The invoice is already paid, cancelled or refunded, or a payment is awaiting confirmation.
SUBSCRIPTION_NOT_ACTIVE422The subscription has already been cancelled.
TERMINATION_BLOCKED422An item on the subscription cannot be cancelled now; nothing changed.
BUNDLED_ITEM_CANCELLATION_BLOCKED422The IP block came with a server; cancelling the server releases it.
SUBSCRIPTION_CANCELLATION_INCOMPLETE422Some items were cancelled before one failed. Sending the request again is safe.
IDEMPOTENCY_KEY_REUSED409The Idempotency-Key was already used for a different request.
ORDER_CREATION_IN_PROGRESS409A request with the same Idempotency-Key is still creating the order. Retry-After: 5.
ORDER_PROVISIONING_IN_PROGRESS409A create under the same Idempotency-Key is still running. Retry-After: 5.
PROVISIONING_FAILED409Nothing in an hourly order or an IP block request could be provisioned.
VAT_SERVICE_UNAVAILABLE503The EU VAT number check could not be completed. Retry-After: 30.
STRIPE_PROVIDER_ERROR502The payment provider failed.

Networking codes are listed in Managing IP addresses and Setting up virtual networks, next to the calls that answer them. VALIDATION_FAILED detail codes are named in the guide for the operation that sends them.

Idempotency

Five operations accept an Idempotency-Key header of up to 200 characters: creating a bare metal service, creating an IP block, creating an order, and assigning or unassigning a service on a virtual network. Send a fresh value for each new action and the same value when you retry that action.

The three create calls share one set of keys per organization, and a key is never forgotten. A retry with the same key and the same purchase (billing cycle, promo code and items) places nothing new and answers with what the first request created; Create order then answers 200 in place of 201, with data.replayed set to true. The same key with a different purchase is refused with 409 IDEMPOTENCY_KEY_REUSED. While the first request is still running, a retry gets 409 ORDER_CREATION_IN_PROGRESS with Retry-After: 5; send it again after that many seconds.

On the virtual network calls a key belongs to the service in the body, and it is remembered until 90 days after the change has been applied. A retry answers 202 again without queuing a second change, and the same key with another network or segment gets 409 IDEMPOTENCY_KEY_REUSED. A second change while the first is still being applied gets 409 NETWORK_CHANGE_IN_PROGRESS with Retry-After: 10.

Rate limits

Each API key has its own limits:

RequestsLimit
Reads (GET)1,000 per minute
Writes (POST, PUT, PATCH, DELETE)100 per minute

Polling a deployment every 20 seconds, as the guides do, uses 3 reads a minute.