> ## 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.

# How scrobbling works

> Report real-time playback — start, pause, stop, checkin — so Simkl tracks 'Watching now' and auto-marks items watched.

Scrobbling lets apps report **real-time playback events**. Use it when a user starts, pauses, or stops watching. If you just want a "Mark as watched" button without tracking playback, use [`POST /sync/history`](/api-reference/simkl/add-to-history) instead.

This page is a reference index. The lifecycle, decision tree, platform recipes, and gotchas live in the Scrobble guide:

<Card title="Scrobble guide — full walkthrough" icon="play" href="/guides/scrobble" horizontal>
  Mermaid state diagram of the lifecycle, "checkin vs scrobble loop" decision tree, universal player-event mapping table, reference Scrobbler implementations in Python / JavaScript / Swift / TypeScript, seek/scrub handling, and a gotchas FAQ.
</Card>

Pass as much data as you can (title, year, `ids`) so Simkl can detect the item reliably.

`progress` is a percentage from `0.00` to `100.00`. The input format is flexible (max 2 decimal places — `75`, `75.0`, `75.12` are all fine); responses are standardized (`75`, `75.12`, `45.5`). Scrobble IDs are 64-bit integers.

<Danger>
  **One call per user action, never on a timer.**

  Send a scrobble event **only when the user does something** — pressed Play, Paused, Stopped, closed the player, or the episode ended. **While the video is playing and the user is doing nothing, send nothing at all.**

  There is no heartbeat and no "report progress every N seconds" in this API. Simkl advances `progress` on its own between your events using the item's known runtime, so a 45-minute episode is **2 calls**, not 90. Polling produces an identical result while burning 45× the rate limit, and gets apps throttled.

  Full breakdown, including seek/scrub handling, in the [Scrobble guide](/guides/scrobble#one-call-per-user-action).
</Danger>

## What each endpoint does

<Frame caption="The &#x22;Now Watching&#x22; banner on the user dashboard, fed by [`/scrobble/start`](/api-reference/simkl/scrobble-start) and [`/scrobble/checkin`](/api-reference/simkl/scrobble-checkin). The progress bar updates from the `progress` you send (start / pause / stop) or, for checkin, is extrapolated from the item runtime.">
  <img src="https://mintcdn.com/andrewmasyk/RbJc6mqlYX6FcPwH/images/watching-now-dashboard.webp?fit=max&auto=format&n=RbJc6mqlYX6FcPwH&q=85&s=a71e052e0bbc1f376f6dc58f9785517a" alt="Simkl user dashboard showing 'NOW WATCHING' Fallout S02E07 'The Handoff' with a 46% watched progress bar and 27 minutes left" width="1645" height="906" data-path="images/watching-now-dashboard.webp" />
</Frame>

| Endpoint | Effect |
| - | - |
| [`POST /scrobble/start`](/api-reference/simkl/scrobble-start) | Marks the user as **"Watching now"** on their profile. Does **not** mark the item as watched. |
| [`POST /scrobble/pause`](/api-reference/simkl/scrobble-pause) | Saves the current `progress` so the user can resume later. |
| [`POST /scrobble/stop`](/api-reference/simkl/scrobble-stop) | Ends the session. With `progress` ≥ 80% (or your [`min_progress`](/guides/scrobble#change-the-80-threshold)), marks the item watched; below that, the session is kept as a resumable pause. The only endpoint that accepts [`?allow_rewatch=yes`](#rewatches). |
| [`POST /scrobble/checkin`](/api-reference/simkl/scrobble-checkin) | Like [`start`](/api-reference/simkl/scrobble-start), but Simkl **auto-marks the item watched when its computed progress reaches 100%** based on the item's runtime — no further calls needed from your app. Use this when you can't reliably hook into stop events. |

<Note>
  **Start ≠ watched.** A bare [`start`](/api-reference/simkl/scrobble-start) only puts the title in the user's "Watching now" banner. The item gets marked as watched only when:

  * you call [`stop`](/api-reference/simkl/scrobble-stop) with progress ≥ 80%, **or**
  * you used [`checkin`](/api-reference/simkl/scrobble-checkin) and the auto-tracked progress reaches 100%, **or**
  * you separately call [`POST /sync/history`](/api-reference/simkl/add-to-history).
</Note>

## How it works

Simkl stores **one active scrobble session per show/movie/anime**. Calling `/scrobble/start` **replaces any existing session** for that item and **clears previous pauses**. Paused sessions (created by `/scrobble/pause`, or by `/scrobble/stop` with progress under 80%) can be retrieved via [Get Playbacks](/api-reference/simkl/get-playback-sessions) and resumed by calling `/scrobble/start` again with the same item. To delete a paused session, use [Delete Playback](/api-reference/simkl/delete-playback).

### Lifecycle at a glance

Progress drives every transition. The 80% threshold is the only "magic number" you have to remember. It's the default — add `?min_progress=` (50–100) to a `stop`, `start` or `checkin` call to use your own; see [Changing the 80% threshold](/guides/scrobble#change-the-80-threshold).

```
                                     progress ≥ 80
                                     ┌──────────────► action: scrobble  (marked watched)
  start ──► pause ──► start ──► stop ┤
   0%       45%       45%      ?     │ progress < 80
                                     └──────────────► action: pause     (resumable)


  checkin ────────────────────────────► (no progress; auto-scrobbles at 100% from runtime)
```

* **Two ways to mark something watched.** Either drive [`start`](/api-reference/simkl/scrobble-start) → [`stop`](/api-reference/simkl/scrobble-stop) ≥ 80% yourself, or fire [`checkin`](/api-reference/simkl/scrobble-checkin) once and let Simkl auto-complete from the item's runtime.
* **`progress` is required for [`start`](/api-reference/simkl/scrobble-start) / [`pause`](/api-reference/simkl/scrobble-pause) / [`stop`](/api-reference/simkl/scrobble-stop)** (defaults to `0` if you omit it). On [`checkin`](/api-reference/simkl/scrobble-checkin) it is silently dropped server-side.
* **`simkl` ID alone is enough** for any of the four endpoints. Title + year + extra IDs help when you don't have a `simkl` ID and need Simkl's matcher to find the item.

### Typical flow

<Steps>
  <Step title="Playback begins" icon="play">
    User presses Play in your app → `POST /scrobble/start` (or `/scrobble/checkin` if you want auto-completion). Title appears in the "Watching now" banner.
  </Step>

  <Step title="User pauses" icon="pause">
    User pauses → `POST /scrobble/pause` with current `progress`. The session is kept as resumable.
  </Step>

  <Step title="User resumes" icon="play">
    User unpauses → `POST /scrobble/start` again with current `progress`. Same session continues.
  </Step>

  <Step title="User stops" icon="stop">
    User stops or finishes → `POST /scrobble/stop` with current `progress`. ≥ 80% marks watched; lower is kept as a pause.
  </Step>
</Steps>

<Tip>
  **Use [`checkin`](/api-reference/simkl/scrobble-checkin) for fire-and-forget tracking** — when you have an item's runtime but no reliable "stop" event (e.g. some embedded players, casting flows). Simkl extrapolates progress from the start time + runtime and marks the item watched automatically when 100% is reached.
</Tip>

<Note>
  Members can view and manage their saved playbacks at [simkl.com/my/history/playback-progress-manager/](https://simkl.com/my/history/playback-progress-manager/).
</Note>

### Action types in responses

The response `action` field tells you what Simkl did with your call:

| Value | When you'll see it |
| - | - |
| `start` | Returned by `/scrobble/start` when beginning or resuming. |
| `checkin` | Returned by `/scrobble/checkin` — Simkl will auto-scrobble at 100% from the item's runtime. |
| `pause` | Returned by `/scrobble/pause`, or by `/scrobble/stop` when progress \< 80%. |
| `scrobble` | Returned by `/scrobble/stop` when progress ≥ 80% — item marked as watched. |

### Session management

* **Expiry timestamps:** start = now + remaining runtime; stop = now + 1 hour; pause = now.
* **Persistence:** sessions persist until manually removed or replaced by the next scrobble for that title. Retention by plan: Free 7 days, PRO 30 days, VIP 90 days.
* **Rate limiting:** one scrobble operation per user at a time (20-second lock).
* **Duplicate prevention:** `409` if you try to stop an already-completed session within 1 hour.

### Anime episode numbering

Simkl uses **AniDB** as the primary source for anime data, which numbers seasons/episodes differently from TMDB/TVDB. When you scrobble anime using TMDB season/episode numbers, Simkl maps them to the corresponding AniDB episode. Responses include both the Simkl/AniDB numbers (`season`, `number`) and the original TVDB numbers (`tvdb_season`, `tvdb_number`) for reference.

<a id="rewatches" />

### Rewatches

Scrobbling something the user has **already finished** records nothing by default — Simkl treats it as a duplicate and drops it. Add `?allow_rewatch=yes` to log it as a separate viewing session instead.

| | |
| - | - |
| **Where** | [`POST /scrobble/stop`](/api-reference/simkl/scrobble-stop) **with `progress` ≥ 80**, and [`POST /scrobble/checkin`](/api-reference/simkl/scrobble-checkin). Never `start` or `pause` — they mark nothing watched, and neither does a stop below 80. |
| **Who** | Simkl **PRO / VIP** accounts. Free accounts get `rewatch_status: "pro_required"` on `stop`. |
| **Returns** | On `stop`: `rewatch_id` and `rewatch_status`, added to the normal `scrobble` response. On `checkin`: **nothing** — see below. |
| **Read** | `rewatch_status`. `active` / `completed` / `closed` mean it saved; `first_watch`, `too_soon`, `not_eligible`, `pro_required`, `null` mean it didn't. |

**On `stop` the rewatch is immediate** and the outcome comes straight back. **On `checkin` it is deferred**: a check-in marks nothing watched at request time, so Simkl stores the opt-in on the check-in and applies it when the check-in matures at the end of the item's runtime. The check-in response therefore carries no rewatch fields, the PRO / VIP check runs at check-in time rather than at maturation, and the choice is frozen once the row is written.

<Danger>
  **Don't send it on [`start`](/api-reference/simkl/scrobble-start).** It auto-finishes the user's *previous* unfinished playback before opening a new one and honours the flag while doing it — so the flag on `start` can log a rewatch of a **different title**, with nothing in the response to tell you. Put it on `stop` or `checkin`, and only when the user explicitly asked.
</Danger>

<Warning>
  **`rewatch_id` is not a success signal.** A session that's already running returns its id even when it rejected your watch (inside the 2-day gap, for example). Branch on `rewatch_status`.
</Warning>

<CardGroup cols={2}>
  <Card title="Rewatches guide" icon="repeat" href="/guides/rewatches#scrobble">
    Full walkthrough — every status value, the PRO / VIP check, the 2-day gap, and how to wire it into a player.
  </Card>

  <Card title="Stop endpoint reference" icon="stop" href="/api-reference/simkl/scrobble-stop">
    Parameter reference for `allow_rewatch` and the full `rewatch_status` table.
  </Card>
</CardGroup>

## Endpoints

<CardGroup cols={2}>
  <Card title="Start" icon="play" href="/api-reference/simkl/scrobble-start">
    `POST /scrobble/start` — show "Watching now"; resume a paused session.
  </Card>

  <Card title="Checkin" icon="circle-check" href="/api-reference/simkl/scrobble-checkin">
    `POST /scrobble/checkin` — auto-mark watched at 100% based on runtime.
  </Card>

  <Card title="Pause" icon="pause" href="/api-reference/simkl/scrobble-pause">
    `POST /scrobble/pause` — save progress so the user can resume.
  </Card>

  <Card title="Stop" icon="stop" href="/api-reference/simkl/scrobble-stop">
    `POST /scrobble/stop` — end session; ≥ 80% marks watched.
  </Card>
</CardGroup>

### Playbacks

<CardGroup cols={2}>
  <Card title="Get playbacks" icon="list" href="/api-reference/simkl/get-playback-sessions">
    `GET /sync/playback` — list saved paused playbacks (cross-device resume; optionally narrow with `/:type` where `:type` is `episodes` or `movies`).
  </Card>

  <Card title="Delete playback" icon="trash" href="/api-reference/simkl/delete-playback">
    `DELETE /sync/playback/:id` — remove a saved playback by ID.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.