{
"access_token": "YOUR_ACCESS_TOKEN",
"token_type": "bearer",
"scope": "public"
}Step 2 of OAuth 2.0. POST your authorization code here to receive an access_token. The success response carries {access_token, token_type: "bearer", scope: "public", expires_in: 157680000} — about 5 years. Save the token securely. No refresh token is issued; if a 401 arrives before that lifetime (user revoked from Connected Apps), send the user back through /oauth/authorize for a fresh consent.
The endpoint accepts both application/x-www-form-urlencoded (the RFC 6749 §3.2 default that most OAuth libraries use) and application/json — pick whichever your HTTP client prefers. Client credentials can be sent in either the request body (client_id + client_secret) or the Authorization: Basic <base64(client_id:client_secret)> header (RFC 6749 §2.3.1). All four combinations are equivalent. Off-the-shelf OAuth libraries work out-of-the-box with default configuration; see OAuth client libraries for live-tested examples across every popular runtime.
Two flows share this endpoint, distinguished by which secret you send:
client_secret + redirect_uri.code_verifier instead — no secret required. See the Public PKCE walkthrough.| Field | Required | Notes |
|---|---|---|
grant_type | yes | Must be authorization_code. |
code | yes | Authorization code returned to your redirect_uri (or, for PKCE-without-registered-URI, displayed on simkl.com). |
client_id | yes (in body or Basic Auth header) | Your app’s client_id. |
client_secret | conditional | Confidential flow only. Mutually exclusive with code_verifier. May be sent in the body or in the Authorization: Basic header. |
code_verifier | conditional | PKCE flow only. The original verifier you generated locally; Simkl re-derives the challenge and matches it against what you sent on /oauth/authorize. |
redirect_uri | conditional | Required on the confidential flow (must match the URL registered for your app byte-for-byte). On PKCE, required only if you sent one to /oauth/authorize — and then it must match that one. |
All failures return JSON with an error field (and usually a message field too). 401 responses additionally carry an RFC 6750 §3 WWW-Authenticate: Bearer realm="api.simkl.com", error="..." header.
| Status | error | When |
|---|---|---|
| 403 | empty_field | A required body field is missing (code, client_id, grant_type, or both client_secret/code_verifier). |
| 403 | redirect_failed | redirect_uri doesn’t match the URL registered for the app. |
| 401 | secret_error | Wrong client_secret (confidential flow) or PKCE verification failed (message: "PKCE verification failed"). |
| 401 | grant_error | The code is invalid, expired, or already used. Codes are single-use — restart from /oauth/authorize. |
Confidential-client (server-side) flow.
Public-client flow with code_verifier.
{
"access_token": "YOUR_ACCESS_TOKEN",
"token_type": "bearer",
"scope": "public"
}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.
Optional alias for the client_id query parameter. Simkl accepts your client_id either as the simkl-api-key request header or as the ?client_id=… query parameter — pick one. The query-parameter form is preferred because it makes the request fully self-describing in URL form.
Descriptive identifier for your app, ideally name/version. Examples: PlexMediaServer/1.43.1.10540, kodi-simkl/0.9.2, MyApp/2.4.1 (https://myapp.com).
Your client_id from your Simkl developer settings. Required on every request.
Short, lowercase identifier for your app (e.g. plex-scrobbler, kodi-bridge). Helps Simkl identify which apps are using the API.
Your app's current version (e.g. 1.0, 2.4.1). Helps Simkl debug issues you report.
Body for POST /oauth/token. Send grant_type=authorization_code plus the code you received at redirect_uri. Either client_secret (standard flow) or code_verifier (PKCE) must be present.
Authorization code returned to your redirect_uri.
Your application's client_id.
URL where the user was sent back with the code. Required on the confidential flow. On PKCE, required only if you sent one to /oauth/authorize. When sent, must match a URL registered for the app byte-for-byte (scheme, host, port, path, trailing slash, casing).
"https://yourdomain.com/oauth.html"
authorization_code PKCE code verifier (43–128 unreserved characters). Required on the PKCE flow — mutually exclusive with client_secret. Send the original verifier; Simkl re-derives the SHA-256 challenge and matches it against the code_challenge you sent on /oauth/authorize. On mismatch the response is 401 secret_error with "PKCE verification failed" in the message field.
Valid code will generate you the access_token
Successful response from POST /oauth/token. Carries access_token, token_type: bearer, scope: public, and expires_in: 157680000 (5 years). Notable omission vs RFC 6749 §5.1: no refresh_token — Simkl tokens are long-lived but not refreshable. A 401 on a subsequent authenticated call means the user revoked the app; re-run OAuth.
Long-lived bearer token. Save it securely; there is no refresh-token equivalent.
"abc123def456"
Always bearer (lowercase, despite RFC 6750's canonical Bearer capitalization). Use case-insensitive matching when comparing.
"bearer"
Currently always public. Simkl does not have a scope system — tokens implicitly grant all permissions the user has approved for the app. The scope field is a placeholder kept for compatibility with OAuth 2.0 token-response shape; ignore its value.
public "public"
Token lifetime in seconds (RFC 6749 §5.1). Always 157680000 (about 5 years). The value is effectively infinite for practical session lengths — Simkl tokens remain valid until the user revokes the app from Connected Apps settings. There is no refresh_token; if a 401 arrives before this lifetime expires, the user revoked your app — re-run the OAuth flow.
157680000
Was this page helpful?