Savee Developers
API

Errors

The error envelope, every status code the API returns, and what to do about each one.

The envelope

Every error — at every status code — has the same shape:

{
	"error": {
		"code": "UNAUTHORIZED",
		"message": "Missing Bearer token"
	}
}

Branch on error.code, not on error.message. The codes are stable; the messages are written for humans and may be reworded at any time.

Status codes

HTTPerror.codeWhenWhat to do
400INVALID_INPUTBad query parameter — unknown key, or limit outside 1–100Fix the request. Retrying unchanged won't help.
401UNAUTHORIZEDMissing, malformed, or unknown tokenCheck the Authorization header; regenerate the token if needed.
402PAYMENT_REQUIREDToken is valid but the subscription has lapsedRenew the subscription; the same token starts working again.
403FORBIDDENAPI access is not available on this account, or an OAuth token is missing a scopeCheck the account's subscription state, or request the missing scope. Retrying won't help.
404NOT_FOUNDNo such route, or the board doesn't exist / isn't accessible to the callerCheck the path and the board id.
429TOO_MANY_REQUESTSRate limit exceededWait Retry-After seconds, then retry.
500INTERNAL_ERRORSomething broke on our sideRetry with backoff. If it persists, tell us.

Insufficient scope

Only OAuth tokens can hit this — a personal access token represents you and carries every scope. When a call falls outside what the user approved, the response is 403 with a WWW-Authenticate header naming exactly what was missing:

HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope", scope="saves:read"
{
	"error": {
		"code": "FORBIDDEN",
		"message": "This token is missing the saves:read scope."
	}
}

It's 403 rather than 401 on purpose (RFC 6750 §3.1): the token is valid, it just doesn't cover this call. Re-authenticating with the same scopes won't fix it — send the user through authorization again asking for the additional scope. Read the scope parameter from the header rather than guessing, and note it can name more than one scope, space-separated.

Unknown query parameters are rejected

Paginated endpoints reject query keys they don't recognise rather than ignoring them:

curl 'https://api.savee.com/v1/saves?limt=10' \
  -H "Authorization: Bearer sv_live_…"
{
	"error": {
		"code": "INVALID_INPUT",
		"message": "Unrecognized key(s) in object: 'limt'"
	}
}

This is deliberate. A silently ignored limt=10 would page through your entire library at the default page size while you believed you'd asked for ten.

404 on boards is intentionally ambiguous

/v1/boards/{boardID}/saves returns 404 both when a board doesn't exist and when it exists but you have no role on it. The API won't confirm the existence of someone else's board, so you can't tell those cases apart — and neither can anyone probing for board ids.

Retrying

  • 429 and 500 are worth retrying. Honour Retry-After on a 429; use exponential backoff on a 500.
  • 400, 401, 403 and 404 will return the same result no matter how many times you retry. Fix the request or the account state.
  • 402 resolves itself when billing does — retry on a slow schedule (hourly, not per-second) if you want a lapsed integration to recover on its own.

On this page