Skip to main content
Every API call that touches user data needs a user access_token. This page covers the four things you decide before writing any code, in the order you hit them. Two of those decisions are easy to confuse, because the numbers differ: Developer settings asks for the type. The flow is a choice you make in code. They are not the same list.

1. Pick a client type

The type is not a filing category — it is the one answer that configures the registration. Picking it decides three things at once:
  • Whether a client_secret is minted at all. One type gets one; for the other two no secret is ever created, so there is nothing to leak.
  • Whether redirect URIs are stored. Two types require at least one; the third registers none.
  • Which flows are therefore reachable. A registration with no redirect URI has nowhere for Simkl to send the user back to, so it cannot run a browser redirect.
That is also why the choice is fixed at registration and cannot be changed afterwards — correcting it means registering another app. These are the labels you will see on the form, in this order:
AUTH V1 and AUTH V2 registrations are counted separately, so migrating never costs you a slot — your existing V1 apps keep working and do not use up your V2 allowance.The allowance for unverified apps is 3 on Free, 5 on PRO, 10 on VIP — per version, not combined. Verified apps do not count against it at all, and verification is free on every plan: request it from developer settings once your app is live.

2. Decide whether you need a client_secret

Only Server apps & services has one, and only because a backend can keep it. It is not a capability upgrade and unlocks nothing — the other two types are fully functional without one.

What the secret actually does

It answers one question at the token endpoint: is this request really coming from your backend? Your client_id is public, so on its own it proves nothing. The secret is the proof, and it is why a server app can be held to a stricter standard than an app running on a stranger’s phone. It is deliberately not a key to anything by itself. There is no app-only grant — client_credentials returns 400 unsupported_grant_type — so a secret with no authorization code and no refresh token alongside it reaches nothing user-specific.

When you send it

Once you are registered as Server apps & services, sending the secret is not optional — it is required on every request that authenticates your app: Two transports are accepted, and they are equivalent — pick whichever your HTTP library makes easy:
Client authentication is checked before anything else in the request. A missing or wrong secret is 401 invalid_client, and you get it regardless of what else might be wrong — so during setup, fix invalid_client first and re-read the rest of the error only after it clears.The upside: a 401 invalid_client does not consume your authorization code. The code is never reached, so you can fix the secret and retry the same one.
If the secret leaks, rotate it — do not register a new app. Developer settings regenerates it; the new one is shown once, the old one dies immediately, and existing user grants survive, so nobody has to re-authorize. Rotating is what stops a leaked secret being used to refresh.
Never ship a client_secret in anything a user can install. Mobile binaries, JavaScript bundles and extension packages can all be unpacked. If your code runs on someone else’s device, register Mobile, desktop & browser apps and rely on PKCE — that is exactly what it is designed for.
Your client_id is public and safe to commit — in source, in a JS bundle, in a public repo. Under V2 it reaches nothing user-specific on its own. Only the secret and user tokens need protecting, and those are revoked automatically if they reach GitHub, private repositories included.

3. Pick a flow

OAuth flow

Web, mobile, SPA, desktop, extensions — anything that can open a browser.

Device / PIN flow

TVs, consoles, watches, CLIs — anything that can’t. 8-character code.
Both run on AUTH V2 and both require PKCE. Any type can use the Device / PIN flow. Choose TV, devices & command line only if that is the only flow your app will ever use — if you also ship a web or mobile companion, register Mobile, desktop & browser apps and use both flows from it.
A client_secret is not a third flow. A Server apps & services app runs the OAuth flow like everyone else — same authorize redirect, same PKCE, same code. The only difference is that the secret travels alongside the code at the token endpoint. Every access token belongs to a user who authorized it, whichever type you registered.

4. Choose a scope

Omitting scope gives you read-only. V2 defaults to media:read, so a write request on a token minted without an explicit scope fails with 403 insufficient_scope. Ask for media:read media:write up front if your app writes — widening later means sending the user through consent again.
Full detail, including how to handle an insufficient scope at runtime, is on Scopes.

Point your app at the endpoints

Four URLs, on two different hosts. Setting them directly is a perfectly normal way to configure an app — most of our examples do exactly that:
Only the consent page is on simkl.com. The other three are on api.simkl.com, and pointing the authorize URL at the API host is the single most common wiring mistake — it returns a 404.

Or let the library fetch them

Simkl publishes an RFC 8414 discovery document at /.well-known/oauth-authorization-server. It carries all four URLs under the metadata names above, plus the supported auth methods, scopes and PKCE methods. Neither approach is more correct. Hardcoding is fine and is what the rest of these docs show. Discovery is worth it when your library already speaks it — you configure one setting instead of four, and you pick up any future endpoint change without a release:
If your app has no client_secret, pass client.None(). Left out, openid-client defaults to client_secret_post and tries to send a secret you do not have. A Server apps & services app does the opposite — pass the secret as the third argument and leave the fourth one out:
If your library asks for /.well-known/openid-configuration, point it at OAuth 2.0 instead. Simkl is an OAuth 2.0 server, not an OpenID Connect provider, so that path returns 404 and the document lives at /.well-known/oauth-authorization-server. Libraries that walk both paths in turn — Spring Security, for one — sort this out themselves. A 404 during setup means yours does not, and needs telling.
Either way you still choose the client type, the secret, the flow and the scope yourself — discovery configures endpoints, not decisions. Setup snippets for Node, Python and Java are on the V2 overview.

What you will be working with

See Tokens and refresh for the refresh grant, revocation, and the two refresh behaviours that surprise people.
Check one thing before you build: V2 apps must send a user token on everything except plain catalog lookups. If your app has a catalog or browsing mode that works with no signed-in user, read the V2 overview first — search in particular needs a token.
AUTH V1 is being retired. Apps on it are expected to stop working around April 2027, so starting a new integration there means doing this work twice.If you already ship a V1 app it keeps working for now, and Migrating from V1 to V2 is the path off. That window is comfortable for most apps; if it is not comfortable for yours, talk to us rather than running out of time.

Next

OAuth flow

The browser flow end to end, with PKCE.

Device / PIN flow

Show a code, poll until the user approves.

Client libraries

What to configure in a stock OAuth library, and the three whose defaults break.

AUTH V2 overview

Discovery, anonymous limits, and the endpoint reference.

Errors

Every error code, including the two different 401s.

Scopes

The full media:read / media:write reference.