Rate limits and errors
The FeatherPanel API response envelope, the common error codes, how rate limiting works, which limits are on by default and how admins change them.
On this page
Response envelope
Every /api route answers with the same JSON shape. A success:
{
"success": true,
"message": "OK",
"data": { },
"error": false,
"error_message": null,
"error_code": null
}
An error keeps the same keys and adds an errors array:
{
"success": false,
"message": "Invalid API key",
"data": [],
"error": true,
"error_message": "Invalid API key",
"error_code": "INVALID_API_KEY",
"errors": [{ "code": "INVALID_API_KEY", "detail": "Invalid API key", "status": 401 }]
}
Branch on error_code, not on message: messages can change, codes are what the code checks for. When a route
does not set a status, the panel uses 400.
Common error codes
These come from the middleware that runs before every controller, so you can meet them on almost any route.
| Code | Status | When |
|---|---|---|
NOT_AUTHENTICATED | 401 | No session and no API key. |
INVALID_API_KEY | 401 | The Bearer token matches no key. |
PUBLIC_API_KEY_NOT_ALLOWED | 401 | A public key was sent while public-key auth is off. |
INVALID_ACCOUNT_TOKEN | 400 | The session cookie is invalid or expired. |
API_KEY_IP_NOT_ALLOWED | 403 | The key has an IP allow list and the request came from elsewhere. |
ACCOUNT_DELETED | 403 | The account is deleted or scheduled for deletion. |
USER_BANNED | 400 | The account is banned. |
USER_NOT_FOUND | 404 | The key’s owner no longer exists. |
NOT_AUTHORIZED | 403 | Your role lacks the admin permission the route needs. |
SERVER_NOT_FOUND | 404 | The server UUID or short ID does not exist. |
ACCESS_DENIED | 403 | You do not own the server or VM and are not a subuser on it. |
SERVER_SUSPENDED | 403 | The server is suspended. |
RATE_LIMITED | 429 | See below. |
Controllers add their own codes for validation, for example MISSING_REQUIRED_FIELDS, INVALID_DATA_TYPE and
INVALID_DATA_LENGTH. The endpoint index lists the routes, and the panel serves its full
OpenAPI document at /api/openapi.json.
Rate limiting
When a limit is hit the panel answers 429:
{
"success": false,
"message": "You are being rate limited! Retry after 42 seconds.",
"error_code": "RATE_LIMITED",
"data": { "error_code": "RATE_LIMITED", "retry_after": 42 }
}
The response also has a Retry-After header in seconds. Wait that long before retrying.
How limits are applied
- Limits are counted per client IP in Redis. Without Redis the panel falls back to a file-based limiter.
- Rate limiting is on globally unless an admin turns it off.
- Each route ships with a suggested limit in code, such as 10 per minute on
/api/user/auth/loginand 5 per minute on/api/user/auth/register. The endpoint index shows the suggested limit of every route. - Only the sign-in routes, the e-mail verification route, the API key routes and the public webhooks enforce their suggested limit by default. All other limits stay off until an admin enables them, so the UI’s polling routes do not start returning 429 after an update.
- Users whose role has
admin.api.bypass_restrictionsare never rate limited.
Changing limits
Open Admin > Rate Limits (needs admin.settings.edit). You can switch rate limiting off globally, and give
each route a limit per second, minute, hour or day. The page writes to backend/storage/config/ratelimit.json:
{
"_enabled": true,
"routes": {
"login": { "_enabled": true, "per_minute": 10 }
}
}
Keys are route names (the login route is called login). A route entry with "_enabled": false turns that route’s limit off even if it is one of the enforced defaults.
The same changes are available through /api/admin/rate-limits.
Related
Frequently asked questions
Rate limiting is on globally, but only the authentication, API key and public webhook routes enforce their built-in limits by default. Every other route has a suggested limit that an admin can switch on.
Requests from a user whose role has the admin.api.bypass_restrictions permission are not rate limited.
In backend/storage/config/ratelimit.json. The Rate Limits page in the admin area edits that file.