Before you start
- Register a new app. Your V1
client_idcannot be upgraded. You need a newclient_idand a fixed client type. - Plan for user action. Every existing user must authorise your V2 app once. V1 tokens cannot be exchanged for V2 tokens.
- Check browsing before sign-in. Public catalogue lookups still work anonymously, but search and user-scoped requests need a token.
What you are gaining
Custom Lists is the first API that requires AUTH V2. New APIs will also require V2.
How per-user quotas change capacity
How per-user quotas change capacity
The quota change is the one that matters as you grow. A V1 app has a fixed ceiling however many users it has, so one heavy user or a runaway sync loop eats the budget everyone else needed. In V2 ten thousand users bring ten thousand allowances — capacity grows with your user base rather than being divided between it, and a noisy user can no longer affect anyone else. See Rate limits.
Client IDs, secrets and token security
Client IDs, secrets and token security
That is also why V2 client IDs are safe to publish. Every user-scoped V2 endpoint refuses a bare
client_id, so there is no library to read, nothing to write, and no per-user quota to burn — ship yours in your source code, your JavaScript bundle or a public repository, as intended for every client type. All a stranger gets is public catalog data that is free and uncounted anyway.The security story is better in both directions. A V2 access token that leaks is useless after seven days, where a V1 token would have stayed valid for five years. PKCE makes a stolen authorization code unusable without the matching verifier. And if your client_secret ever leaks you can rotate it from developer settings without disturbing a single user — existing grants keep working, because the secret only authenticates your app at the token endpoint.Configure an OAuth library with discovery
Configure an OAuth library with discovery
The library setup is also simpler. Simkl publishes a small JSON file listing its own endpoints and capabilities — the standard “discovery document” at Setup snippets for Node, Python and Java are on the V2 overview, along with the one flag some libraries need to look for the OAuth 2.0 document rather than the OpenID Connect one.If your library does not support discovery, nothing is lost — set the two endpoints directly as you do today.
/.well-known/oauth-authorization-server. Most OAuth libraries know how to read it.So instead of setting the authorize URL, the token URL and the supported auth methods yourself, you give the library one setting — https://simkl.com — and it fetches the rest. Libraries usually call that setting the issuer:The one thing that might be hard
Decide how browsing before sign-in will work. V2 limits anonymous requests to public catalogue data.
Review anonymous access before planning the implementation. If this makes migration difficult, tell us; temporary limit increases or more time may help.
What changes
What does not change
Everything that is not authentication. The API itself is identical — same endpoints, same request and response shapes, same conventions. A V2 token goes in the sameAuthorization: Bearer header a V1 token did, and every call you already make keeps working unchanged once it carries one.
So the migration is genuinely scoped to your auth code. You are not rewriting your sync logic.
The one adjacent thing that does change is how your quota is counted — per user rather than per app. That needs no code from you, and it is a change in your favour, but it is worth knowing about if you have capacity monitoring built around the old model. See Rate limits.
The work, in order
Register your V2 app
Register a new app to get a new This is a second app, not an edit to the first. Your V1 app keeps working the whole time, which is what lets you run both side by side while users move across.Client type is fixed at registration and cannot be changed either, so pick deliberately:
client_id, plus a new client_secret if you register it as a server app. Your V1 client_id cannot be upgraded.V1 credentials are rejected by
/oauth2/* endpoints with 401 invalid_client: “This client_id is not enabled for OAuth 2.0”.Registering the V2 app does not use up a V1 slot, or the reverse. The unverified-app allowance is counted separately for each version — 3 on Free, 5 on PRO, 10 on VIP, per version — so you never have to delete a working V1 app to make room for the V2 one.Verified apps do not count at all, and verification is free. If you are running several V1 apps and need headroom on either side while you migrate, ask us.
Note that any V2 client type can use the device flow — TV, devices & command line is not a permission to use it, it is the type for an app that never performs a browser redirect. If you have a companion app that signs users in through a browser, register Mobile, desktop & browser apps and use both flows from it.
Ship the new
client_id to your users before you need it. Because it is a different credential, a client that only learns the V2 client_id in a later release cannot migrate anyone still running an older build. If your app is distributed rather than server-side, getting the new ID into the field early is the thing most likely to determine whether you finish on time.Decide what happens to anonymous access
Settle this before you write anything. It is the one item that can change your product rather than your code, and the answer may affect everything below it.V1 lets an app make Search is the one that catches people out. It is not in the exempt set, so a browse mode where the user types a title stops working before sign-in, even though fetching a known title by ID still succeeds. If your pre-sign-in experience needs more than known-ID lookups, you either require sign-in for it, or keep that path on your V1 app while the authenticated paths move to V2. Both are legitimate.
client_id-only requests with no signed-in user on any endpoint, within a daily cap. V2 narrows that to public catalog data only — summaries by Simkl ID, episode lists, and the Trending and Calendar files. A user-scoped request without a token returns:Add PKCE
Mandatory for every client type in V2, including server apps. If you used V1’s optional PKCE with
S256 you already have this and it carries over unchanged. If you used plain, or skipped PKCE, this is new code — see PKCE in AUTH V2.Ask for the scope you need
V1 tokens do everything, so there was nothing to request. In V2, omitting
scope gives you read-only, and so does misspelling it. If your app writes, pass scope=media:read media:write and then check the scope in the token response.A migration that appears to succeed and then fails on the first write is usually this.Add refresh handling
A 7-day token needs refreshing; a 5-year one did not. Refresh on a
401 or shortly before expiry.Refreshing replaces the access token for that connection only. Two devices that sign in separately can refresh independently, even when they use the same app and Simkl account.If you copy the same tokens between devices or processes, they share one connection. Independent refreshes can then invalidate each other’s access tokens. Have each device sign in separately, or coordinate refreshes through one owner. See Tokens and refresh.Migrating existing users
There is no token exchange — a V1 token cannot be swapped for a V2 one. Every existing user has to authorize your V2 app once, which makes this the part of the migration that depends on your users rather than on you. Two ways to get there, and they are both supported. Pick whichever suits your app — nothing in Simkl requires you to carry two authentication paths.Run both client IDs side by side
Ship the V2client_id alongside the V1 one and keep both live. Nothing in Simkl objects to this: the two registrations are entirely separate apps, each with its own grants, so a user can hold a V1 token from one and a V2 token from the other at the same time with no conflict. Revoking one leaves the other untouched.
That gives you the shape that makes this painless:
Your app runs on whichever credential it holds for that user, and the prompt to reconnect becomes an invitation rather than a wall. If you take this route, get both credentials working before you write any of the prompt UI — once they do, the deadline stops being a cliff.
You can tell the two tokens apart locally, with no API call. A V2 access token starts with
simkl_at_ and is 43 characters. A V1 token is 64 lowercase hex characters with no prefix. So token.startsWith("simkl_at_") is enough to decide which code path a given user is on, which credential to refresh, and whether to show the prompt.Send tokens exactly as issued — token matching is case-sensitive, so upper- or lower-casing a stored token will silently fail to authenticate.When to prompt
Start quiet and escalate. A reconnect prompt gets roughly one chance at being taken seriously, so spending it early — while everything still works and the user has no reason to act — is how it ends up ignored.
Never interrupt a task. Prompt on launch, on a settings screen, or after something completes — not in front of the thing the user came to do.
Where the prompt goes, by app type
- Mobile / desktop
- Web
- TV / device
- Headless
Put a permanent row in your existing account or settings screen — “Simkl: connected (legacy) — Reconnect”. That row is the canonical destination, and it matters even when nobody clicks it: a suspicious user who distrusts a banner goes looking for it there, and needs to find it.The banner is the express lane; its button opens the authorization flow directly rather than dumping the user on a settings page to hunt for a second button.Use the platform’s secure web-auth session —
ASWebAuthenticationSession on iOS, Custom Tabs on Android — never an embedded WebView. On iOS, do not suppress the system’s ”…Wants to Use simkl.com to Sign In” prompt: it is chrome your app cannot spoof, which is exactly what makes it reassuring.Make the prompt trustworthy
This is the part most apps get wrong, and it costs them conversions. “Please sign in again” is also what credential phishing looks like, and users have been trained to distrust it. Being genuine is not enough on its own — you have to look genuine.- Explain the reason. Say “Simkl is retiring the login method this app uses” rather than the vague “your session has expired”.
- Name the destination. Explain that sign-in opens simkl.com in the browser and that your app never sees the password.
- Explain what stays. Reassure users that their watch history stays where it is.
- Give a clear timeframe. Use the announced retirement date when available. Explain what the user needs to do without alarmist wording.
401 can mean the user revoked you from Connected Apps, or that something transient went wrong. If you show “Simkl retired this login” to someone who simply disconnected your app, you are both wrong and alarming. Branch on what you actually know, and keep a neutral message for the rest.
Your app will appear twice in the user’s Connected Apps list while both registrations hold a grant — the two client IDs are separate apps as far as Simkl is concerned. That is expected and harmless, but it surprises people, so it is worth a line in your own help text. Once a user has reconnected, revoke the V1 token you are no longer using and the duplicate disappears.
After the deadline
Degrade by capability, not by locking the app.- Keep local data. Cached lists, history and posters stay visible, marked stale. Losing a token is not losing the account — do not log the user out of your own app, and do not clear the cache.
- Queue writes instead of failing them. “12 changes waiting to sync — reconnect to send them” is a reason to act, and converts far better than a warning. Never discard the queue.
- Suspend background sync rather than retry-storming a token that cannot come back.
- Check the account matches on reconnect. If the user authorizes a different Simkl account, do not silently merge — the queued writes belong to the old one. Ask them.
- Keep the reconnect path forever. People come back months later.
Watch your own numbers rather than guessing. You can see how much of your user base has moved by how many V2 grants your app holds. If the curve looks like it will not finish in time, tell us — we would much rather extend your timeline than break your users.
Cut over to V2 only
Ship a release that only knows V2, and have everyone reconnect at once. No dual credential handling, no prompt schedule, no window where the same endpoint answers two shapes — the V1 path goes away with the migration itself. What you get: one authentication path from day one. Carrying two token formats, two refresh behaviours and two response shapes through a codebase is real work, and it is work with a known expiry date — especially if you are rewriting the auth layer anyway. What it costs: every user hits a signed-out state at the same time. A reconnect screen nobody explained looks identical to a bug, and users who quietly stop opening the app do not appear in any error log. Anything running unattended — a media-server plugin, a scrobbler, a bot, a cron job — stops at the moment of the update and stays stopped until someone notices. Four things make it go smoothly:- Explain it before they see it. Release notes, and a line on the reconnect screen itself: “Simkl has upgraded its sign-in. Reconnect once and you are done.” Without a reason, the screen reads as a failure.
- Do not treat a missing token as a missing account. Keep cached lists, history and queued writes exactly as After the deadline describes. Reconnecting should restore a working app, not an empty one.
- Leave the V1 registration alone. Delete it later, and only once the old builds are genuinely gone. Every user still on the previous version depends on it, and users update slowly — a mobile or desktop app typically has a long tail running releases many months old.
- Expect the support spike in one place. It arrives on release day rather than spread over months, which is easier to staff for but harder to ignore. Have the answer written before you ship.
Traps specific to migrating
Keep discovery tied to V2
Compare token types without case sensitivity
Handle repeat authorisation as a new grant
Each V2 sign-in creates a separate connection, called a grant. If a user signs into the same app on their phone and tablet, each device gets its own access and refresh tokens. Refreshing or revoking the phone’s grant leaves the tablet’s grant working.This depends on each device signing in separately. Copying the same tokens between devices shares one grant instead. Revoke a grant when that connection is no longer needed.
See also
AUTH V2 overview
Client types, lifetimes, and which flow to use.
OAuth flow
The V2 replacement for the V1 OAuth flow.
Device / PIN flow
The V2 replacement for the PIN flow.
AUTH V1
What you are migrating from. Being retired around April 2027.