Skip to main content
Simkl uses standard HTTP status codes. Error responses always have a JSON body with error (machine-readable identifier), code (integer — the HTTP status code echoed in the body), and an optional message (human-readable guidance).
Standard error envelope
Branch on error, never on message. The error identifier is the contract — it’s stable across releases. The message text is developer-facing prose that may be reworded at any time. Parsing message will break your client.

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
  • Authorization header missing on a token-required endpoint.
  • The user’s access_token was revoked at Connected Apps settings.
  • A typo or truncation in the token value.
Fix Re-authenticate with OAuth 2.0 or PIN. Simkl tokens advertise 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
The 401 response also carries an RFC 6750 §3 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.
Most catalog endpoints don’t return 404 for missing IDs. Hitting /movies/99999999 returns 200 [] (empty array), not 404. Use 404 documentation in per-endpoint references as the source of truth — most read-only catalog endpoints simply return empty.
404 Not Found

Conflict

409 — The resource state conflicts with the request. Common causes
  • Tried to POST /scrobble/stop on a session that completed in the last hour.
  • Tried to perform a write that would duplicate an existing record.
Fix Treat 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_id request cap — see Rate limits.
Fix Double-check the value. If it’s correct, reach out on Discord.
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 log error and code together with your request URL. error is the stable contract you’ll branch on; code confirms the HTTP class.