API conventions for authentication, pages, errors and rate limits

llms.txt

Every operation of the SrvYard API follows the same small set of rules. Learn them once and you can read any endpoint in the reference without surprises: how you authenticate, what a list looks like, how to ask for the next page, what an error contains, and what to do when you are slowed down.

This article walks through those rules in the order a script meets them, and shows where each one is written down in the dashboard. It assumes you have a token already; if not, start with Create an API token and pick scopes.

Before you begin

  • A token with the scopes you need. Each operation names the scope it requires. A token can never do more than the role of its owner allows.
  • A plan with API access. Without it every call is answered 403 with the code plan_required.
  • Any HTTP client. The examples use curl, but nothing is specific to it. Always send Accept: application/json.

1. Authenticate every request

The API lives under /api/v1 on your SrvYard address, and every request carries the token in a header:

curl -H "Authorization: Bearer $SRVYARD_TOKEN" \
     -H "Accept: application/json" \
     https://srvyard.example/api/v1/servers

The token decides which organisation you are talking to, so no organisation id appears in any URL. Resources of another organisation answer 404, never 403, so a token cannot be used to find out which ids exist elsewhere.

The cheapest way to test a token is GET /api/v1/token. It needs no scope and returns the token's name, organisation, scopes, expiry and last use.

Four things can go wrong before the request reaches an endpoint, and each has its own message:

Status Code What it means
401 unauthenticated No header, not a srv_ token, or the token is unknown or revoked.
401 token_expired The token passed its expiry date.
403 plan_required The organisation's plan has no API access.
403 missing_scope The token lacks the scope this operation needs; the answer names it.

2. Read an operation in the reference

Open Settings > API tokens > Reference, choose a group and open an operation. Every operation row follows the same pattern.

The List servers operation opened in the API reference
An operation in the Reference tab: scope, parameters and an example response.
  1. The scope badge. servers:view is the permission the token needs. Your role must hold it as well.
  2. Parameters. For a list: page, per_page, sort and the filter[...] fields this endpoint understands, each with a one-line description. A red star marks a required one.
  3. Example response and example request. The response is an example of the shape, not real data; values such as string are placeholders. The example request is a curl command you can copy.

Underneath, the line Errors lists the failure statuses the operation can return.

3. Lists: pages, sorting and filters

Every list answers with a data array plus paging information:

  • page starts at 1.
  • per_page defaults to 25 and is capped at 100. Larger values are lowered to 100.
  • sort takes a comma-separated list of fields; a leading - means descending, for example sort=-created_at. The allowed fields are listed on each operation, and an unknown one is rejected with 422.
  • filter[name]=... narrows the list. Most filters match exactly, and several values can be given separated by commas (filter[status]=ready,disconnected); filter[name] matches names that contain the text. An unknown filter is rejected with 422 and the message lists the allowed ones.
  • include=... on the operations that support it adds related data.
curl -G -H "Authorization: Bearer $SRVYARD_TOKEN" -H "Accept: application/json" \
     --data-urlencode "filter[status]=ready" \
     --data-urlencode "sort=-created_at" \
     --data-urlencode "per_page=50" \
     https://srvyard.example/api/v1/servers

Ids are always UUIDs, dates are ISO-8601, and no internal numeric ids or secrets ever appear. A secret that SrvYard generates for you (a new database password, a backup passphrase) is returned once, in the response that created it.

4. Find the operations a scope unlocks

When you build a token you often know the scope and need the endpoints, or the reverse.

The reference search box filtered by a scope name
Searching by scope lists every operation a token with that scope can call.
  1. Search endpoints, scopes. Matches the method, the path, the summary, the operation name and the scope. Typing sites:manage-deploys shows the groups that contain operations with that scope.
  2. The result list. Each row shows the method, the path, what it does, an async badge where relevant and the scope.

Use this to choose the smallest set of scopes for a token.

5. Work that runs in the background

Anything that needs the server (deploying, installing, rebooting, creating a server) does not make you wait. The call returns 202 Accepted straight away with the resource and a status_url, which is also sent in the Location header.

The Deploy a site operation with the async badge
An operation that runs in the background carries an async badge and answers 202.
  1. async. The operation answers 202 and finishes later.
  2. The scope. Here sites:manage-deploys, the same permission as the Deploy button in the dashboard.
  3. The response. Contains the new resource and its status_url. The example shows the shape of a deployment object; a deployment you have just queued starts as queued.

Poll status_url with GET until status reaches a final value: ready or failed for servers and sites, finished, failed or cancelled for deployments. Every few seconds is plenty. A delete that needs remote work returns the resource in a deleting state first and answers 404 once it is gone.

6. Errors

Every error from /api/* has the same shape (RFC 9457, content type application/problem+json):

{
  "type": "https://srvyard.example/api/v1#forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "Your API token or the role of its user does not allow this action.",
  "code": "forbidden",
  "missing_scope": "servers:create",
  "request_id": "..."
}
  • Branch on code, not on the wording of detail. Common codes: validation_failed (422), unauthenticated, token_expired, forbidden, missing_scope, plan_required, plan_limit (402), not_found, idempotency_conflict (409) and rate_limited (429).
  • A 422 carries an errors object with one list of messages per field.
  • A 402 with plan_limit means the organisation hit a limit of its plan, for example the number of servers.
  • Every response, success or failure, has an X-Request-Id header, repeated in the body as request_id. Quote it when you contact support.
The Using the API card with the rate limit sentence and the error paragraph
The card summarises the rate limit and the error format.
  1. The summary line. JSON over HTTPS, versioned under /api/v1, 60 requests a minute per token.
  2. The error paragraph. An example 403 and what 401, 403 and 429 mean, always visible on the Tokens tab.

7. Retry safely with idempotency keys

A network timeout on a POST leaves you unsure whether the deployment was queued. Send an Idempotency-Key header (1 to 255 printable characters, no spaces, unique per action) on POST, PUT, PATCH and DELETE:

curl -X POST -H "Authorization: Bearer $SRVYARD_TOKEN" -H "Accept: application/json" \
     -H "Idempotency-Key: deploy-2026-10-24-build-1187" \
     https://srvyard.example/api/v1/sites/<site-id>/deployments
  • The same key with the same request within 24 hours replays the first response, with the header Idempotent-Replayed: true. Nothing is queued twice.
  • The same key with a different body or URL answers 409 idempotency_conflict. A request that is still running with that key also answers 409.
  • Keys belong to one token. Server errors and rate-limit answers are not stored, so those can be retried with the same key.
  • The .env update and token creation are never stored, because their bodies hold secrets.

8. Rate limits

A token may make 60 requests a minute. The heavier operations (scans, exports, installs) allow 10 a minute in a separate bucket. Every answer carries X-RateLimit-Limit and X-RateLimit-Remaining; when you run out you get 429 with the code rate_limited and a Retry-After header holding the seconds to wait.

Tip

Do not loop faster than you need. Poll a status_url every five seconds rather than every hundred milliseconds, ask for per_page=100 instead of walking small pages, and wait for Retry-After before trying again.

Changes are announced ahead of time: a breaking change would ship as /api/v2, and an operation being retired is flagged with the Deprecation and Sunset headers and deprecated: true in openapi.json. Every successful change you make through a token is written to the activity log with the actor token:<name>, without the request body.

Troubleshooting

  • 422 with "Unknown filter" or "Unknown sort". You used a field the endpoint does not support. The message lists the allowed ones; the reference shows the same list.
  • 404 for something you can see in the dashboard. The token belongs to another organisation, or the id is from a different one. Check GET /api/v1/token.
  • 403 missing_scope. Read missing_scope in the answer and create a token that includes it.
  • 409 idempotency_conflict. You reused a key for a different request. Generate a new key per action, and reuse one only for retries of the same call.
  • 429 even though you are slow. Several jobs may share one token. The limit is per token; give each job its own.
  • The list is shorter than expected. Lists are paged at 25 by default. Add per_page=100, or follow the pages.
  • A 202 call never finishes. Poll status_url and read last_error or the log of the deployment. See A deployment failed.
Last updated 2026-10-248 min readNeed help? Contact support