Simkl uses standard HTTP status codes. Error responses always have a JSON body withDocumentation Index
Fetch the complete documentation index at: https://api.simkl.org/llms.txt
Use this file to discover all available pages before exploring further.
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
| Code | Name | Retry? |
|---|---|---|
200 | OK | — |
201 | Created | — |
204 | No Content | — |
302 | Found | — |
400 | Bad Request | No — fix the request |
401 | Unauthorized | No — re-authenticate |
403 | Forbidden | No — fix the app or contact us |
404 | Not Found | No |
409 | Conflict | No |
412 | client_id failed | No |
429 | Too Many Requests | Yes — back off |
500 | Internal Server Error | Yes — exponential backoff |
502 | Bad Gateway | Yes — exponential backoff |
503 | Service Unavailable | Yes — exponential backoff |
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
error | When | Example message |
|---|---|---|
empty_field | A required field is missing | Missed "to" parameter |
wrong_parameter | A field has an unaccepted value | Wrong "to" parameter |
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
error | When |
|---|---|
redirect_failed | OAuth/PIN redirect parameter doesn’t match a URL registered for your app |
forbidden | Generic refusal — feature gated to Simkl Pro/VIP, or an action the user hasn’t consented to |
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):
| Attempt | Wait before retry |
|---|---|
| 1 | 1 s |
| 2 | 2 s |
| 3 | 4 s |
| 4 | 8 s |
| 5 | 16 s (give up after this) |
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:
| Code | Suggested user message |
|---|---|
401 | Please sign in again to continue. |
403 | This action isn’t available for your account. |
404 | We couldn’t find that title. |
429 | Slow down — try again in a moment. |
5xx | Simkl is having a moment. We’ll retry automatically. |
Logging
Always logerror and code together with your request URL. error is the stable contract you’ll branch on; code confirms the HTTP class.