error (machine-readable identifier), code (integer — the HTTP status code echoed in the body), and an optional message (human-readable guidance).
Standard error envelope
At a glance
Not every endpoint can return every code. Each endpoint’s reference page lists the codes that actually fire there — many endpoints can only return a subset (e.g.,
/sync/activities can only return 200, 401, 412, 429, 500). The 4xx/5xx pages below describe the codes; check the per-endpoint reference for which apply.Success codes
OK
200 — Success. The body contains the resource you requested.
Created
201 — Success. A new resource was created. Typical for POST /sync/... and POST /scrobble/....
No Content
204 — Success but the response body is empty. Typical for DELETE endpoints.
Found
302 — A redirect. Follow the Location header. Most commonly returned by the redirect endpoint.
4xx — your request
Bad Request
400 — The request was malformed. The message field usually identifies the offending parameter.
Real error values returned
Fix
Read
message, correct the request, then resend. Don’t retry without changing the request.
400 Bad Request
Unauthorized
401 — Missing or invalid user access token.
Common causes
Authorizationheader missing on a token-required endpoint.- The user’s
access_tokenwas revoked at Connected Apps settings. - A typo or truncation in the token value.
expires_in: 157680000 (5 years) on mint, so a 401 in practice means the user removed your app from their account or the token never matched in the first place.
401 Unauthorized
WWW-Authenticate header for clients that key off it:
Forbidden
403 — Refused. The request was understood but the caller is not permitted to perform it.
Known error values
Fix
Match the redirect URL exactly to what’s registered in your developer settings. For Pro/VIP-gated features, surface an upgrade prompt rather than retrying.
403 Forbidden — redirect mismatch
Not Found
404 — The URL or resource doesn’t exist.
404 Not Found
Conflict
409 — The resource state conflicts with the request.
Common causes
- Tried to
POST /scrobble/stopon a session that completed in the last hour. - Tried to perform a write that would duplicate an existing record.
409 as soft-success when scrobbling — the user already finished the episode. Don’t retry the same call. The body includes watched_at (when the original watch landed) and expires_at (when the 1-hour duplicate-window closes), so you can show the user the existing watch state instead of re-firing the call.
409 Conflict (POST /scrobble/stop)
client_id failed
412 — Your client_id is missing, wrong, suspended, or has hit a request limit.
Common causes
- Typo in
client_id(use the value from developer settings, not a screenshot). - App was suspended for a Terms of Service violation.
- App hit a per-
client_idrequest cap — see Rate limits.
412 client_id failed
Too Many Requests
429 — Rate limit hit.
Fix
Implement exponential backoff. Cache responses aggressively. Use Trending and Calendar CDN files instead of polling. Full guidance: Rate limits.
429 Too Many Requests
5xx — our problem
Internal Server Error
500 — Something broke on our side.
Fix
Retry with exponential backoff. If the error persists for more than ~30 seconds, report it on Discord.
500 Internal Server Error
Bad Gateway
502 — Simkl is being upgraded or briefly unreachable.
Fix
Retry with exponential backoff. Check status for known issues.
Service Unavailable
503 — Servers are up but overloaded.
Fix
Retry after the delay. Respect any Retry-After header.
Handling errors gracefully
Exponential backoff
Recommended retry pattern for transient errors (429, 500, 502, 503):
Add jitter (random 0–1 s) so multiple clients don’t synchronize.
retry-with-backoff.js
Don’t retry deterministic errors
400, 401, 403, 404, 409, 412 mean something is wrong with the request itself. Retrying without changing the request just wastes quota and triggers 429.
User-facing messaging
message is human-readable but written for developers. For end users:
Logging
Always logerror and code together with your request URL. error is the stable contract you’ll branch on; code confirms the HTTP class.