curl --request GET \
--url 'https://api.simkl.com/sync/playback?client_id=' \
--header 'Authorization: Bearer <token>' \
--header 'User-Agent: <user-agent>'import requests
url = "https://api.simkl.com/sync/playback?client_id="
headers = {
"User-Agent": "<user-agent>",
"Authorization": "Bearer <token>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {'User-Agent': '<user-agent>', Authorization: 'Bearer <token>'}
};
fetch('https://api.simkl.com/sync/playback?client_id=', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));const options = {
method: 'GET',
headers: {'User-Agent': '<user-agent>', Authorization: 'Bearer <token>'}
};
fetch('https://api.simkl.com/sync/playback?client_id=', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));val client = OkHttpClient()
val request = Request.Builder()
.url("https://api.simkl.com/sync/playback?client_id=")
.get()
.addHeader("User-Agent", "<user-agent>")
.addHeader("Authorization", "Bearer <token>")
.build()
val response = client.newCall(request).execute()import Foundation
let url = URL(string: "https://api.simkl.com/sync/playback")!
var components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
let queryItems: [URLQueryItem] = [
URLQueryItem(name: "client_id", value: ""),
]
var queryAllowed = CharacterSet.urlQueryAllowed
queryAllowed.remove(charactersIn: "&=+#")
let encodedQuery = queryItems.map { "\($0.name.addingPercentEncoding(withAllowedCharacters: queryAllowed)!)=\(($0.value ?? "").addingPercentEncoding(withAllowedCharacters: queryAllowed)!)" }.joined(separator: "&")
components.percentEncodedQuery = components.percentEncodedQuery.map { $0 + "&" + encodedQuery } ?? encodedQuery
var request = URLRequest(url: components.url!)
request.httpMethod = "GET"
request.timeoutInterval = 10
request.allHTTPHeaderFields = [
"User-Agent": "<user-agent>",
"Authorization": "Bearer <token>"
]
let (data, _) = try await URLSession.shared.data(for: request)
print(String(decoding: data, as: UTF8.self))HttpResponse<String> response = Unirest.get("https://api.simkl.com/sync/playback?client_id=")
.header("User-Agent", "<user-agent>")
.header("Authorization", "Bearer <token>")
.asString();const url = 'https://api.simkl.com/sync/playback?client_id=';
const options = {
method: 'GET',
headers: {'User-Agent': '<user-agent>', Authorization: 'Bearer <token>'}
};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.simkl.com/sync/playback?client_id="
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("User-Agent", "<user-agent>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}using RestSharp;
var options = new RestClientOptions("https://api.simkl.com/sync/playback?client_id=");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("User-Agent", "<user-agent>");
request.AddHeader("Authorization", "Bearer <token>");
var response = await client.GetAsync(request);
Console.WriteLine("{0}", response.Content);
using RestSharp;
var options = new RestClientOptions("https://api.simkl.com/sync/playback?client_id=");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("User-Agent", "<user-agent>");
request.AddHeader("Authorization", "Bearer <token>");
var response = await client.GetAsync(request);
Console.WriteLine("{0}", response.Content);
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.simkl.com/sync/playback?client_id=",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"User-Agent: <user-agent>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}require 'uri'
require 'net/http'
url = URI("https://api.simkl.com/sync/playback?client_id=")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["User-Agent"] = '<user-agent>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body$headers=@{}
$headers.Add("User-Agent", "<user-agent>")
$headers.Add("Authorization", "Bearer <token>")
$response = Invoke-WebRequest -Uri 'https://api.simkl.com/sync/playback?client_id=' -Method GET -Headers $headersCURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "GET");
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, stdout);
curl_easy_setopt(hnd, CURLOPT_URL, "https://api.simkl.com/sync/playback?client_id=");
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "User-Agent: <user-agent>");
headers = curl_slist_append(headers, "Authorization: Bearer <token>");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
CURLcode ret = curl_easy_perform(hnd);CURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "GET");
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, stdout);
curl_easy_setopt(hnd, CURLOPT_URL, "https://api.simkl.com/sync/playback?client_id=");
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "User-Agent: <user-agent>");
headers = curl_slist_append(headers, "Authorization: Bearer <token>");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
CURLcode ret = curl_easy_perform(hnd);[
{
"id": 123,
"progress": 45.5,
"paused_at": "2024-01-15T10:30:00.000Z",
"type": "episode",
"episode": {
"season": 1,
"episode": 5,
"title": "Episode 5",
"tvdb_season": 1,
"tvdb_number": 5
},
"show": {
"title": "Breaking Bad",
"year": 2008,
"ids": {
"simkl": 12345,
"slug": "breaking-bad",
"tmdb": "1429",
"imdb": "tt0903747"
}
}
},
{
"id": 124,
"progress": 75,
"paused_at": "2024-01-15T11:15:00.000Z",
"type": "movie",
"movie": {
"title": "Inception",
"year": 2010,
"ids": {
"simkl": 67890,
"slug": "inception",
"tmdb": "27205",
"imdb": "tt1375666"
}
}
}
]{
"error": "user_token_failed",
"code": 401
}{
"error": "id_err",
"code": 404
}{
"error": "client_id_failed",
"code": 412,
"message": "Your client_id is wrong. Try another one"
}{
"error": "rate_limit",
"code": 429
}{
"error": "internal",
"code": 500
}Get paused playback sessions for every type
Returns the user’s paused playback sessions across all types (movies, shows, anime). For a single type see GET /sync/playback/{type}.
Useful for “Continue Watching” rails that mix types.
Which IDs can I send/expect? All accepted input identifiers and the keys you’ll see echoed back in responses are listed at Standard media objects → Supported ID keys. Send every ID you have on writes — Simkl picks the first that resolves and ignores the rest. Reminder: slug is response-only (never send it on a request).
Already-watched items can appear here
A paused playback is returned even when the user has already finished that title, as long as the pause happened after they finished it. That is a re-viewing in progress, not stale data, and hide_watched=true (the default) deliberately does not filter it out.
So a Continue Watching row built from this endpoint can contain a movie or episode the user has completed before. If your UI also shows a “watched” checkmark, expect both to be true at once — show the resume position anyway.
hide_watched=true only removes playbacks that were overtaken: paused first, then marked watched from somewhere else. Those are genuine leftovers.
This changed in August 2026. Previously a completed movie, or the exact episode the watchlist last pointed at, was hidden regardless of when it was watched, which meant re-viewings never showed up.
curl --request GET \
--url 'https://api.simkl.com/sync/playback?client_id=' \
--header 'Authorization: Bearer <token>' \
--header 'User-Agent: <user-agent>'import requests
url = "https://api.simkl.com/sync/playback?client_id="
headers = {
"User-Agent": "<user-agent>",
"Authorization": "Bearer <token>"
}
response = requests.get(url, headers=headers)
print(response.text)const options = {
method: 'GET',
headers: {'User-Agent': '<user-agent>', Authorization: 'Bearer <token>'}
};
fetch('https://api.simkl.com/sync/playback?client_id=', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));const options = {
method: 'GET',
headers: {'User-Agent': '<user-agent>', Authorization: 'Bearer <token>'}
};
fetch('https://api.simkl.com/sync/playback?client_id=', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));val client = OkHttpClient()
val request = Request.Builder()
.url("https://api.simkl.com/sync/playback?client_id=")
.get()
.addHeader("User-Agent", "<user-agent>")
.addHeader("Authorization", "Bearer <token>")
.build()
val response = client.newCall(request).execute()import Foundation
let url = URL(string: "https://api.simkl.com/sync/playback")!
var components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
let queryItems: [URLQueryItem] = [
URLQueryItem(name: "client_id", value: ""),
]
var queryAllowed = CharacterSet.urlQueryAllowed
queryAllowed.remove(charactersIn: "&=+#")
let encodedQuery = queryItems.map { "\($0.name.addingPercentEncoding(withAllowedCharacters: queryAllowed)!)=\(($0.value ?? "").addingPercentEncoding(withAllowedCharacters: queryAllowed)!)" }.joined(separator: "&")
components.percentEncodedQuery = components.percentEncodedQuery.map { $0 + "&" + encodedQuery } ?? encodedQuery
var request = URLRequest(url: components.url!)
request.httpMethod = "GET"
request.timeoutInterval = 10
request.allHTTPHeaderFields = [
"User-Agent": "<user-agent>",
"Authorization": "Bearer <token>"
]
let (data, _) = try await URLSession.shared.data(for: request)
print(String(decoding: data, as: UTF8.self))HttpResponse<String> response = Unirest.get("https://api.simkl.com/sync/playback?client_id=")
.header("User-Agent", "<user-agent>")
.header("Authorization", "Bearer <token>")
.asString();const url = 'https://api.simkl.com/sync/playback?client_id=';
const options = {
method: 'GET',
headers: {'User-Agent': '<user-agent>', Authorization: 'Bearer <token>'}
};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.simkl.com/sync/playback?client_id="
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("User-Agent", "<user-agent>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}using RestSharp;
var options = new RestClientOptions("https://api.simkl.com/sync/playback?client_id=");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("User-Agent", "<user-agent>");
request.AddHeader("Authorization", "Bearer <token>");
var response = await client.GetAsync(request);
Console.WriteLine("{0}", response.Content);
using RestSharp;
var options = new RestClientOptions("https://api.simkl.com/sync/playback?client_id=");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("User-Agent", "<user-agent>");
request.AddHeader("Authorization", "Bearer <token>");
var response = await client.GetAsync(request);
Console.WriteLine("{0}", response.Content);
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.simkl.com/sync/playback?client_id=",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"User-Agent: <user-agent>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}require 'uri'
require 'net/http'
url = URI("https://api.simkl.com/sync/playback?client_id=")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["User-Agent"] = '<user-agent>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body$headers=@{}
$headers.Add("User-Agent", "<user-agent>")
$headers.Add("Authorization", "Bearer <token>")
$response = Invoke-WebRequest -Uri 'https://api.simkl.com/sync/playback?client_id=' -Method GET -Headers $headersCURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "GET");
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, stdout);
curl_easy_setopt(hnd, CURLOPT_URL, "https://api.simkl.com/sync/playback?client_id=");
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "User-Agent: <user-agent>");
headers = curl_slist_append(headers, "Authorization: Bearer <token>");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
CURLcode ret = curl_easy_perform(hnd);CURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "GET");
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, stdout);
curl_easy_setopt(hnd, CURLOPT_URL, "https://api.simkl.com/sync/playback?client_id=");
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "User-Agent: <user-agent>");
headers = curl_slist_append(headers, "Authorization: Bearer <token>");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
CURLcode ret = curl_easy_perform(hnd);[
{
"id": 123,
"progress": 45.5,
"paused_at": "2024-01-15T10:30:00.000Z",
"type": "episode",
"episode": {
"season": 1,
"episode": 5,
"title": "Episode 5",
"tvdb_season": 1,
"tvdb_number": 5
},
"show": {
"title": "Breaking Bad",
"year": 2008,
"ids": {
"simkl": 12345,
"slug": "breaking-bad",
"tmdb": "1429",
"imdb": "tt0903747"
}
}
},
{
"id": 124,
"progress": 75,
"paused_at": "2024-01-15T11:15:00.000Z",
"type": "movie",
"movie": {
"title": "Inception",
"year": 2010,
"ids": {
"simkl": 67890,
"slug": "inception",
"tmdb": "27205",
"imdb": "tt1375666"
}
}
}
]{
"error": "user_token_failed",
"code": 401
}{
"error": "id_err",
"code": 404
}{
"error": "client_id_failed",
"code": 412,
"message": "Your client_id is wrong. Try another one"
}{
"error": "rate_limit",
"code": 429
}{
"error": "internal",
"code": 500
}Authorizations
Preferred form: your client_id as a URL query parameter on every request. Self-describing in logs and curl commands. See Headers and required parameters.
A user access_token, sent as Authorization: Bearer .... Required for endpoints that read or modify the user's library, scrobble session, ratings, settings, or playbacks. Every flow returns the same kind of token. See Set up authentication.
Headers
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).
Query Parameters
Slice your result to the first N items. Default: 10000
Controls whether playbacks for items the user has already finished are filtered out.
true (default) hides a paused playback only when the item was watched at or after the moment it was paused — a stale leftover, e.g. the user paused at 40% and then marked the whole thing watched from another app.
It does not hide a playback that was paused after the item was finished. That is a re-viewing in progress, and it is returned even with hide_watched=true. A user who finished a film years ago and is 45% into watching it again gets that playback back in the list, which is what a Continue Watching row should show.
false skips the check entirely, including the stale-leftover case.
One edge case: for items whose watch date is the "watched, date unknown" placeholder there is no date to compare, so true falls back to hiding completed movies and the specific episode the watchlist last pointed at.
true, false Filter sessions from this date
Filter sessions until this date
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.
Response
OK
Internal playback session ID. Use to delete via DELETE /sync/playback/{id}.
Saved progress percentage 0–100.
Standard movie object. See the Standard Media Objects guide.
Hide child attributes
Hide child attributes
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
"153021"
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{ "simkl": 53536, "imdb": "tt0181852", "tmdb": "296" }
"Terminator 3: Rise of the Machines"
2003
Standard show object. May include nested seasons/episodes for partial sync.
Hide child attributes
Hide child attributes
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
"153021"
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{ "simkl": 53536, "imdb": "tt0181852", "tmdb": "296" }
"The Walking Dead"
2010
Hide child attributes
Hide child attributes
Hide child attributes
Hide child attributes
x >= 01
x >= 12
ISO-8601 GMT timestamp.
"2014-09-01T09:10:11Z"
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
"153021"
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{ "simkl": 53536, "imdb": "tt0181852", "tmdb": "296" }
Standard anime object. Like Show, but may include anime_type. Episode numbering follows AniDB.
Hide child attributes
Hide child attributes
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
"153021"
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{ "simkl": 53536, "imdb": "tt0181852", "tmdb": "296" }
"Attack on Titan"
2013
Anime production format. tv is the common case; movies, OVAs, ONAs, and music videos all surface through the /anime/* endpoints with the same response shape.
tv, movie, special, ova, ona, music video Hide child attributes
Hide child attributes
x >= 01
x >= 12
ISO-8601 GMT timestamp.
"2014-09-01T09:10:11Z"
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
"153021"
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{ "simkl": 53536, "imdb": "tt0181852", "tmdb": "296" }
Episode reference. Use season + number, or ids.
Hide child attributes
Hide child attributes
x >= 01
x >= 12
ISO-8601 GMT timestamp.
"2014-09-01T09:10:11Z"
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
"153021"
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{ "simkl": 53536, "imdb": "tt0181852", "tmdb": "296" }
Was this page helpful?