Skip to main content

Documentation Index

Fetch the complete documentation index at: https://api.simkl.org/llms.txt

Use this file to discover all available pages before exploring further.

There’s no reinventing the wheel here — the API uses OAuth 2.0. Requesting user-associated information requires a token that needs to be included in all request headers made to the API. To obtain the client_id and client_secret, please create an app first.
Two domains, two roles. OAuth uses two different hosts — easy to mix up, and the most common cause of “404 Not Found” during integration:
EndpointHostWhat it does
GET /oauth/authorizesimkl.comBrowser-facing consent page. The user lands here, signs in, and approves your app.
POST /oauth/tokenapi.simkl.comServer-to-server code exchange. Your backend posts the code here and gets back an access_token.
If your authorize URL points at api.simkl.com you’ll get a 404 — it has to be simkl.com.
To make calls on behalf of a user you have to obtain an access_token. To do this, first send the user to https://simkl.com/oauth/authorize to receive a code, then post it in JSON format to https://api.simkl.com/oauth/token. The response contains the access_token.

STEP 1 — Authorize (simkl.com)

Open a URL like the one below in the user’s browser or a Custom Tab (do not use a WebView on mobile):
https://simkl.com/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=http://yourdomain.com/oauth.html&state=RANDOM_CSRF_TOKEN&app-name=my-app-name&app-version=1.0
Simkl will redirect back to your redirect_uri with ?code=... appended (and &state=... if you sent one).
Build this URL against https://simkl.comnot https://api.simkl.com. The API host has no /oauth/authorize page.
Use state for CSRF protection. Generate a random string before redirecting and store it on your session. When the redirect arrives, verify the state echoed back matches what you sent. If it doesn’t, reject the request — someone else may have started a flow on your behalf.
User denial doesn’t return an error= parameter. If the user clicks “No” on the consent screen, Simkl redirects to / on simkl.com — not back to your redirect_uri with error=access_denied. Treat any flow where the redirect never lands within a sensible timeout (e.g. 5 minutes) as a denial / cancellation and let the user retry.

STEP 2 — Exchange code for token (api.simkl.com)

POST the code to https://api.simkl.com/oauth/token with your client_id, client_secret, redirect_uri, and grant_type=authorization_code.
Both content-types and both credential locations work. Simkl’s POST /oauth/token accepts:
  • Content-Type: application/x-www-form-urlencoded (the RFC 6749 §3.2 default) or Content-Type: application/json — pick whichever your HTTP client prefers
  • Client credentials in the request body (client_id + client_secret parameters) or in the Authorization: Basic header (RFC 6749 §2.3.1) — both paths are honored
That means off-the-shelf OAuth libraries work out-of-the-box with no custom encoding or auth-method config. The two equivalent ways to call the token endpoint:
curl -X POST https://api.simkl.com/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "User-Agent: my-app-name/1.0" \
  --data-urlencode "client_id=YOUR_CLIENT_ID" \
  --data-urlencode "client_secret=YOUR_CLIENT_SECRET" \
  --data-urlencode "code=AUTHORIZATION_CODE" \
  --data-urlencode "redirect_uri=YOUR_REDIRECT_URI" \
  --data-urlencode "grant_type=authorization_code"
For library-specific examples (Python, Node, Java, Go, PHP), see OAuth client libraries — most are zero-config.
The successful response contains your access_token:
{
  "access_token": "...",
  "token_type":   "bearer",
  "scope":        "public",
  "expires_in":   157680000
}
expires_in is 5 years in seconds — effectively infinite for any realistic session. There’s no refresh_token; Simkl tokens are long-lived and the API has no refresh-token grant. If a 401 arrives before that lifetime elapses, the user revoked your app at Connected Apps settings — restart the flow from STEP 1.
The authorization code is single-use. As soon as you POST it to /oauth/token, the server deletes it — even if the exchange fails (network error, validation mismatch, etc.). If your exchange fails for any reason, don’t retry with the same code — restart from STEP 1.
Same user, same token. If the same user runs the OAuth flow twice for your app — whether via the standard flow, PKCE, or PIN — Simkl returns the same access_token both times (it increments an internal usage counter but doesn’t rotate the token). Storing the latest response is safe; you don’t need to invalidate older tokens of your own because there aren’t multiple ones.
Public clients (mobile, SPA, browser extensions, desktop binaries) must not embed client_secret. Anything compiled into the user’s app should be considered leaked. Use the Public PKCE flow (code_verifier + code_challenge) instead — same browser-based UX, no secret required.

See also

Choose a flow

Per-platform recommendations, code samples, comparison across all three flows.

Public PKCE

The variant for mobile, SPA, browser extensions, and desktop binaries — same browser UX, no client_secret required.

PIN flow

The alternative for TVs, consoles, watches, CLIs, and media-server plugins — no client_secret and no redirect required.

GET /oauth/authorize

Endpoint reference — every accepted query parameter, including PKCE.

POST /oauth/token

Endpoint reference — body fields, response shape, error codes, interactive playground.