Skip to main content
AUTH V2 is Simkl’s standards-compliant OAuth 2.0 implementation, and the recommended path for every new integration. It follows the specs closely enough that a stock OAuth library configured against it should work without Simkl-specific patches. If you are maintaining an existing app built on AUTH V1, it is being retired — expected to stop working around April 2027. Start with Migrating from V1 to V2; for most apps the work is confined to your auth code, and we are happy to help if yours is harder than that.
V2 apps are authenticated-only for anything user-specific. There is no general anonymous client_id-only access the way V1 allows — a V2 app that calls a user-scoped endpoint without a bearer token gets:
If your app has a browsing mode that today works without a signed-in user, check it against the exceptions below before you migrate. Anything beyond plain catalog lookups — search included — will need a signed-in user.
Catalog data is the exception — it needs no user token, even on V2:These carry no per-user state, so they are exempt by design rather than as a side effect of caching — a cache miss behaves the same way. They also do not count against your quota.“No user token” does not mean “no parameters”. Every request still carries client_id, app-name and app-version, plus a User-Agent header — see Headers and required parameters. The exception is about the Authorization header only.Everything else requires a user token, including search, /redirect, and every sync, scrobble, ratings, custom-lists and user-settings endpoint.

Which flow do I need?

OAuth flow

For web apps, mobile apps, SPAs, desktop binaries and browser extensions — anything that can open a browser. PKCE is mandatory for all of them.

Device / PIN flow

For TVs, consoles, watches, CLIs and media-server plugins — anything without a usable browser or keyboard. Show an 8-character code, the user types it on their phone.
There is no third option, and which one you use depends on the device, not on whether you have a client_secret. If you have not registered the app yet, Set up authentication walks the decisions in order — client type, secret, flow, scope — and is the page to read first.

You need a new client_id

A V1 client_id cannot be upgraded to V2. Register a new app. There is no conversion, no switch in developer settings, and no way to enable V2 on existing credentials — V2 apps are a separate registration with a new client_id (and, for server apps, a new client_secret shown to you once).Sending V1 credentials to a /oauth2/* endpoint returns:
If you see that while holding credentials that work perfectly against the V1 endpoints, nothing is wrong with your request — you need a V2 app. Register one.
Your existing V1 app keeps working while you migrate, so you can run both side by side: new sign-ins through the V2 app, existing users still served by the V1 one until they move. That is the recommended way to do it — see Migrating from V1 to V2.

How many client IDs do I need?

One per app type you actually use — not one per platform. A client type is fixed at registration, so a single registration cannot cover two of them.

Everything through your backend

One client ID. Your backend signs users in, stores tokens and makes every API call. A mobile or TV interface that talks only to your backend does not need its own registration — it never touches Simkl directly.

Different types in one product

Two or three client IDs. Parts of your product sign in differently — an app callback here, a PIN-style device flow there, a server callback somewhere else. Each type needs its own registration.
The question is not “how many platforms do I ship?” but “how many different ways does my code obtain a token?”. If every platform funnels through one backend, that is one way and one client ID.
Three parts, three different sign-in mechanics, so three registrations:Start with the type you need now and add the others when you need them — there is no benefit to registering all three up front.
A user access token only works with the client_id it was issued for. Registrations are entirely separate: a token minted for client ID A is rejected by B and C.So a user of a multi-client product authorizes each one independently. Someone who signs in on your mobile app has not signed in on your TV app — that is a separate grant, and they will see a separate consent screen.This is the strongest argument for the single-backend shape above when it fits your product: one registration, one grant per user, one place tokens live.
The three-registration example above fits inside the smallest allowance. Unverified apps are capped at 3 on Free, 5 on PRO and 10 on VIP — counted separately for AUTH V1 and AUTH V2, so the V1 apps you are migrating from never consume a V2 slot.Verified apps do not count at all, and verification is free on every plan. See Set up authentication.
Two domains, two roles. OAuth V2 uses two different hosts — easy to mix up, and the most common cause of “404 Not Found” during integration:Only the consent page lives on simkl.com. If your authorize URL points at api.simkl.com you’ll get a 404./oauth2/authorize is the odd one out. The other three are requests your code makes and reads a response from. This one is a destination you hand to a browser — open it with the platform’s web-auth session, or redirect to it. Fetching it yourself gets you a login redirect, not a code.Setting these four URLs directly is fine and is what the examples here do. If your library prefers it, all four are also listed in Simkl’s discovery document, which resolves them from the single issuer setting https://simkl.com.

What you get back

Every successful exchange returns the same shape, whichever flow produced it:
Both tokens are 43 characters and carry a recognisable prefix — simkl_at_ for access, simkl_rt_ for refresh. Size your storage columns accordingly, and treat both as secrets.
Credentials found on GitHub are revoked automatically. The simkl_ prefixes exist so that automated secret scanning can recognise a leaked Simkl credential on sight, and each one carries a checksum so a scanner can tell a real credential from a random string before acting.If a client secret, access token or refresh token is committed to GitHub, it is revoked — including in private repositories. Never commit or paste one, and treat a leak as something that will be caught rather than something you might get away with.Your client_id is different: it is public and safe to ship. Putting it in your source code, your JavaScript bundle or a public repository is expected for every client type — that is what it is for. Only the secret and the user tokens need protecting.The reason it is safe is structural: a V2 client_id on its own reaches nothing worth stealing. Every user-scoped endpoint refuses it outright without an access token, so there is no library to read, nothing to write, and no per-user quota for a stranger to burn through. The only thing it still opens is public catalog data — the same summaries and data files anyone can fetch — which is cached, free, and does not count against anyone’s allowance.
If a secret does leak, rotate it rather than registering a new app. A Server apps & services registration can regenerate its secret from developer settings; the new one is shown once, the old one dies immediately, and existing user grants survive — your users do not have to re-authorize. The secret only authenticates your app at the token endpoint, so rotating it stops a leaked secret being used to refresh without disturbing anyone who has already connected.

Token lifetimes at a glance

Full detail, including the two refresh behaviours that surprise people, is on Tokens and refresh.

What the user sees

Your grant is not invisible to the person who approved it. Connected Apps shows every user, for every app they have connected: Two consequences worth designing for. Asking for media:write when you only read is visible, and sits next to apps that asked for less. And when a user says your app stopped syncing, that page settles it faster than any log you have: the allowance is theirs and shared across every app they run, so the cause is frequently another integration entirely — which you cannot see and they can.

Discovery — let your library configure itself

Simkl publishes a small JSON file listing its own endpoints and capabilities — the standard RFC 8414 “discovery document”, at /.well-known/oauth-authorization-server. It describes V2. Most OAuth libraries know how to read it, which means you do not have to configure the authorize URL, the token URL and the supported auth methods yourself. Give the library one setting instead — https://simkl.com — and it fetches the rest. Libraries usually call that setting the issuer:
If your library looks 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 the OIDC path returns 404 and the document you want lives at /.well-known/oauth-authorization-server. Libraries that try both in turn — Spring Security, for one — fall through on their own. Libraries that try only the one you selected need a flag: { algorithm: "oauth2" } in openid-client, for example. A 404 during setup usually means the library is looking at the OIDC path and has no fallback.
Give Spring the issuer with no trailing slash. issuer-uri: https://simkl.com resolves; https://simkl.com/ does not. RFC 8414 requires the issuer in the document to match the one you asked for character for character, and the two differ — so the trailing slash turns a working configuration into “Unable to resolve Configuration with the provided Issuer”.And PKCE is not automatic for a client with a secret. Spring adds PKCE by default only for clients that have no secret, but Simkl requires it from every client type — so a Spring app configured with a client-secret reaches the token endpoint without a code_verifier and gets 400 invalid_request. Opt in explicitly:
If your library does not support discovery at all, nothing is lost — set https://simkl.com/oauth2/authorize and https://api.simkl.com/oauth2/token directly, as listed above. Discovery saves you typing, not capability.
The Node and Python snippets here were run against production on 2026-09-18 and configure themselves correctly from this document. The Java one is documentation-checked only. Neither covers the browser half of the flow, which needs a human — see Client libraries for the per-library detail and the current status of each.If one of the above does not work for you, tell us and send the capture — that is how the V1 matrix got built.

See also

OAuth flow

The full walkthrough with PKCE, including the failures that are hardest to diagnose.

Device / PIN flow

RFC 8628, end to end — polling rules, expiry, and what the user sees.

Scopes

media:read and media:write, and why omitting scope gives you read-only.

PKCE in V2

S256 only. How to generate the verifier and challenge correctly.

Client libraries

Per-library configuration, and the three defaults that need changing.

Migrating from V1

What actually changes, and what doesn’t.

AUTH V1

The original flow. Being retired around April 2027.

Errors

The OAuth 2.0 error vocabulary, and the envelope that differs.