Skip to main content
POST
/
sync
/
history
curl -X POST 'https://api.simkl.com/sync/history?client_id=YOUR_CLIENT_ID&app-name=my-app-name&app-version=1.0' \ -H 'User-Agent: my-app-name/1.0' \ -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ -H 'Content-Type: application/json' \ -d '{ "movies": [ { "watched_at": "2026-05-15T22:30:00Z", "title": "Dune: Part Two", "year": 2024, "ids": { "simkl": 1015859 } }, { "watched_at": "2026-05-14T20:00:00Z", "title": "Oppenheimer", "year": 2023, "ids": { "imdb": "tt15398776", "tmdb": "872585" } } ], "shows": [ { "title": "The Last of Us", "year": 2023, "ids": { "simkl": 1411674 }, "seasons": [ { "number": 1, "episodes": [ { "number": 1, "watched_at": "2026-05-13T19:00:00Z" }, { "number": 2, "watched_at": "2026-05-13T20:00:00Z" }, { "number": 3, "watched_at": "2026-05-14T18:30:00Z" } ] } ] }, { "title": "Breaking Bad", "year": 2008, "ids": { "imdb": "tt0903747" }, "status": "completed", "rating": 10 } ] }'
{ "added": { "movies": 2, "shows": 2, "episodes": 65, "statuses": [ { "request": { "watched_at": "2026-05-15T22:30:00Z", "title": "Dune: Part Two", "year": 2024, "ids": { "simkl": 1015859 }, "type": "movie", "rating": null }, "response": { "status": "completed", "simkl_type": "movie", "anime_type": null } }, { "request": { "watched_at": "2026-05-14T20:00:00Z", "title": "Oppenheimer", "year": 2023, "ids": { "imdb": "tt15398776", "tmdb": "872585" }, "type": "movie", "rating": null }, "response": { "status": "completed", "simkl_type": "movie", "anime_type": null } }, { "request": { "title": "The Last of Us", "year": 2023, "ids": { "simkl": 1411674 }, "type": "show", "rating": null }, "response": { "status": "watching", "simkl_type": "tv", "anime_type": null } }, { "request": { "title": "Breaking Bad", "year": 2008, "ids": { "imdb": "tt0903747" }, "status": "completed", "rating": 10, "type": "show" }, "response": { "status": "completed", "simkl_type": "tv", "anime_type": null } } ] }, "not_found": { "movies": [], "shows": [], "episodes": [] } }

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.

Authorizations

client_id
string
query
default:YOUR_CLIENT_ID
required

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.

Authorization
string
header
default:YOUR_ACCESS_TOKEN
required

OAuth 2.0 or PIN-flow access_token. Required for endpoints that read or modify the user's library, scrobble session, ratings, settings, or playbacks. See Authentication.

Headers

User-Agent
string
required

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

allow_rewatch
enum<string>
default:no

Opt into rewatch tracking. When yes, POST /sync/history records an additional rewatch session instead of being a no-op for already-watched items, and GET /sync/all-items returns one extra entry per saved rewatch session alongside the item's normal entry. Available to Simkl Pro and VIP users — non-Pro callers see no effect even with the flag set.

⚠️ Do not enable this flag until you've read the Rewatches guide end-to-end and implemented the precautions. Used carelessly (on retries, on every scrobble event, on importer re-runs, without pinning rewatch_id after the first write), it will pollute the user's history stats and rewatches panel with phantom sessions. The flag should be gated behind explicit user intent — a dedicated "Rewatch" button — never on background or automated flows. Also expose a per-user Track rewatches toggle in your app's settings (default off) — not every user wants the rewatch-session complexity.

Limits: up to 50 rewatches per item (movie, show, or anime), and any two watch events on the same item (movie or episode) must be at least 2 days apart — a new rewatch within 48 hours of the previous watch of that same item collapses into the same session (it's a rewatch, not a rewind 😄). Full walkthrough — session lifecycle (active / completed / closed with bidirectional transitions), episode-level tracking, reading sessions back from GET /sync/all-items, and ready-made code for simkl.com-style UI patterns — in the Rewatches guide.

Available options:
yes,
no
skip_auto_watching
enum<string>

When yes, suppresses the implicit episode auto-fill that happens when you POST a show without explicit seasons/episodes. Use when the client manages episode-level state itself.

Available options:
yes
client_id
string
required

Your client_id from your Simkl developer settings. Required on every request.

app-name
string
required

Short, lowercase identifier for your app (e.g. plex-scrobbler, kodi-bridge). Helps Simkl identify which apps are using the API.

app-version
string
required

Your app's current version (e.g. 1.0, 2.4.1). Helps Simkl debug issues you report.

Body

application/json

Body for /sync/history and /sync/history/remove. Items go under movies[], shows[], or anime[] — Simkl resolves anime titles correctly under either shows[] or anime[], so match the field to your data type when known.

movies
object[]
shows
object[]
episodes
object[]

Top-level convenience array for marking episode-level history without nesting. The server wraps each entry into a synthetic single-season show. Each item carries the same shape as items under shows[].seasons[].episodes[] plus the parent show reference.

anime
object[]

Array of anime entries (same shape as shows[]).

Response

Success

Response from POST /sync/history. Note: not_found.shows includes any anime entries that failed to resolve, regardless of whether they were sent under anime[] or shows[] — there is no separate not_found.anime bucket.

added
object
not_found
object

Items Simkl could not match. Inspect this to surface diagnostics back to your users. Anime entries that fail to resolve land in shows regardless of which top-level array they were sent under — there is no separate anime bucket in not_found.