active, completed, or closed). Up to 50 sessions per item. Simkl returns the full session list so you can replicate the simkl.com “Rewatches” panel.
POST /scrobble/stop you’ll see rewatch_status: "too_soon"; on POST /sync/history the write is simply a no-op.This 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. Don’t treat it as an error or retry it.Details and the other ceilings (50 sessions per item) in The 2-day gap and Limits and rules.?allow_rewatch=yes get a silent no-op on POST /sync/history — the request still consumes a rate-limit slot but returns added: { movies: 0, shows: 0, episodes: 0 }. POST /scrobble/stop is the exception: it answers rewatch_status: "pro_required", so you can read the reason off the response. Either way, gate the flag behind the user’s plan instead of sending it blindly:- On sign-in (or first launch), call
GET /users/settingsonce and readaccount.type— one of"free","pro", or"vip". Cache it locally. - Don’t refetch on every poll. User settings are set-and-forget. Watch
activities.settings.allfromGET /sync/activities(you’re already polling it for the sync delta — see the Sync guide); when that timestamp bumps, refetch/users/settingsonce to pick up plan upgrades, downgrades, timezone changes, etc. - If
account.type === "free", hide or disable the “Rewatch” button with a short note like “Rewatch tracking requires Simkl PRO or VIP” and link to simkl.com/vip. Don’t send?allow_rewatch=yesfor free-tier users — there’s no value in firing a request that the server will silently drop.
Endpoints used
POST /sync/history
?allow_rewatch=yes makes the write land in a rewatch session instead of the canonical row.GET /sync/all-items
?allow_rewatch=yes adds one extra row per saved session alongside the canonical entry.POST /scrobble/stop
?allow_rewatch=yes on a stop with progress ≥ 80, or on a check-in. Never on /scrobble/start.Adding a rewatch toggle to your app
Rewatch tracking is opt-in twice over: once by the user in your settings, and once by Simkl’s plan gate. Wire both before you send?allow_rewatch=yes anywhere.
1. Ship it off by default
The toggle must start disabled. Most people just want “mark watched” and would be confused by a title that shows as completed and has a session in progress. Turning it on should be a deliberate act. Something like this in your settings screen:2. Check the plan when they flip it on
Rewatches are a Simkl PRO / VIP feature. Check before enabling, not after the first failed write — a free-tierPOST /sync/history with the flag is a silent no-op that still costs a rate-limit slot.
You should already have account.type cached from sign-in (see the plan check). If it isn’t "pro" or "vip", don’t enable the toggle — show an upgrade prompt instead:
3. Handle the downgrade
A user can be PRO today and free next month. Whenactivities.settings.all bumps and the refetched account.type comes back "free", stop sending the flag and disable the toggle — but don’t delete anything. Their existing sessions stay on their Simkl account and reappear if they resubscribe.
Both surfaces fail safely if you miss it: POST /sync/history no-ops, and POST /scrobble/stop answers rewatch_status: "pro_required", which is a good trigger to flip your cached flag and re-check the plan.
Quick start
A movie rewatch:rewatch_id and rewatch_status inside added.statuses[].response. Cache the rewatch_id and pin it on every subsequent write for that session.
When does a write become a rewatch?
The server always tries the regular write first. If the regular write would actually mark new data, that takes precedence — no rewatch is created. A write lands in a rewatch session when any of these holds:- The item is already Completed and the regular add is a no-op (movies: user already finished it; shows: payload has no episodes and the show is Completed).
- You included episodes and every one is already on the user’s history — i.e. the regular write would change nothing.
- You passed
is_rewatch: trueon the item.
is_rewatch: true does not bypass the regular-first logic. If the write tries to add a date that already exists in canonical history, it’s still rejected. The flag’s main use is to create an empty rewatch session on a Completed item your UI explicitly marked as “starting a rewatch” — without writing any episodes yet.
The episode or movie must already exist on the user’s history (completed / watching / hold / dropped) — Plan-to-Watch entries can’t be rewatched, there’s nothing to rewatch.
Scrobble (players and media servers)
If your app is a player that reports playback with/scrobble/* instead of writing history directly, you can record rewatches too.
The problem it solves: your user finishes Interstellar for the third time. Your player sends /scrobble/stop at 95%. Simkl looks at it, sees the movie is already marked watched, and throws the event away. The user’s rewatch count never moves.
The fix: add ?allow_rewatch=yes to that one call. Simkl then logs it as a separate viewing session instead of discarding it.
The whole feature in one call
Reading the result
rewatch_status is the only field you need to branch on. Three values mean it worked:
rewatch_status on GET /sync/all-items, which only ever returns the three session states. If you share an enum type between the two, widen it here.Send it on stop or checkin, never on start or pause
allow_rewatch=yes on /scrobble/start. It will eventually log a rewatch on a title the user isn’t even watching.Here’s how: your user watched 90% of Dune last night, then closed the player without a clean stop. Tonight they press play on Arrival. Your /scrobble/start for Arrival makes Simkl tidy up that stale Dune session first — and if the flag is on the URL, Dune gets logged as a rewatch. Nothing in the response mentions it, so you’d never notice in testing.Check-ins rewatch too, just not straight away
/scrobble/checkin is fire-and-forget: it doesn’t mark anything watched when you call it, so it can’t tell you a rewatch result either. Simkl stores the opt-in on the check-in and applies it when the check-in matures into a watch at the end of the item’s runtime.
Three things follow from that, and they’re easy to get wrong:
- No
rewatch_id/rewatch_statusin the response. Nothing has happened yet. If you need to confirm the session exists, read it back later fromGET /sync/all-items?allow_rewatch=yes. - The PRO / VIP check runs at check-in time. A user who is PRO when you check in still gets the rewatch when it matures, even if their plan lapses in between.
- The decision is frozen once the check-in is recorded. You can’t add or remove the flag afterwards. Deleting the playback before it matures cancels the whole check-in, rewatch included.
/scrobble/stop for rewatches — you get an immediate, readable answer instead of having to poll for one.
Wiring it into your player
Four gates, all of which must be true before the flag goes on the URL:Is the account PRO or VIP?
account.type once at sign-in and cache it. See the plan check above.Has the user enabled rewatch tracking in your settings?
Did the user actually ask for this watch to be a rewatch?
Is this the stop call?
/scrobble/stop, with progress ≥ 80 so it actually marks watched.Per-item rewatch fields
All fields below are optional. They only take effect when?allow_rewatch=yes is on the URL.
Session states
A session is in exactly one of three states. For TV / anime,completed is server-earned only: the server promotes a session when the rewatch covers every aired regular episode (specials in season 0 don’t count). For movies, completed is the default and only meaningful state.
POST /sync/history?allow_rewatch=yes + rewatch_status: "..."):
"active"— start or reactivate a session. The previously-active session for the item auto-closes."closed"— end the active session. No-op against acompletedsession; reactivate it first if you really need to close it."completed"— honored for movies. For TV / anime, silently clamped to"closed"because only coverage earns DONE. Subsequent episode writes that complete coverage will promote the session back to"completed".
completed session (whether active or closed) auto-promotes to completed the moment an episode write pushes coverage to every aired regular episode.
The 2-day gap
Any two watch events on the same item (movie or individual episode) must be at least 2 days apart, or the second write collapses into the first session. It’s a rewatch, not a rewind 😄. The gap is per-item, not per-show. Watching S1E1 on Monday and S1E2 on Tuesday is fine — those are different items. The rule only fires when the same episode is written twice within 48h.- Sleep-and-resume — user starts an episode at night, finishes the next morning. One viewing, not two.
- Wrong timezones — clients labeling local time as UTC (or vice versa) drift 1–12 hours per write.
- DST transitions — naive date libraries miscalculate by an hour twice a year.
- Multi-device duplicates — phone + TV both report the same play.
- Retry storms — flaky connections retry a write that already landed.
- Importer re-runs — re-running an import re-sends the same
watched_at. - Scrobble pause/resume noise — bathroom break → re-fire of
/start+/stop. - Buggy progress reporting — players hitting 80%+ multiple times on seeks.
Reading sessions back
?allow_rewatch=yes on GET /sync/all-items adds one extra entry per saved rewatch session alongside the canonical entry for each item. Always pair with date_from — never call without it outside the initial Phase-1 sync.
Canonical vs rewatch rows
last_watched_at and watched_episodes_count on the canonical entry never incorporate rewatch progress, no matter how many sessions exist for the item.
Worked example — Game of Thrones with one rewatch session
A representative shape fromGET /sync/all-items?allow_rewatch=yes&extended=full&episode_watched_at=yes — Game of Thrones (Simkl ID 17465) finished long ago, currently being rewatched in progress (4 of 73 episodes into S1). The item appears twice in shows[]: once as the canonical row, once as the in-progress rewatch row.
- Same
simklID on both rows —17465appears twice. Group byids.simklwhen iterating; treatis_rewatch: truerows as side-cars on the canonical entry. - Canonical
watched_episodes_countis73/73(the user finished the show originally); the rewatch row’s4/73is per-session and does NOT advance the canonical count. last_watched_atis independent on each row — canonical reflects the original final watch (2026-05-15); the rewatch row reflects its own most-recent episode (2026-05-16T20:00:00Z).rewatch_status: "active"because the session is mid-coverage (4/73). It auto-flips to"completed"whenwatched_episodes_count >= total_episodes_count - not_aired_episodes_count. See Session states for the full rules.seasons[].episodes[]only appears on the rewatch row under this flag combo (extended=full+episode_watched_at=yes). Withoutextended=full, the rewatch row would arrive summary-only:watched_episodes_count: 0sentinel, noseasons[]block.- Movies are simpler — no
seasons[], justmovie: { ... }, and the rewatch row’srewatch_statusalways starts atcompleted(movies have nothing to track episode-wise). - Anime rewatch rows have the same shape as show rows plus an
anime_typefield ("tv","movie", etc.) on the rewatch row itself.
Flag combinations for episode lists
episode_watched_at=yes is a modifier — it adds timestamps to episodes already loaded. Without extended=full, episodes aren’t loaded and the flag has no effect on rewatch entries.
UI patterns (mirror simkl.com)
Stats badge — '×3 times (1 partial), last 12 days ago'
Stats badge — '×3 times (1 partial), last 12 days ago'
?allow_rewatch=yes response for the current item and count by rewatch_status. closed is the “partial” count; completed is the clean-finish count.'Currently rewatching #N' progress bar (TV / anime)
'Currently rewatching #N' progress bar (TV / anime)
active session and show watched_episodes_count / total_episodes_count. Number the badge by chronological index among all sessions for the item — the number isn’t stored on the server, you compute it client-side.Movies don’t really have an active phase — every movie rewatch session flips to completed immediately.Rewatch history list — '×3 sessions' panel
Rewatch history list — '×3 sessions' panel
last_watched_at ascending. Label sessions “Rewatch #1”, “Rewatch #2”, etc. on the client.Limits and rules
- Maximum 50 rewatches per item (movie, show, or anime — combined cap).
- One
activesession per item. Making another session active auto-closes the previous active one. - 2-day minimum gap between watch events on the same movie or episode. See The 2-day gap.
- Simkl PRO / VIP only. Free-tier writes are silent no-ops.
Edge cases
Free-tier user calls ?allow_rewatch=yes
Free-tier user calls ?allow_rewatch=yes
200 OK with added: { movies: 0, shows: 0, episodes: 0 }. No error code is surfaced. To prevent this in your app, check account.type from GET /users/settings at sign-in (cache it locally; refetch only when activities.settings.all bumps — see the PRO / VIP gate at the top of this page). Reading the added counts in the response is a fallback if you can’t gate up-front, but it’s wasteful — the request still consumed a rate-limit slot.50-cap reached
50-cap reached
Active session collision
Active session collision
watched_episodes_count is preserved). The old session is still readable and still counts toward the 50-cap.watched_at collides with canonical history
watched_at collides with canonical history
is_rewatch: true — is rejected if the watched_at date matches an existing canonical episode/movie row. Use a different watched_at value, or omit the conflicting episode.Same episode written twice within 48h
Same episode written twice within 48h
watched_episodes_count doesn’t increment; last_watched_at updates to the latest value. Intentional — see The 2-day gap.Conflict-split on same episode, different dates
Conflict-split on same episode, different dates
watched_at more than 48h away, the server closes the current active session (preserving its watched_episodes_count) and opens a fresh active session with the conflicting episode at the new timestamp. Common with offline-capable apps catching up after a long sync gap.Sessions started via simkl.com are pre-populated
Sessions started via simkl.com are pre-populated
/sync/all-items after a session was created on simkl.com to see what’s actually there.1970-01-01T00:00:01Z on a rewatch episode
1970-01-01T00:00:01Z on a rewatch episode
Reference
Sync guide
date_from semantics, deletion reconciliation, edge cases.