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

# Mark as watched

> The fastest way to record that a user finished a movie, episode, or season — without scrobbling.

If you just want to record that a user **finished** something — a movie, an episode, a whole season — use a single [`POST /sync/history`](/api-reference/simkl/add-to-history) call. You don't need scrobbling, and you don't need a media player.

<Tip>
  **Use scrobble only if you're tracking real-time playback** (a media-server plugin, a video player). If you just want a "Mark as watched" button, this page is what you need.
</Tip>

<Note>
  **`POST /sync/history` already moves the item to the right Watchlist — don't follow up with `/sync/add-to-list`.** The history call places (and re-classifies) the item based on the watch event. Read `added.statuses[].response.status` in the response to see where it landed: a `"completed"` write on a still-airing show is silently downgraded to `"watching"`, and that resolved status is what the server stored. A follow-up `/sync/add-to-list` call would overwrite the server's smarter decision.
</Note>

## Endpoints used on this page

<CardGroup cols={2}>
  <Card title="POST /sync/history" icon="circle-plus" href="/api-reference/simkl/add-to-history">
    Mark one or many items as watched. The main endpoint for this guide.
  </Card>

  <Card title="POST /sync/history/remove" icon="circle-minus" href="/api-reference/simkl/remove-from-history">
    Undo a mark-as-watched. Same payload shape.
  </Card>

  <Card title="POST /scrobble/start" icon="play" href="/api-reference/simkl/scrobble-start">
    For media players: report that playback has begun.
  </Card>

  <Card title="POST /scrobble/stop" icon="stop" href="/api-reference/simkl/scrobble-stop">
    For media players: stop playback. ≥80% progress = marked watched.
  </Card>
</CardGroup>

## Mark a movie

<CodeGroup>
  ```bash curl theme={"theme":{"light":"github-light","dark":"vesper"}}
  curl -X POST "https://api.simkl.com/sync/history?client_id=$CLIENT_ID&app-name=my-app-name&app-version=1.0" \
    -H "Authorization: Bearer $ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -H "User-Agent: my-app-name/1.0" \
    -d '{
      "movies": [
        { "ids": { "imdb": "tt1201607" } }
      ]
    }'
  ```

  ```js JavaScript theme={"theme":{"light":"github-light","dark":"vesper"}}
  const params = new URLSearchParams({
    client_id:     clientId,
    'app-name':    'my-app-name',
    'app-version': '1.0',
  });

  await fetch(`https://api.simkl.com/sync/history?${params}`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type':  'application/json',
      'User-Agent':    'my-app-name/1.0',
    },
    body: JSON.stringify({
      movies: [{ ids: { imdb: 'tt1201607' } }],
    }),
  });
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"vesper"}}
  import requests

  requests.post(
      'https://api.simkl.com/sync/history',
      params={
          'client_id':   client_id,
          'app-name':    'my-app-name',
          'app-version': '1.0',
      },
      headers={
          'Authorization': f'Bearer {access_token}',
          'Content-Type':  'application/json',
          'User-Agent':    'my-app-name/1.0',
      },
      json={'movies': [{'ids': {'imdb': 'tt1201607'}}]},
  )
  ```
</CodeGroup>

You can pass any ID Simkl recognizes — see [Supported ID keys](/conventions/standard-media-objects#supported-id-keys) for the full list with types and examples. Full payload reference at [`POST /sync/history`](/api-reference/simkl/add-to-history).

<Tip>
  **User doesn't remember when they watched it?** POST `"watched_at": "1970-01-01T00:00:01Z"` — Simkl's *"Very long time ago / I don't remember"* placeholder. simkl.com's "When did you watch this?" date picker exposes this as a smart-suggestion card; clients should mirror that option. Full convention (including detection rule for reading and recommended date-picker UX patterns) at [Dates and timezones → "Very long time ago" placeholder](/conventions/dates#very-long-time-ago-placeholder).
</Tip>

<Tip>
  **Send every identifier you have — `title`, `year`, and the full `ids` object.**

  Simkl walks the `ids` object in priority order (`simkl` first when present, then external IDs like `imdb`, `tmdb`, `tvdb`, `mal`, `anidb`, …). If no ID resolves, it falls back to a `title` + `year` match, then to title-only as a last resort. Sending everything you know — title, year, plus every external ID your client has cached — maximizes the chance the right item gets credited. Extra fields are free; missing fields can cause a `404 id_err` or, worse, a silent mismatch.

  **You don't need to search before writing.** Endpoints like `/scrobble/*`, `/sync/history`, `/sync/add-to-list`, and `/sync/ratings` resolve IDs server-side — pass whatever you have directly, no `/search/*` round-trip required. Calling `/search/id` first to "resolve" a Simkl ID is wasted work that doubles your API quota for no gain.

  See [Supported ID keys](/conventions/standard-media-objects#supported-id-keys) for the full list.
</Tip>

## Mark an episode

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "shows": [
    {
      "ids": { "tmdb": "1399" },
      "seasons": [
        {
          "number": 1,
          "episodes": [{ "number": 1 }]
        }
      ]
    }
  ]
}
```

## Mark a whole season

Drop the `episodes` array — Simkl marks every episode in the listed season:

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "shows": [
    {
      "ids": { "tmdb": "1399" },
      "seasons": [{ "number": 1 }]
    }
  ]
}
```

## Mark a whole show

Drop both `seasons` and `episodes`:

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "shows": [
    { "ids": { "tmdb": "1399" } }
  ]
}
```

## Mix everything in one call

Movies, shows, and anime can be sent together. Useful for importing watch history from another tracker. Anime entries are equally valid under either `shows[]` or `anime[]` — Simkl resolves the catalog by `ids` either way (see [Anime in `shows[]` or `anime[]`](/conventions/standard-media-objects#anime)).

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "movies": [
    { "ids": { "imdb": "tt1201607" } },
    { "ids": { "tmdb": "12445" } }
  ],
  "shows": [
    { "ids": { "tvdb": "121361" } }
  ],
  "anime": [
    { "ids": { "mal": "4246" } }
  ]
}
```

## When to use which endpoint

<Columns cols={2}>
  <Card title="Mark as watched" icon="circle-check" href="/api-reference/simkl/add-to-history">
    [`POST /sync/history`](/api-reference/simkl/add-to-history) — instant, one call. Best for "Mark watched" buttons, importers, manual logging.
  </Card>

  <Card title="Scrobble" icon="play" href="/api-reference/scrobble">
    Real-time playback tracking. Best for media players (Plex, Jellyfin, custom apps) that report actual user events.
  </Card>
</Columns>

<Note>
  **Scrobble does mark the item as watched, but only at the end of playback** — either when you call [`POST /scrobble/stop`](/api-reference/simkl/scrobble-stop) with ≥ 80% progress, or when [`POST /scrobble/checkin`](/api-reference/simkl/scrobble-checkin) auto-completes at 100% based on the item's runtime. **[`POST /scrobble/start`](/api-reference/simkl/scrobble-start) alone does *not* mark anything watched** — it only puts the title in the user's "Watching now" banner. If you're already scrobbling, you don't also need to call `/sync/history`.
</Note>

## Record a rewatch (Simkl Pro / VIP)

A `POST /sync/history` call on an already-Completed item is normally a no-op. Pass `?allow_rewatch=yes` to record an additional viewing as a separate rewatch session — but **read the full [Rewatches guide](/guides/rewatches) first**. The flag opens a parallel write path that, used carelessly, pollutes the user's history stats and rewatches panel with phantom sessions. The guide covers session lifecycle (`active` / `completed` / `closed`), per-item fields (`rewatch_id`, `is_rewatch`, …), episode-level tracking on shows, reading sessions back from `GET /sync/all-items`, UI patterns simkl.com uses, and — critically — the precautions you have to implement before enabling the flag.

```bash theme={"theme":{"light":"github-light","dark":"vesper"}}
curl -X POST "https://api.simkl.com/sync/history?allow_rewatch=yes&client_id=$CLIENT_ID&app-name=my-app-name&app-version=1.0" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "User-Agent: my-app-name/1.0" \
  -d '{ "movies": [{ "ids": { "imdb": "tt1201607" }, "watched_at": "2026-05-10T20:00:00Z" }] }'
```

## Removing items

Made a mistake? Remove with the same payload shape, just hit [`POST /sync/history/remove`](/api-reference/simkl/remove-from-history):

```bash theme={"theme":{"light":"github-light","dark":"vesper"}}
curl -X POST "https://api.simkl.com/sync/history/remove?client_id=$CLIENT_ID&app-name=my-app-name&app-version=1.0" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "User-Agent: my-app-name/1.0" \
  -d '{ "movies": [{ "ids": { "imdb": "tt1201607" } }] }'
```

<Tip>
  **A few `POST /sync/history` behaviors worth knowing:**

  * **No `seasons` / `episodes` and no `status` for a show.** Simkl picks based on the show's airing status: finished airing → **Completed** with every episode marked; currently airing → **Watching** with all *already-aired* episodes marked (future episodes left untouched); not yet released → **Watching** with no episodes marked.
  * **Set status without touching episodes.** Pass `status: "watching"` (or any other status) on the show to move the watchlist bucket without auto-marking episodes.
  * **Episode-level `ids` override `number`.** When an episode object includes `ids.tvdb` or `ids.anidb`, those IDs take precedence over the `number` field for matching — useful for absolute-order anime numbering or specials whose episode numbers don't line up across sources. That said, **prefer `season` + `number` when you have them** — episode IDs can be re-issued when catalogs merge or re-number, while `S1E4` is stable forever (see [Episode IDs](/conventions/standard-media-objects#episode)).
  * **Skip the auto-fill.** Add `?skip_auto_watching=yes` to suppress the auto-mark behavior for shows posted without explicit episodes. Only takes effect when the request also sets an explicit `status` on the show.
</Tip>
