Add to Watchlist
Move an item into one of the user’s Watchlist statuses. The body’s per-item to field selects the destination:
Most apps prefer POST /sync/history for sync flows. Use this endpoint (/sync/add-to-list) only when your app has explicit “Add to Plan to Watch” / “Move to Hold” UI buttons — i.e. setting watchlist status without recording a watch event. For backfill from another tracker, scrobbling, or marking-watched UI, send to /sync/history instead (which carries status, rating, memo, AND the watch event in one shape).
Don’t chain /sync/history then /sync/add-to-list — the history call already moves the item. POST /sync/history automatically places (and re-classifies) the item on the right Watchlist based on the watch event. Read added.statuses[].response.status in the history response to see the resolved status — e.g. a "completed" write on a still-airing show is silently downgraded to "watching" and reflected there. A follow-up /sync/add-to-list call is redundant and may overwrite the server’s smarter decision.
See the Sync guide for the two-phase pull/delta pattern.
to value | Destination |
|---|---|
watching | Currently watching. (For movies, automatically becomes completed since movies are atomic.) |
plantowatch | Plan to watch. |
hold | On hold. |
dropped | Dropped. |
completed | Completed. |
to is per-item (each entry in the array carries its own destination). The legacy top-level to shape is not accepted — the server returns 400 empty_field (Missed "to" parameter) when to only appears at the top level.
{
"movies": [
{
"to": "completed",
"title": "Inception",
"year": 2010,
"ids": {
"simkl": 472214,
"imdb": "tt1375666",
"tmdb": "27205"
}
}
],
"shows": [
{
"to": "watching",
"title": "Game of Thrones",
"year": 2011,
"ids": {
"simkl": 17465,
"slug": "game-of-thrones",
"imdb": "tt0944947",
"tmdb": "1399",
"tvdb": "121361"
}
}
]
}
Optional per-item fields: watched_at, added_at.
Response: added and not_found
The response always returns 201 (even on partial failures) with two arrays per media-type:
{
"added": {
"movies": [{ "to": "completed", "ids": {...}, "type": "movie" }],
"shows": [{ "to": "watching", "ids": {...}, "type": "show" }]
},
"not_found": {
"movies": [{ "title": "Definitely Not A Real Movie", "year": 9999 }],
"shows": []
}
}
Items the server’s resolver matched land in added. Items it couldn’t match (typos, fuzzy title misses, IDs not in Simkl’s catalog yet) land in not_found — verbatim copies of the input so you can show “we couldn’t add: …” in your UI. Always inspect both arrays after a bulk call.
Errors: 400 empty_field if to is missing on an item; 400 wrong_parameter if to is not one of the values above.
Silent to rewrites
The server may downgrade your requested to value when an item isn’t in a state where that status applies:
- Movies with
to: "watching"→ silently rewritten tocompleted(movies are atomic; you can’t “be watching” a movie). - Shows that aren’t ready for
completed(still airing, or pre-release) get rewritten towatchingorplantowatchrespectively, depending on whether any episode has aired.
The rewrites happen server-side; the consumer just sees the actual stored value in added.<type>[i].to. There is no error code surfaced for the rewrite — the only way to detect it is to compare the value you sent against the value that came back.
Note: this endpoint operates on the Simkl Watchlist (the five canonical statuses above). Custom user-created lists will get their own API in a future release.
Sync guide — full walkthrough
Two-phase model (initial pull → activities-checked delta loop), date_from semantics, deletion reconciliation, edge cases, and reference implementations in Node and Python.
Anime titles: can go in either the anime[] array OR the shows[] array — both are accepted. The server normalizes anime into the response’s shows array with "type": "show" per-item, since anime are TV-like in the cross-catalog data model. AniDB-specific IDs (mal, anidb, anilist, kitsu) belong inside each item’s ids object regardless of which array it lives in. See Anime under shows[].
Authorizations
Preferred form: your client_id as a URL query parameter on every request. Self-describing in logs and curl commands. See Headers and required parameters.
OAuth 2.0 or PIN-flow access_token. Required for endpoints that read or modify the user's library, scrobble session, ratings, settings, or playbacks. See Authentication.
Headers
Descriptive identifier for your app, ideally name/version. Examples: PlexMediaServer/1.43.1.10540, kodi-simkl/0.9.2, MyApp/2.4.1 (https://myapp.com).
Query Parameters
Your client_id from your Simkl developer settings. Required on every request.
Short, lowercase identifier for your app (e.g. plex-scrobbler, kodi-bridge). Helps Simkl identify which apps are using the API.
Your app's current version (e.g. 1.0, 2.4.1). Helps Simkl debug issues you report.
Body
Body for POST /sync/add-to-list. Moves items into one of the user's Watchlist statuses. Items go under movies[], shows[], or anime[] — Simkl resolves anime titles correctly under either shows[] or anime[], so match the field to your data type when known.
Response
OK
Response from POST /sync/add-to-list.