Stop
Finalizes the user’s playback session. The action field in the response tells you what Simkl did:
progress | action | Result |
|---|---|---|
| ≥ 80 | scrobble | Item is marked watched. |
| < 80 | pause | Session is saved as a paused playback. |
When progress < 80, the session is kept as a resumable playback, retrievable cross-device via GET /sync/playback/{type}. When progress ≥ 80, the item is marked watched and no playback is saved. See the Playback overview for retention.
Body shape is identical to /scrobble/start.
Duplicate prevention
Stopping a session that’s already been finalized within the past hour returns 409 Conflict with watched_at and expires_at so you know when the prior scrobble expires:
Three separate windows guard this endpoint, and they are easy to mix up:
| Window | Applies to | What happens |
|---|---|---|
| 20 seconds | Any scrobble call, per user | A second in-flight call collides with the per-user lock and returns 400 with RATE_LIMIT. |
| 1 hour | Re-stopping the same item | Returns 409 Conflict with watched_at / expires_at, as above. The watch is already recorded. |
| 2 days | Rewatches only (?allow_rewatch=yes) | Two watches of the same movie or episode must be at least 2 days apart. Anything closer is absorbed into the existing session and reports rewatch_status: "too_soon". |
The 2-day gap is deliberate: it absorbs player retries, scrubbing back and forth across the 80% mark, and importer re-runs, which would otherwise fill the user’s history with phantom sessions. It is not an error and must not be retried. See The 2-day gap.
Seek and scrub behavior
The ≥80% auto-scrobble rule applies to the progress you send with this call — not to anywhere the user temporarily scrubbed during playback. A user who scrubbed to 95% mid-watch but then rewinds and stops at 30% sends progress: 30, and the server stores action: "pause". Only the value at the moment of stop matters.
Note: A 20-second per-user lock collision returns HTTP
400withRATE_LIMIT, not429— the lock failure is treated as a malformed request from a duplicate-fire client.
Scrobble guide — full walkthrough
Real-time playback tracking — /start, /pause, /stop lifecycle, paused-playback resumption across devices, when scrobble auto-completes, and the difference between /scrobble/checkin (fire-and-forget) and /scrobble/start (active tracking).
Alternative to episode.season + episode.number: pass episode.ids with tvdb or anidb to identify the exact episode by external episode ID. Useful for Plex / media-server integrations that have a TVDB or AniDB episode ID but not the season/number mapping. (Episode-level imdb and tmdb IDs are not accepted — those exist only at the show/movie level. Use the show/anime object’s ids for those.) If both forms are sent, episode.ids takes precedence.
Rewatches
A /scrobble/stop with progress ≥ 80 marks the item watched — but when the user has already completed that item, that write is a no-op: the server detects the duplicate and skips it. To record it as a separate rewatch session instead, send ?allow_rewatch=yes. Detection is automatic from the outcome; there is no per-item flag on this endpoint.
The 80 threshold still gates everything. A rewatch can only exist where a watch does, so a stop below 80 resolves to action: "pause" — resume position saved, nothing marked, and no rewatch_* keys in the response whether or not you sent the flag. If rewatch_status is missing, read action first: "pause" means the progress was too low, not that the rewatch was rejected.
Send the flag on /scrobble/stop or /scrobble/checkin — never on /start or /pause. Those two mark nothing watched, so they can’t produce a session for the item in the request. Worse, /start auto-scrobbles the user’s previous outstanding playback once its progress passes 80%, and that path honours the flag: the previous row may belong to a different title, and no response field reports it, so the flag on /start can silently open a rewatch on something the user isn’t looking at.
Simkl PRO / VIP only. Free-tier callers get rewatch_status: "pro_required" and no session — unlike POST /sync/history, which fails silently, so you can read the reason straight off the response. Still worth gating client-side: check account.type from GET /users/settings at sign-in and enable the flag only for "pro" / "vip". Cache that value; refetch only when activities.settings.all from GET /sync/activities bumps. Expose a per-user Track rewatches toggle that defaults to off. Full pattern in the Rewatches guide.
When the flag is set, two keys are added to a scrobble response:
| Key | Notes |
|---|---|
rewatch_id | The session this watch landed on, or null. A session that is already running reports its id even when it rejected this watch — read rewatch_status, never the presence of an id, to find out whether the watch was recorded. |
rewatch_status | What happened to this watch. |
rewatch_status | Meaning |
|---|---|
active | Recorded on a session that is still in progress. Typical for TV / anime. |
completed | Recorded on a session that is now complete. Movie sessions are created complete, so a movie rewatch reports completed, not active. |
closed | Recorded on a session that was manually closed or left partial. |
first_watch | Not a re-viewing. This is the original watch, and no session was created. |
too_soon | Rejected by the 2-day gap required between two watches of the same item. |
not_eligible | The item’s watchlist status is plan-to-watch. |
pro_required | The account is not Simkl PRO or VIP. |
null | The watch itself failed to record, so no rewatch was attempted. |
On /scrobble/checkin these keys are absent, because a check-in doesn’t mark anything watched at request time. The flag is remembered on the check-in and applied when it matures. You find out what happened by reading the sessions back from GET /sync/all-items?allow_rewatch=yes, not from the response.
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
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.
Opt into rewatch tracking. When yes and the account is Simkl PRO or VIP, a /scrobble/stop with progress ≥ 80 on an already-completed item records a separate rewatch session instead of being a no-op, and the response gains rewatch_id + rewatch_status. Below 80 the stop is a pause, so nothing is marked and no rewatch fields are returned.
Send this on /scrobble/stop only. The flag is accepted here for consistency, but on /scrobble/start and /scrobble/checkin it applies to the user's previous outstanding playback rather than the item in this request, and nothing in the response reports that. See the /scrobble/stop description for the full rule.
Also supported on POST /scrobble/checkin, where the opt-in is stored on the check-in and applied later, when the check-in matures into a watch. The check-in response carries no rewatch fields because nothing has happened yet.
Read the Rewatches guide before enabling this. Gate it behind explicit user intent, a dedicated "Rewatch" button, never on automatic scrobble events, and expose a per-user Track rewatches toggle in your app settings that defaults to off. Used carelessly it pollutes the user's history stats and rewatches panel with phantom sessions.
yes, no Body
- Movie
- TV episode
- Anime episode
Request body for the four scrobble endpoints. Send exactly one of movie, show+episode, or anime+episode — all keys are singular objects (one item per request). For batch sync writes the keys are plural arrays: see /sync/history etc. progress is required for /scrobble/start, /scrobble/pause, and /scrobble/stop; on /scrobble/checkin it is optional and ignored server-side.
Anime under show? Yes — if you don't know whether a title is anime (e.g. you only have TMDB/TVDB data), send it under show and Simkl resolves it to the correct catalog automatically. The anime key is only needed when you want to pass anidb / mal / anilist IDs that don't exist at the TV-show level.
Movie reference. Use ids (any one external ID) and optionally title / year. Movies have no episode block.
Playback percentage 0-100. Up to 2 decimals. Required for /scrobble/start, /scrobble/pause, /scrobble/stop. Optional (and ignored) for /scrobble/checkin.
0 <= x <= 100TV show reference (must be paired with episode).
Anime reference. Pair with episode for series; an anime alone (no episode) is treated as an anime movie / OVA.
Episode identifier. Use season + number, OR ids with a tvdb / anidb episode ID. If both are sent, episode.ids takes precedence (matches Plex-style integrations that have an episode ID but no season/number mapping).
- By season + number
- By episode ID
Response
Created
Response for /scrobble/start, /scrobble/pause, /scrobble/stop and /scrobble/checkin.
rewatch_id and rewatch_status are present only on a scrobble action (a /scrobble/stop with progress >= 80) that was sent with ?allow_rewatch=yes. On every other call they are absent entirely.
Playback event id for this call. 0 on /scrobble/start (the handler only assigns an id once the session is no longer just opening). Pass it to the playback-removal endpoint to undo a stop.
start for /start, pause for /pause and for /stop with progress<80, scrobble for /stop with progress≥80 (item marked watched).
start, pause, scrobble Echo of the request progress, normalized.
Standard movie object. See the Standard Media Objects guide.
Standard show object. May include nested seasons/episodes for partial sync.
Standard anime object. Like Show, but may include anime_type. Episode numbering follows AniDB.
Episode object returned by the /scrobble/* endpoints. This is not the same shape as the Episode object accepted in sync request bodies: it carries a resolved title, and it has no ids map.
Type 4 null — the watch did not land on a rewatch session. See Null and missing values.
The rewatch session this watch was recorded on. Present only when ?allow_rewatch=yes was sent and action is scrobble — otherwise the key is absent entirely.
A session that is already running reports its id even when it rejected this watch (for example inside the 2-day gap), so this field alone does not tell you the watch was recorded. Read rewatch_status for that.
Type 4 null — the watch itself failed to record, so no rewatch was attempted. See Null and missing values.
What happened to this watch. Present only when ?allow_rewatch=yes was sent and action is scrobble — otherwise the key is absent entirely.
The first three values are session states, and mean the watch was recorded on a session of that state. The rest explain why no session took it. Note this is a superset of the three-value rewatch_status returned by GET /sync/all-items — do not reuse that enum here.
active — recorded on a session still in progress (typical for TV / anime).
completed — recorded on a session that is now complete; movie sessions are created complete.
closed — recorded on a session that was manually closed or left partial.
first_watch — not a re-viewing; this is the original watch.
too_soon — rejected by the 2-day gap between two watches of the same item.
not_eligible — the item's watchlist status is plan-to-watch.
pro_required — the account is not Simkl PRO or VIP.
active, completed, closed, first_watch, too_soon, not_eligible, pro_required, null