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

# Changelog

> What's new across the Simkl API and these docs.

**[Discord](https://discord.gg/MJsWNE4)** is where most API announcements and developer chat happen — join us for new endpoints, breaking changes, and integration help in real time.

For broader Simkl product news — apps, features, releases — follow [**@Simkl**](https://twitter.com/Simkl) on X.

Use the **filter** in the right rail to narrow this changelog by tag.

<Update label="2026-10-14" description="Finale markers on TV episodes" tags={["New feature"]}>
  <Badge color="blue" icon="sparkles">New feature</Badge>

  **`finale_type` on TV episodes**

  [`GET /tv/episodes/{id}`](/api-reference/simkl/get-tv-episodes) now returns `finale_type` on every episode, so you can badge finales anywhere you list episodes — not only in the calendar:

  | Value | Meaning |
  | - | - |
  | `1` | Mid-season finale |
  | `2` | Season finale |
  | `3` | Series finale |
  | `null` | Not a finale |

  It's the same field, with the same values, as [`finale_type` in the calendar files](/api-reference/calendar#param-finale-type). Specials can carry it too. Anime episodes don't have it — [`GET /anime/episodes/{id}`](/api-reference/simkl/get-anime-episodes) omits the key.
</Update>

<Update label="2026-10-09" description="Original language and trailer types on title pages, search sorting, scrobble threshold" tags={["New feature"]}>
  <Badge color="blue" icon="sparkles">New feature</Badge>

  **Original language on title pages**

  [`GET /tv/{id}`](/api-reference/simkl/get-tv-show), [`/anime/{id}`](/api-reference/simkl/get-anime) and [`/movies/{id}`](/api-reference/simkl/get-movie) now return `original_language`: a lower-case ISO 639-1 code such as `en` or `ja`, or `null` when none is on file. It's the same field as in the trending and calendar files, now available for any title. Movies keep their existing upper-case `language` as well.

  **Trailer types**

  Every video in `trailers` on the same three endpoints now has a `type`: `trailer`, `teaser`, `clip`, `featurette`, `behind_the_scenes`, `bloopers` or `opening_credits` (`null` when it has no kind on file). Filter on it to show only trailers, or to group extras. For movies and TV the list is ordered English first, then by `type`, then by size; anime isn't grouped by `type`.

  **Sorting for text search**

  [`GET /search/{type}`](/api-reference/simkl/search-by-text) takes a `sort` parameter: `relevance` (the default), `release-date`, `last-air-date` (TV and anime only), `title` or `rank`. Paging keeps the order. Any other value returns `400 wrong_parameter`.

  **Choose your own scrobble threshold**

  Add `?min_progress=` (50–100) to [`/scrobble/stop`](/api-reference/simkl/scrobble-stop) to set the progress at which a stop marks the item watched instead of saving a pause. On [`start`](/api-reference/simkl/scrobble-start) and [`checkin`](/api-reference/simkl/scrobble-checkin) it sets the same threshold for finishing the user's previous open session. The default stays 80. See [Changing the 80% threshold](/guides/scrobble#change-the-80-threshold).
</Update>

<Update label="2026-10-07" description="Trending by genre, and genres fixed in trending, DVD, calendar and list data" tags={["New feature", "Fix"]}>
  <Badge color="blue" icon="sparkles">New feature</Badge>

  **Trending by genre**

  This month's most watched titles for every genre, on the same CDN and under the same rules as the other [trending files](/api-reference/trending) — public, no user token, required parameters and `User-Agent`, attribution:

  * `https://data.simkl.in/discover/trending/{type}/genre/index.json` — the genres for `movies`, `tv` or `anime`, each with its `name`, `slug` and item `count`.
  * `https://data.simkl.in/discover/trending/{type}/genre/{slug}_month_500.json` — one genre's ranking, up to 500 titles.

  Read the slugs from `index.json` rather than hardcoding them. A genre file is its own ranking, not a slice of `month_500.json`, so it reaches titles the main file never shows. See [By genre (this month)](/api-reference/trending#by-genre-this-month).

  <Badge color="orange" icon="bug">Fix</Badge>

  **`genres` lists only the title's own type**

  In the [trending](/api-reference/trending), DVD and [calendar](/api-reference/calendar) files and [`GET /lists/{id}`](/api-reference/simkl/get-list), the `genres` array now lists only genres of the title's own type. Before, some titles carried genres from another type: a false "Action" on 43 non-action movies in the month list, and repeats such as "Action, Action" or "Comedy, Comedy". Anime titles no longer show "Animation".
</Update>

<Update label="2026-10-03" description="Episode runtimes, similar titles, list collections and roles, audience counts" tags={["New feature"]}>
  <Badge color="blue" icon="sparkles">New feature</Badge>

  **Episode runtimes**

  Every episode now has a `runtime` in whole minutes, or `null` when none is on file:

  * [`GET /tv/episodes/{id}`](/api-reference/simkl/get-tv-episodes) and [`GET /anime/episodes/{id}`](/api-reference/simkl/get-anime-episodes) — on every episode.
  * [`GET /sync/all-items`](/api-reference/simkl/get-all-items) — add `episode_runtime=yes` (with `extended=full`) to get it on each episode row.
  * [Calendar v2 files](/api-reference/calendar) — on every `calendar[].episode`. This is an integer, unlike the show-level `metadata.runtime` display string (`"22m"`).

  **Similar titles on title pages**

  [`GET /movies/{id}`](/api-reference/simkl/get-movie), [`/tv/{id}`](/api-reference/simkl/get-tv-show) and [`/anime/{id}`](/api-reference/simkl/get-anime) now return `similar`: titles in the same genres, as shown at the bottom of a title's page on simkl.com. It is omitted when there are none. Items in `similar`, `users_recommendations` and `relations` also carry `fanart` now.

  **Custom Lists: collections and roles**

  * [`GET /lists/{id}`](/api-reference/simkl/get-list) returns `collection` — the sibling lists this one belongs with, such as a franchise split into *Movies* and *TV Shows*, or `null`. See [Lists that belong together](/guides/custom-lists#collections), and [Custom List Collections](https://docs.simkl.org/how-to-use-simkl/core-features/watchlists-and-custom-lists/custom-lists/custom-list-collections) for how users create one.
  * [`GET /lists/user/{userId}`](/api-reference/simkl/get-user-lists) gives every list a `role` — `owner`, `collaborator` or `follower` — so followed and shared lists can be told apart.
  * Auto lists set to random order are not available through the API and return `400`; see the [FAQ](/guides/custom-lists#common-questions).

  **Audience counts on trending, DVD and calendar files**

  [Trending and DVD files](/api-reference/trending) and [calendar v2](/api-reference/calendar) `metadata` now include `watching` and `completed` — all-time counts of users with the title in each list. On trending files, `watched` still counts only the file's timeframe; use `watching` + `completed` for a title's overall audience.
</Update>

<Update label="2026-09-18" description="Custom lists read API — beta, PRO and VIP" tags={["Major launch", "New feature"]}>
  <Badge color="green" icon="rocket">Major launch</Badge>
  <Badge color="blue" icon="sparkles">New feature</Badge>

  **Custom lists are readable from the API**

  Two endpoints are live in beta: [`GET /lists/user/{userId}`](/api-reference/simkl/get-user-lists) for a user's lists, and [`GET /lists/{id}`](/api-reference/simkl/get-list) for a list's contents. Creating, editing and deleting lists, and adding or removing items, remain web-only for now.

  **Requires [AUTH V2](/api-reference/auth-v2) and a Simkl PRO or VIP access token.** This is the first API that V1 `client_id`s cannot reach, and new APIs from here on will be built the same way — see [Migrating from V1 to V2](/guides/migrating-v1-to-v2). There is no anonymous access either; an anonymous caller counts as free.

  <Badge color="red" icon="triangle-exclamation">Don't</Badge> **A free or anonymous caller gets `HTTP 200`, not an error.** The body carries `{"error": "premium_only", ...}` with no `items` array and no `pagination`. A client that branches only on the status code will treat it as a successful list and render the placeholder card as real content.

  **Check for an `error` key on a 200 before reading `items`.** The `item` object it carries is renderable — title, description, poster — so you can show it in place of the list rather than leaving the user with a blank screen.

  **There is a polling gate — use it.** *(Added 2026-09-21.)* [`GET /sync/activities`](/api-reference/simkl/get-activities) carries a `custom_lists` block, so you never have to poll the list endpoints on a timer to find out whether anything changed:

  ```json theme={"theme":{"light":"github-light","dark":"vesper"}}
  "custom_lists": {
    "lists": {
      "all":             "2026-09-21T20:53:09Z",
      "regular":         "2026-09-21T20:53:09Z",
      "favorites":       "2026-09-21T20:50:07Z",
      "recommendations": "2026-09-21T20:51:26Z",
      "auto":            "2026-09-21T20:52:45Z"
    }
  }
  ```

  Gate on `all`, then branch on the four buckets to refresh only the kind that moved — a user favouriting one show moves `favorites` alone, so it costs one list refresh rather than a full re-read. Three things to know:

  * **It is nested one level deeper** than the other blocks: `custom_lists.lists.all`, not `custom_lists.all`.
  * **The bucket is `favorites` (plural)** while a list's own `type` field is `favorite` (singular). They do not match.
  * **`auto` does not move when an auto list's contents are rebuilt** — only when the user edits its settings. Auto lists regenerate about once a day, so refresh those on your own schedule rather than waiting for a signal that will not come.

  This block is returned to **AUTH V1 apps too**, even though the list endpoints themselves require V2 — so a V1 app can see that lists changed without being able to read them.

  Being beta, the response shape may still change; anything that would break a client gets announced here. Full detail in the [Custom Lists guide](/guides/custom-lists), the [API overview](/api-reference/lists), and [Custom Lists have their own timestamps](/guides/sync#custom-lists-have-their-own-timestamps) for the polling pattern.
</Update>

<Update
  label="2026-09-18"
  description="AUTH V2 is here — V1 retires around April 2027"
  tags={["New feature", "Breaking change"]}
  rss={{
title: "AUTH V2 is here, and every app needs a new client_id",
description: "AUTH V1 is expected to stop working around April 2027; the exact date is not fixed. A V1 client_id cannot be upgraded, so every developer must register a new V2 client_id. V2 is standards-compliant OAuth 2.0, counts request quotas per user rather than per app, and requires a user access token on everything except public catalog lookups. Tell us in Discord #api if six months is not enough time for your app."
}}
>
  <Badge color="blue" icon="sparkles">New feature</Badge>
  <Badge color="red" icon="triangle-exclamation">Migration required</Badge>

  ## AUTH V2 is here, and every app needs a new client\_id

  **Your V1 `client_id` cannot be upgraded.** There is no conversion and no toggle — V2 is a separate registration with a new `client_id`, and your existing V1 credentials are rejected by every `/oauth2/*` endpoint with `401 invalid_client`.

  <Warning>
    **[AUTH V1](/api-reference/auth-v1) apps are expected to stop working around April 2027**, about six months after this release. The exact date is not fixed, and we will announce it here and in the [Discord](https://discord.gg/MJsWNE4) `#api` channel with notice — but do not wait for that to start.

    That window should be comfortable for almost everyone. **If it is not comfortable for you, tell us rather than running out of time.** We can help with the transition, raise your limits while you run both, or extend your timeline where a situation genuinely needs it. If something about your integration makes [migrating](/guides/migrating-v1-to-v2) look impossible, that is exactly the case we want to hear about.
  </Warning>

  ### What you get

  [AUTH V2](/api-reference/auth-v2) is a standards-compliant OAuth 2.0 implementation. Stock OAuth libraries work against it without Simkl-specific handling, and it publishes [RFC 8414 discovery metadata](https://simkl.com/.well-known/oauth-authorization-server) most of them can configure themselves from.

  Two flows replace the three in V1:

  <CardGroup cols={2}>
    <Card title="OAuth flow" icon="lock" href="/api-reference/oauth2-authorization-code">
      Anything that can open a browser — web, mobile, SPA, desktop, extensions. **PKCE is mandatory** for every client type, `S256` only.
    </Card>

    <Card title="Device / PIN flow" icon="tv" href="/api-reference/oauth2-device">
      TVs, consoles, watches and CLIs, with an 8-character code (RFC 8628). Replaces the V1 PIN flow.
    </Card>
  </CardGroup>

  ### Request quotas are now counted per user

  Each user brings their own daily allowance, set by their own Simkl plan:

  | The user's plan | Requests per day |
  | - | -: |
  | Free | 500 |
  | PRO | 1,000 |
  | VIP | 10,000 |

  **There is no app-wide quota to manage or exhaust.** A V1 app has one allowance covering everything it does however many users it has, so a single heavy user can spend the budget the rest of them needed. On V2 your capacity grows with your user base instead of being divided between it, and one user can no longer spend another user's allowance. See [Rate limits](/resources/rate-limits#auth-v2-per-user).

  ### Decide what happens before sign-in

  <Warning>
    **V2 narrows anonymous access to public catalog data.** Summaries by Simkl ID, episode lists, and the Trending and Calendar files still work without a user token. Everything else needs one — **including search**, which a V1 app can call with just a `client_id`.

    If your app has a browse mode that works before sign-in, that needs a decision before you move — it is the one part of this migration that can turn into a product question rather than an auth change. See [Migrating from V1 to V2](/guides/migrating-v1-to-v2).
  </Warning>

  ### The work, in order

  For most apps the work is confined to your auth code — you are not rewriting your sync logic.

  <Steps>
    <Step title="Register a new V2 app" icon="plus">
      [Register in developer settings](https://simkl.com/settings/developer/) and choose a [client type](/api-reference/auth#client-types), which is **fixed at registration**:

      * **Mobile, desktop & browser apps**
      * **TV, devices & command line**
      * **Server apps & services**
    </Step>

    <Step title="Move your auth code across" icon="code">
      Swap `/oauth/*` for `/oauth2/*`, add PKCE, request the [scope](/api-reference/oauth2-scopes) you need, and add refresh handling. [Migrating from V1 to V2](/guides/migrating-v1-to-v2) has the full sequence.
    </Step>

    <Step title="Ship the new client_id early" icon="rocket">
      **Your V1 app keeps working the whole time**, so you can run both side by side while users move across.

      If your app is distributed rather than server-side, get the new `client_id` into the field early — a build that only learns it in a later release cannot migrate anyone still running an older one, and that is the thing most likely to decide whether you finish on time.
    </Step>
  </Steps>

  ### Token and permission changes

  | | V1 | V2 |
  | - | - | - |
  | Access token | 5 years | **7 days** — add refresh handling |
  | Refresh token | None | **180 days**, and the clock resets on every use |
  | Scopes | None, always full access | [`media:read` / `media:write`](/api-reference/oauth2-scopes), **defaults to read** |
  | Client type | Implicit | Fixed at registration |

  See [Tokens and refresh](/api-reference/oauth2-tokens) for the refresh grant and revocation.

  ### Three behaviours that each cost an afternoon

  1. **Omitting `scope` grants read-only, and so does misspelling it.** An unrecognised scope string is silently treated as read-only rather than rejected, so the failure surfaces much later as a `403` on your first write. Read the `scope` in the token response rather than assuming you got what you asked for.

  2. **A failed token exchange consumes the authorization code.** Once the request passes client authentication, the code is burned before the grant is validated, so you cannot correct the request and retry the same code. **The one exception is `401 invalid_client`** — client authentication runs first, so a wrong secret leaves the code alive and you can retry it inside the 10-minute window.

  3. **Refreshing invalidates the previous access token immediately.** Two processes that independently refresh the same grant will keep cutting each other off. Either nominate one refresh owner that writes the new token to shared storage, or give each instance its own authorization.
</Update>

<Update label="2026-09-10" description="Rewatches in scrobble: ?allow_rewatch=yes on stop and checkin" tags={["New feature"]}>
  <Badge color="blue" icon="sparkles">New feature</Badge>

  **Rewatches now work while scrobbling**

  `?allow_rewatch=yes` used to be limited to `POST /sync/history` and `GET /sync/all-items`. Players can now record rewatches directly from the scrobble loop, on [`POST /scrobble/stop`](/api-reference/simkl/scrobble-stop) and [`POST /scrobble/checkin`](/api-reference/simkl/scrobble-checkin). **Simkl PRO / VIP only**, and the flag is a **query parameter**, not a body field.

  Without it, scrobbling something the user already finished records nothing. The server sees a duplicate watch and drops it, so their rewatch count never moves.

  * **On `stop`** the rewatch is immediate, but only when `progress` is **80 or higher**, the same threshold that marks the item watched in the first place. A stop below 80 resolves to `action: "pause"`, marks nothing, and returns no rewatch fields at all, flag or no flag. The response gains `rewatch_id` and `rewatch_status`. Branch on the **status**, never on the id: a session that is already running reports its id *even when it rejected your watch*. `active` / `completed` / `closed` mean it saved. `first_watch`, `too_soon`, `not_eligible`, `pro_required` and `null` explain why it didn't. Note this is a **superset** of the three-value `rewatch_status` on `/sync/all-items`, so widen your enum if you share one.
  * **On `checkin` it is deferred.** A check-in marks nothing watched at request time, so Simkl stores the opt-in and applies it when the check-in matures at the end of the item's runtime. The response carries **no rewatch fields**, so read the session back from `GET /sync/all-items?allow_rewatch=yes` if you need to confirm it. The PRO / VIP check runs at check-in time, and the choice is frozen once the check-in is recorded.

  <Badge color="red" icon="triangle-exclamation">Don't</Badge> **Never send the flag on [`/scrobble/start`](/api-reference/simkl/scrobble-start).** That endpoint auto-finishes the user's *previous* unfinished playback before opening a new one, and it honours the flag while doing it. It can log a rewatch of a **different title**, with nothing in the response to tell you. Gate the flag behind explicit user intent, and ship a *Track rewatches* setting that defaults to off.

  **Also changed:** [`GET /sync/playback`](/api-reference/simkl/get-playback-sessions-all) now returns **in-progress re-viewings**. A paused playback on a title the user already finished is no longer hidden, as long as the pause happened after they finished it. Continue Watching rows can therefore contain completed titles. That is intended, and `hide_watched=true` (still the default) deliberately leaves them in. It only removes playbacks that were *overtaken*: paused first, then marked watched elsewhere. If your app worked around the old behaviour, drop that workaround.

  Full walkthrough, including the four gates to check before enabling and the 2-day gap between watches, in the [**Rewatches guide, Scrobble section**](/guides/rewatches#scrobble) and the [Scrobble guide](/guides/scrobble#rewatches).
</Update>

<Update label="2026-07-21" description="Calendar data files v2 — migrate by 1 Feb 2027" tags={["Breaking change"]}>
  <Badge color="red" icon="triangle-exclamation">Breaking</Badge>

  **Calendar data files v2 — old files stop updating 1 February 2027**

  A new version of the [calendar data files](/api-reference/calendar) is live under a `/v2/` path segment, for all three catalogs and for both the rolling and monthly files:

  ```
  https://data.simkl.in/calendar/v2/tv.json
  https://data.simkl.in/calendar/v2/anime.json
  https://data.simkl.in/calendar/v2/movie_release.json
  https://data.simkl.in/calendar/v2/{YEAR}/{MONTH}/{tv|anime|movie_release}.json
  ```

  **Deadline:** the old files (same paths **without** `/v2/`) keep regenerating until **1 February 2027**. After that date they stop updating permanently and will serve increasingly stale data. Update your code before then.

  What's different in v2:

  * **Split payload.** Each file is now one object with two keys instead of a flat array — `calendar` (one entry per airing or release, chronological) and `metadata` (one record per distinct title, keyed by Simkl ID). A show airing 12 episodes this month appears 12 times in `calendar` but only once in `metadata`, so titles, posters, IDs, ratings, and genres are stored once rather than repeated on every airing. Join the two on `simkl_id`.
  * **All timestamps are UTC.** Every `date` is now ISO 8601 with a trailing `Z`. The old files carried per-catalog offsets (`-05:00` for US TV, `+09:00` for anime), which forced clients to normalize before sorting or comparing.
  * **Finale markers.** A new `finale_type` field flags mid-season (`1`), season (`2`), and series (`3`) finales so you can badge them, `null` otherwise.
  * **Much richer metadata.** Each title now carries alternate titles, fanart, popularity rank, drop rate, watched and plan-to-watch counts, ratings from Simkl plus IMDb or MAL, country, original language, runtime, status, network, genres, and trailer — plus `title_romaji` and `anime_type` on anime, and `dvd_date` on movies.

  The [Calendar page](/api-reference/calendar) has the full field reference and a worked "next 7 days" example in JavaScript and Python.
</Update>

<Update label="2026-05-25" description="Trending SDK with live preview" tags={["Documentation"]}>
  <Badge color="purple" icon="book">Docs</Badge>

  **Trending SDK with live preview**

  A new [Trending SDK](/api-reference/trending) page shows how to combine filters and sorts on top of our cached top-500 trending feeds. Drop-in JS file (also ported to Python, Swift, Kotlin, and Dart) plus an embedded CodePen you can fork.

  * **Two CDN fetches → full home screen** — combined movies + TV + anime in one round-trip, plus DVD releases.
  * **55 default catalog rows** ready to render with attribution-correct titles — Trending Today, Most Watchlisted, Top Rated, Hidden Gems, Just Premiered, Movies This Year, Best of Netflix / HBO / Disney+ / Prime / Apple TV, Best Action / Drama / Sci-Fi movies, Best of the 2020s / 2010s / 2000s / 90s, Marathon-Worthy TV, Quick Watches, Anime Movies and OVAs, and more.
  * **Tunable recipes** (`hiddenGems`, `bestOfNetwork`, `bestOfGenre`, `bestOfDecade`, `currentYear`, `justReleased`, `marathonWorthy`, ...) — override knobs per row via `recipe_opts` or supply a custom `transform` closure.
  * **Persistent cache** (localStorage in browsers, file in Node), conditional 304 revalidation, retry with backoff, request dedup, reactive subscribe / get API. No build step, no framework.
  * Works as-is in browsers, Node 22+, Deno, Bun, React Native, and Cloudflare Workers.

  Scroll to **"Try it live"** on [the trending page](/api-reference/trending) for the embedded CodePen, or open the page to see the full recipe reference.
</Update>

<Update label="2026-05-22" description="Rewatches API" tags={["Major launch"]}>
  <Badge color="green" icon="rocket">Major launch</Badge>

  **Rewatches via `?allow_rewatch=yes`**

  `POST /sync/history` and `GET /sync/all-items` now track rewatches as separate sessions when the caller opts in with `?allow_rewatch=yes`. **Simkl PRO / VIP only.**

  * **Write:** post episodes (or whole items) the user is rewatching. The server auto-detects rewatch intent, or you can force it with `is_rewatch: true`. Each session has its own `rewatch_id`, `rewatch_status` (`active` / `completed` / `closed`), and per-episode timestamps.
  * **Read:** any item with saved sessions appears multiple times in the response — its normal entry plus one rewatch row per session, each carrying `is_rewatch: true` and the session fields above.

  Walkthrough, state-machine rules, and ready-made code for simkl.com-style UI patterns in the new [**Rewatches guide**](/guides/rewatches). Quick mention also in the [Sync guide → Record a rewatch](/guides/sync#common-write-operations) and [Mark as watched → Record a rewatch](/guides/mark-as-watched#record-a-rewatch) accordions.
</Update>

<Update label="2026-05-22" description="API Analytics: live per-app request log" tags={["Major launch"]}>
  <Badge color="green" icon="rocket">Major launch</Badge>

  **API Analytics: live per-app request log**

  Every registered Simkl app now has a built-in Analytics view at [/debug/api-analytics](/debug/api-analytics) showing exactly what your code sent to `api.simkl.com`. Up to 24 hours of individual requests, with edge and origin status codes, latency, burst detection, and CSV export.

  What you can see:

  * **Per-request rows** with method, path, query string, edge HTTP status, origin HTTP status, origin latency, cache status, country, and User-Agent. Each row has a **Link** column that re-fires the same `GET` in a new tab so you can read the live response body.
  * **Burst detector** that flags 3+ requests from the same IP in one second with a red `BURST` badge. Helps you catch retry loops and runaway timers before the auto-blocker fires.
  * **Filters** with `equals`, `contains`, `starts with`, `ends with`, and `in` operators over every column. Common recipe: `Origin contains 4` to surface 4xx failures.
  * **CSV export** of the current filtered view for sharing with support or attaching to bug reports.
  * **One-click mask for sensitive params**: hit a button to redact `code`, `access_token`, `refresh_token`, `pin`, and similar values as `***` before screenshotting or sharing the view.

  How to open it:

  1. Open [simkl.com/settings/developer/](https://simkl.com/settings/developer/) while logged in.
  2. Click your app's row.
  3. Click **Debug** (top-right of the app card). The analytics view opens in a new tab.

  Useful for diagnosing OAuth wrappers that quietly drop headers, sync loops missing `date_from`, silent 4xx errors your HTTP client never surfaces, and stale edge-cache hits.

  Full reference at [API Analytics](/debug/api-analytics).
</Update>

<Update label="2026-05-22" description="Docs revamp: new home at api.simkl.org" tags={["Major launch"]}>
  <Badge color="purple" icon="book">Docs</Badge>

  **Docs revamp**

  [Apiary](https://simkl.docs.apiary.io/) is retired. Oracle is shutting it down in October, and Apiary has been going down for hours at a stretch lately on top of that, so we used the forcing function to redo everything from scratch. The docs now live at [api.simkl.org](https://api.simkl.org). Most of the content is new: what was supposed to be a content migration turned into a top-to-bottom audit when we found significant drift between the old docs and the actual API.

  What's new:

  * **Detailed workflow guides**: [Sync](/guides/sync), [Scrobble](/guides/scrobble), [Search](/guides/search), [Anime](/guides/anime), [Deep linking](/guides/deep-linking), [Mark as watched](/guides/mark-as-watched), and the new [Rewatches](/guides/rewatches) guide.
  * **All three auth flows properly walked through** at [Authentication](/authentication): OAuth 2.0 for server-side web apps, Public PKCE for mobile, SPAs, browser extensions and desktop binaries, and PIN flow for TVs, consoles, CLIs, and media-server plugins.
  * **Conventions pages** for the cross-cutting topics Apiary never covered: [Headers](/conventions/headers), [Pagination](/conventions/pagination), [Dates](/conventions/dates), [Watchlist statuses](/conventions/list-statuses), [Null values](/conventions/null-values), [Images](/conventions/images), [Standard media objects](/conventions/standard-media-objects), [Extended info](/conventions/extended-info), and [CORS](/conventions/cors).
  * **Flat [Errors and status codes](/conventions/errors)** page grouped by HTTP status with cause / fix / example for each.
  * **Interactive try-it-now playground** on all 48 endpoints across 14 categories. Drop in your token, hit Send, get a real response back.
  * **Copy-paste examples in 16 languages**: curl, Python, JS/TS, Kotlin, Swift, Java, Go, C#, Ruby, PowerShell, C/C++, and more.
  * **LLM-friendly**: every page can be copied as raw markdown or opened straight in Claude / ChatGPT / Cursor / VSCode. Full-site [`llms.txt`](/llms.txt) and [`llms-full.txt`](/llms-full.txt) are published.
  * **Full [OpenAPI 3.1 spec](/openapi.json)**: every endpoint and every response shape verified against the live API. Point your SDK generator at it.
  * **One-page [endpoint index](/all-endpoints)** with all 48 endpoints across 14 categories at a glance.
  * **Default three-column layout site-wide** (left sidebar, content, right-rail TOC) so every page has navigable section anchors.
</Update>

<Update label="2026-04-22" description="Required app identification" tags={["Breaking change"]}>
  <Badge color="red" icon="triangle-exclamation">Breaking</Badge>

  **`app-name` and `app-version` are now required**

  Every API request must now include `app-name` and `app-version` URL parameters alongside `client_id`, plus a descriptive `User-Agent` header. This lets us help debug issues faster and prevents accidental blocks.

  ```
  /endpoint?client_id=CLIENT_ID&app-name=my-app-name&app-version=1.0
  ```

  See the updated [Quickstart](/quickstart) and [Headers and conventions](/conventions/headers).
</Update>

<Update label="2026-03-10" description="API terminology: List → Watchlist" tags={["Documentation"]}>
  <Badge color="purple" icon="book">Docs</Badge>

  **Renamed in the docs: "List" → "Watchlist"**

  Simkl's website introduced **Custom Lists** — user-created collections of titles. To avoid confusion with the existing 5-status bucket system (Watching, Plan to Watch, Hold, Dropped, Completed), the API docs now refer to that bucket system as the **Watchlist** instead of "List".

  **Nothing changed at the API level.** The endpoint URL `/sync/add-to-list` is unchanged, request/response shapes are unchanged. Only the docs use the term "Watchlist" now — e.g., the endpoint is referred to as **"Add to Watchlist"** in the sidebar and prose.

  A dedicated Custom Lists API may ship in a future release.
</Update>

<Update label="2026-02-10" description="Trending data files" tags={["New feature"]}>
  <Badge color="blue" icon="sparkles">New feature</Badge>

  **New: Pre-built trending JSON files**

  We've published trending data as static JSON on `data.simkl.in` — no API key required (User-Agent and attribution still required). Useful for "What's hot right now" surfaces without paying any per-user request budget.

  * **Top 100** and **Top 500** lists
  * **Today**, **This Week**, and **This Month** timeframes
  * **Combined**, **Movies**, **TV Shows**, and **Anime** categories
  * **Latest popular DVD releases** list
  * Full title info with multiple IDs

  See the full file index in [Trending data files](/api-reference/trending).
</Update>

<Update label="2026-01-31" description="New endpoint: /scrobble/checkin" tags={["New feature"]}>
  <Badge color="blue" icon="sparkles">New feature</Badge>

  **Manual "fire-and-forget" tracking**

  New endpoint: [`POST /scrobble/checkin`](/api-reference/simkl/scrobble-checkin). Send one request when the user starts watching; Simkl handles the "watching now" bar, runtime tracking, and auto-completion when runtime elapses. One call, then nothing else — useful when you don't have real player events to drive [`/start`](/api-reference/simkl/scrobble-start) / [`/pause`](/api-reference/simkl/scrobble-pause) / [`/stop`](/api-reference/simkl/scrobble-stop).

  | | `/scrobble/checkin` (manual) | `/scrobble/start` / `/pause` / `/stop` (real-time) |
  | - | - | - |
  | **Purpose** | Simple "watching now" status | Active player tracking |
  | **When to call** | Once, when the user starts watching | On real player events (start, pause, stop, resume) |
  | **Auto-complete** | Yes (marks watched on expiry) | No — requires explicit `/stop` |
  | **`progress`** | Optional | Required |

  Use cases: cinema apps, live-TV trackers, social "watching now" status updates. See [Scrobble guide](/guides/scrobble).
</Update>

<Update label="2026-01-15" description="PIN flow polling clarified" tags={["Improvement"]}>
  <Badge color="gray" icon="wrench">Improvement</Badge>

  **PIN poll response — two distinct shapes**

  The `/oauth/pin/{USER_CODE}` polling response now returns one of two explicit JSON shapes (instead of a single shape with mixed semantics): `{ "result": "KO", "message": "Authorization pending" }` while the user hasn't entered the code yet, and `{ "result": "OK", "access_token": "..." }` once they approve. Simpler client-side state machine — branch on `result`.

  See [PIN authentication](/api-reference/pin) for the response schemas.
</Update>

<Update label="2025-10-08" description="Scrobble and Playback APIs launch" tags={["Major launch"]}>
  <Badge color="green" icon="rocket">Major launch</Badge>

  **Major: real-time scrobble + cross-device playback**

  Two big additions for media-player integrations:

  * **Scrobble API** — [`POST /scrobble/start`](/api-reference/simkl/scrobble-start), [`/pause`](/api-reference/simkl/scrobble-pause), and [`/stop`](/api-reference/simkl/scrobble-stop) for real-time playback tracking with progress percentages. See the [Scrobble guide](/guides/scrobble).
  * **Playback API** — [`GET /sync/playback/{type}`](/api-reference/simkl/get-playback-sessions) and [`DELETE /sync/playback/{id}`](/api-reference/simkl/delete-playback) to read and clean up paused sessions across devices.
  * [`GET /sync/activities`](/api-reference/simkl/get-activities) now includes a `playback` timestamp per type, so you know when to refetch playback state.
  * New web UI: [Playback progress manager](https://simkl.com/my/history/playback-progress-manager/) — users can now manage their saved sessions in-product.

  Use case: a user pauses an episode at 40% on the living-room TV, switches to their iPad in the bedroom, and the iPad app picks up exactly where they left off.

  Note: only **one** playback is stored per show/movie. Starting a new episode replaces any previous paused playback for that title.
</Update>

<Update label="2025-08-15" description="Calendar files expanded" tags={["New feature"]}>
  <Badge color="blue" icon="sparkles">New feature</Badge>

  **Monthly archive endpoints**

  The [Calendar JSON](/api-reference/calendar) now includes per-month archives for the last 12 months at the URL pattern `https://data.simkl.in/calendar/{YEAR}/{MONTH}/{tv|anime|movie_release}.json`. The current month regenerates every 6 hours; previous months once a day.
</Update>

<Update label="2025-06-01" description="Image proxy via wsrv.nl" tags={["Documentation"]}>
  <Badge color="purple" icon="book">Docs</Badge>

  **Recommended image hosting: wsrv.nl**

  We now recommend serving Simkl image paths through [wsrv.nl](https://wsrv.nl), a free image proxy that caches, resizes, and converts on the fly. This minimizes load on Simkl's image servers and gives you free transformations like WebP conversion.

  See [Images](/conventions/images) for the URL pattern and size codes. Examples append `&q=90` for origin-equivalent quality (wsrv's default `&q=80` reduces filesize and quality).
</Update>

<Update label="2025-05-29" description="runtime field on /sync/all-items" tags={["Improvement"]}>
  <Badge color="gray" icon="wrench">Improvement</Badge>

  **`runtime` (minutes) on shows and movies**

  `/sync/all-items` responses now include a `runtime` integer field on show and movie items — minutes per episode for shows, total runtime for movies. Useful for "time to finish", watch-time stats, and pacing surfaces.

  ```json theme={"theme":{"light":"github-light","dark":"vesper"}}
  {
    "runtime": 40
  }
  ```
</Update>

<Update label="2025-02-25" description="simkl_type and anime_type in /sync/history response" tags={["Improvement"]}>
  <Badge color="gray" icon="wrench">Improvement</Badge>

  **`simkl_type` and `anime_type` in `/sync/history` response**

  The per-item `response` object inside `added.statuses` now includes `simkl_type` (`movie` / `tv` / `anime`) and `anime_type` (`tv` / `special` / `ova` / `movie` / `music video` / `ona`).

  ```json theme={"theme":{"light":"github-light","dark":"vesper"}}
  "response": {
    "status": "completed",
    "simkl_type": "anime",
    "anime_type": "movie"
  }
  ```

  Useful when you're sending titles by TMDB ID and need to know how Simkl classified the item — e.g., a TMDB movie that Simkl resolved as an anime movie. Also handy for clients that store `simkl_type` locally so they can later reconcile deletions across types.
</Update>

<Update label="2025-02-05" description="/sync/history: status field + use_tvdb_anime_seasons" tags={["Improvement"]}>
  <Badge color="gray" icon="wrench">Improvement</Badge>

  **Complete a show in one call**

  Two additions to `POST /sync/history`:

  * **`status: "completed"`** — pass on a show item to set the watchlist status directly. If the show is still airing, Simkl keeps it in **Watching** instead and reflects the resolved status in the response.
  * **`use_tvdb_anime_seasons: true`** — pair with `status: "completed"` on anime to auto-mark every season watched, using TVDB/TMDB seasonal numbering. Lets clients that index against TVDB skip the AniDB season-mapping step.

  The response now returns a `statuses` array showing the resolved per-item status:

  ```json theme={"theme":{"light":"github-light","dark":"vesper"}}
  {
    "added": {
      "movies": 0,
      "shows": 1,
      "episodes": 0,
      "statuses": [
        {
          "request": { "ids": { "simkl": 39687 }, "status": "completed" },
          "response": { "status": "watching" }
        }
      ]
    }
  }
  ```

  See [`POST /sync/history`](/api-reference/simkl/add-to-history).
</Update>


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