Rate limits & idempotency
Per-PAT rate limits
Every response carries RateLimit-* headers (RFC draft):
| Header | Meaning |
|---|---|
RateLimit-Limit | Bucket size (e.g. 60) |
RateLimit-Remaining | Requests left in the current window |
RateLimit-Reset | Seconds until the window resets |
RateLimit-Policy | 60;w=60 — 60 requests per 60-second window |
Defaults (per PAT, per minute):
- Writes (
POST,PUT,PATCH,DELETE): 60/min - Reads (
GET,HEAD): 300/min
Over the limit returns 429 rate_limited with a Retry-After header. Back off
with jitter and retry.
Idempotency-Key
Make any POST safe to retry:
curl -X POST https://api.vps-server.host/v1/servers/srv_42:reboot \
-H "Authorization: Bearer $PAT" \
-H "Idempotency-Key: $(uuidgen)"
- Same key + same body within 24h → replays the cached response (
Idempotent-Replay: trueheader) - Same key + different body →
409 idempotency_conflict - The key is your responsibility; use a fresh UUID per logical operation.
Retries
Retryable status codes: 429, 502, 503, 504, plus 408 on the rare
HTTP timeout. Use exponential backoff:
for attempt in range(5):
r = requests.post(...)
if r.status_code < 500 and r.status_code != 429:
break
time.sleep((2 ** attempt) + random.random())
Never retry on 400, 401, 403, 404, 409 (except idempotency_conflict
on retry of a failed request — there the key was reused with a changed body,
generate a new one), 422.