Required scopes
/api/platform/v1/playback/now-playingrequiresplayback:read/api/platform/v1/playback/in-progressrequiresplayback:read/api/platform/v1/me/continue-watchingrequiresplayback:readDELETE /api/platform/v1/playback/in-progress/{id}requiresplayback:write/api/platform/v1/playback/eventsrequiresevents:read
Now Playing
typetmdbIdtitleprogressPercentprogressSecondsdurationSecondsnowPlayingplaybackStatemediaSource
In Progress
- At most one episode per show: the most recently updated one that is still resumable. Shows the user has dropped are left out
- An item stays until the user logs a watch of that movie or episode at or after its last playback event. Watches from before it, such as an old import, do not hide a rewatch
sourceSeasonandsourceEpisodegive an episode in TMDB numbering. They differ fromseasonandepisodeonly for an anime episode stored in TVDB order. See Anime episode numbering
playback resource
of /api/platform/v1/me/sync/snapshot.
Dismiss an item with:
Continue Watching
/me/continue-watching returns show-level summaries: the show’s current
season/episode and completion state. It is useful for a “next episode” row, but
it is not the source of an exact movie or episode resume position. Fetch
/api/platform/v1/me/continue-watching/{showId} when you need the details for a
show. Use /api/platform/v1/playback/in-progress for resumable movie or episode
items with progressSeconds, durationSeconds, and the progress identifier
used by the dismiss endpoint.
The status field describes the series’ lifecycle, not its current release
schedule. For example, Returning Series means the series may return even
after its latest season has finished airing. Do not label it “Airing” based on
that status alone. Use releasePhase for a schedule-aware badge:
airing: a season has released episodes and a dated future episode in that same seasonreturning: a subsequent season is announced, with or without an air dateupcoming: an announced first season has not premieredseason_complete: released episodes exist but no later episode or season is currently announcedschedule_pending: a same-season episode is announced without a future air date, a partly released season has no scheduled next date, or a provider-returning series lacks enough schedule dataended,canceled, andunknown: terminal provider states or insufficient schedule data
status response field and
status=returning|airing|ended query filters keep their provider-lifecycle
semantics. Legacy airing matches Returning Series; legacy ended matches
every other provider status, not just ended shows.
Episode pointers and gaps
nextSeason and nextEpisode identify the first currently available
unwatched episode after lastWatchedSeason and lastWatchedEpisode. They never
rewind to an earlier gap and never point at an unaired episode. Both values are
null when there is nothing playable after the last-watched episode.
Earlier gaps are reported separately:
earlierUnwatchedSeasonandearlierUnwatchedEpisodeidentify the gap to fill first, or arenullwhen there is no gap. That is the earliest aired unwatched episode in a season the user has started; only when every started season is complete up to the last-watched episode does it fall back to the earliest aired unwatched episode overallearlierUnwatchedCountis the number of aired unwatched episodes before the last-watched episode, including seasons the user has not startedearlierUnwatchedSourceSeasonandearlierUnwatchedSourceEpisodegive the provider coordinates of that gap for building episode links, likenextSourceSeasonandnextSourceEpisode;earlierUnwatchedEpisodeTitleis its title when known
nextEpisodeToAir is a separate announcement pointer. Its airDate is optional
because an episode can be announced before it is scheduled. A caught-up show
remains in Continue Watching while this pointer is present, but clients must not
treat it as playable until it becomes the nextSeason and nextEpisode.
For example, if only S01E06 is watched, episodes 1–5 are available, and S01E07
is announced but has not aired, the summary contains:
nextSeason and nextEpisode advance to 1 and 7; the
earlier-gap fields continue to identify episodes 1–5.
Current season
ThecurrentSeason* fields describe the season progress is focused on:
nextSeason when an episode is playable, otherwise lastWatchedSeason.
currentSeasonEpisodesTotal counts aired episodes only, and
currentSeasonMinutesRemaining adds up the listed runtimes of that season’s
unwatched aired episodes. episodeRuntimeSource says where
episodeRuntimeMinutes came from: history (the average of runtimes in the
user’s watch history), episode (the next episode’s listed runtime), show
(the provider’s series runtime), or unknown.
Server-sent events
readyplayback-update
playback-update includes:
Recommended client pattern
Use SSE as an invalidation signal, not as the full source of truth.- Open the event stream.
- Wait for
playback-update. - Fetch
/playback/now-playingor/playback/in-progress. - Update your UI from those read endpoints.
401,
refresh or re-authorize before reconnecting. Do not treat an open stream as a
permanent authorization grant.
Browser caveat
Standard browserEventSource does not let you attach an Authorization header. If your app runs in the browser, use one of these approaches:
- Proxy the stream through your backend
- Use a fetch-based streaming client instead of
EventSource - Open the SSE stream from a native, desktop, or server environment that supports bearer headers
Cache behavior
Playback read endpoints return live user state and should be treated as non-cacheable application data.Failure handling
If the SSE connection drops or its handshake returns503 service_unavailable,
wait for the Retry-After delay when one is present (otherwise use bounded
exponential backoff) before reconnecting:
- fetch
/playback/now-playingafter reconnecting - do not assume missed SSE messages contain the full state you need
X-PunchPlay-Request-Id response header when available and log the endpoint, timestamp, and status code.