Record watch events. The unit is the watch event — adding “I watched The Walking Dead S01E01 at 8pm” — not list membership (use POST /sync/add-to-list for that, or set status per-item here to do both at once).
You don’t need a Simkl ID. Same as /sync/add-to-list — the server resolves any combination of identifiers (imdb, tmdb, tvdb, mal, anidb, anilist, kitsu) plus title + year. See Standard media objects → Supported ID keys for the full list, and the /sync/add-to-list ID-resolution table for per-slot semantics.
The body shape determines whether you mark a single episode, a season, or a whole show.
One movie — single movie completion.
{ "movies": [{ "ids": {...} }] }
Whole show — every episode marked. “I finished this whole series.” Send status: "completed" with no seasons / episodes.
{ "shows": [{ "ids": {...}, "status": "completed" }] }
Whole season — every episode of one season. “I finished season 2.” Send seasons[] without an inner episodes.
{ "shows": [{ "ids": {...}, "seasons": [{ "number": 2 }] }] }
Specific episodes only — per-event scrobbling, manual tick-off.
{
"shows": [{
"ids": {...},
"seasons": [{
"number": 1,
"episodes": [{ "number": 1 }, { "number": 2 }]
}]
}]
}
Top-level episodes[] shorthand — auto-wraps to seasons: [{ number: 1, ... }]. Useful for anime sequential numbering and single-season shows.
{ "shows": [{ "ids": {...}, "episodes": [{ "number": 1 }] }] }
The response always reports the actual count of episodes affected in added.episodes, so apps can verify the server expanded correctly.
This endpoint is the only way to set a memo on an item. POST /sync/add-to-list accepts the memo field in the request body and echoes it back in the response, but does not persist it — silently discarded.
To set or update a memo without recording a watch event, send ids + status + memo:
{
"movies": [{
"ids": { "simkl": 53536 },
"status": "plantowatch",
"memo": { "text": "Remind me why I added this", "is_private": true }
}]
}
Two behaviors worth knowing:
watching / plantowatch / hold / dropped / completed) for the memo to stick. Sending status makes this explicit.status AND saves the memo in one shot. added.movies / added.shows reports the count of newly-added items (0 when the item was already there and you only changed memo/status).To read memos back, call GET /sync/all-items?memos=yes — see the Sync guide.
| Field | Type | Notes |
|---|---|---|
watched_at | ISO-8601 string | Pin the watch event to a specific time. Defaults to request time. |
added_at | ISO-8601 string | Override when the item was added to the watchlist (rarely used outside backups). |
status | string | Set the watchlist status (watching/plantowatch/hold/completed/dropped) in the same call. Combine with rating to do “watched + rate + status” in one request. |
rating | int 1-10 | Rate the item alongside the watch event. Same effect as a separate POST /sync/ratings call. |
memo | { "text": string, "is_private": bool } | User memo, max 140 chars. is_private: false shows the memo on the user’s public profile + activity feed; true keeps it self-only. Read-back requires /sync/all-items?memos=yes. |
is_rewatch | bool | Force the rewatch path on this item even if the server can’t auto-detect (used by backup/restore tools). Requires ?allow_rewatch=yes query param to take effect. |
use_tvdb_anime_seasons | bool (anime-only, optional) | Default false (AniDB sequential — flat single-season). Set true to interpret season/number as TVDB per-season numbering. Only needed when your source uses TVDB-style numbering AND the title is multi-season in TVDB (e.g. Demon Slayer S2 Entertainment District). Single-season anime and AniDB-sequential inputs work without this flag. Use when syncing from Plex/Sonarr/Kodi/Jellyfin. |
Re-posting an already-watched episode is a no-op by default — the server detects the duplicate and skips. To insert a rewatch session, send ?allow_rewatch=yes as a query parameter. The server then creates a separate rewatch row that doesn’t double-count the original watch.
For backup/restore tools that always want to insert (even when the auto-detect heuristic can’t fire), set is_rewatch: true per-item AND pass the query param.
added and not_found{
"added": {
"movies": <int count>,
"shows": <int count>,
"episodes": <int count>,
"statuses": [
{
"request": { /* echo of input item, with type added */ },
"response": {
"status": "completed",
"simkl_type": "tv" | "anime" | "movie",
"anime_type": "tv" | "movie" | "ova" | null
}
}
]
},
"not_found": {
"movies": [...],
"shows": [...],
"episodes": [...]
}
}
added.statuses[*].response.simkl_type tells you which catalog the item resolved to (useful when you sent ambiguous IDs — TMDB IDs can be either movie or tv on Simkl). Always inspect to know what got created.
not_found carries the verbatim input for items the resolver couldn’t match (typo, fuzzy-title miss, ID not in Simkl’s catalog yet). Apps should:
not_found.movies.length === 0 && not_found.shows.length === 0 && not_found.episodes.length === 0400 empty_field if a per-item required field is missing. 400 wrong_parameter for invalid enum values. Empty body {} returns 201 with zero counts (NOT 400) — that’s a known asymmetry vs /scrobble/start which 400s on empty body.
Initial-pull-then-delta-loop pattern, date_from semantics, deletion reconciliation, Trakt/Letterboxd migration recipes.
/sync/history vs /sync/add-to-list| Goal | Endpoint |
|---|---|
| User finished watching → mark watched + rate + memo | /sync/history (carries all three in one shape) |
| User clicked “Add to Plan to Watch” button | /sync/add-to-list (status-only, no watch event) |
| Backfill from Trakt/Letterboxd/IMDb (events with timestamps) | /sync/history with watched_at per item |
| Bulk import a watchlist (no watch events) | /sync/add-to-list |
| Remove an item from the user’s library | /sync/history/remove |
Which IDs can I send/expect? All accepted input identifiers and the keys you’ll see echoed back in responses are listed at Standard media objects → Supported ID keys. Send every ID you have on writes — Simkl picks the first that resolves and ignores the rest. Reminder: slug is response-only (never send it on a request).
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.
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.
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).
Opt into rewatch tracking. When yes, POST /sync/history records an additional rewatch session instead of being a no-op for already-watched items, and GET /sync/all-items returns one extra entry per saved rewatch session alongside the item's normal entry. Available to Simkl Pro and VIP users — non-Pro callers see no effect even with the flag set.
⚠️ Do not enable this flag until you've read the Rewatches guide end-to-end and implemented the precautions. Used carelessly (on retries, on every scrobble event, on importer re-runs, without pinning rewatch_id after the first write), it will pollute the user's history stats and rewatches panel with phantom sessions. The flag should be gated behind explicit user intent — a dedicated "Rewatch" button — never on background or automated flows. Also expose a per-user Track rewatches toggle in your app's settings (default off) — not every user wants the rewatch-session complexity.
Limits: up to 50 rewatches per item (movie, show, or anime), and any two watch events on the same item (movie or episode) must be at least 2 days apart — a new rewatch within 48 hours of the previous watch of that same item collapses into the same session (it's a rewatch, not a rewind 😄). Full walkthrough — session lifecycle (active / completed / closed with bidirectional transitions), episode-level tracking, reading sessions back from GET /sync/all-items, and ready-made code for simkl.com-style UI patterns — in the Rewatches guide.
yes, no When yes, suppresses the implicit episode auto-fill that happens when you POST a show without explicit seasons/episodes. Use when the client manages episode-level state itself.
yes 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 for /sync/history and /sync/history/remove. 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.
Hide child attributes
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
153021
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{
"simkl": 53536,
"imdb": "tt0181852",
"tmdb": 296
}
"Terminator 3: Rise of the Machines"
2003
Hide child attributes
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
153021
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{
"simkl": 53536,
"imdb": "tt0181852",
"tmdb": 296
}
"The Walking Dead"
2010
Hide child attributes
Hide child attributes
x >= 01
x >= 12
ISO-8601 GMT timestamp.
"2014-09-01T09:10:11Z"
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
153021
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{
"simkl": 53536,
"imdb": "tt0181852",
"tmdb": 296
}
Top-level convenience array for marking episode-level history without nesting. The server wraps each entry into a synthetic single-season show. Each item carries the same shape as items under shows[].seasons[].episodes[] plus the parent show reference.
Array of anime entries (same shape as shows[]).
Hide child attributes
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
153021
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{
"simkl": 53536,
"imdb": "tt0181852",
"tmdb": 296
}
"The Walking Dead"
2010
Hide child attributes
Hide child attributes
x >= 01
x >= 12
ISO-8601 GMT timestamp.
"2014-09-01T09:10:11Z"
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
153021
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{
"simkl": 53536,
"imdb": "tt0181852",
"tmdb": 296
}
Success
Response from POST /sync/history. Note: not_found.shows includes any anime entries that failed to resolve, regardless of whether they were sent under anime[] or shows[] — there is no separate not_found.anime bucket.
Hide child attributes
New items added to the user's Completed movies list.
New shows added to the user's library.
Number of episodes marked watched.
Hide child attributes
Echo of the input item.
Hide child attributes
Resulting list status.
watching, plantowatch, hold, dropped, completed, removed movie, tv, anime Type 4 null — data not on file in that field's slot. See Null and missing values.
Present when a rewatch session was created or resumed by this call. Use this value on subsequent writes to update the same session.
Lifecycle state of the rewatch session referenced by rewatch_id.
active, completed, closed Items Simkl could not match. Inspect this to surface diagnostics back to your users. Anime entries that fail to resolve land in shows regardless of which top-level array they were sent under — there is no separate anime bucket in not_found.
Hide child attributes
Hide child attributes
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
153021
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{
"simkl": 53536,
"imdb": "tt0181852",
"tmdb": 296
}
"Terminator 3: Rise of the Machines"
2003
Hide child attributes
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
153021
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{
"simkl": 53536,
"imdb": "tt0181852",
"tmdb": 296
}
"The Walking Dead"
2010
Hide child attributes
Hide child attributes
x >= 01
x >= 12
ISO-8601 GMT timestamp.
"2014-09-01T09:10:11Z"
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
153021
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{
"simkl": 53536,
"imdb": "tt0181852",
"tmdb": 296
}
Hide child attributes
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
153021
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{
"simkl": 53536,
"imdb": "tt0181852",
"tmdb": 296
}
"The Walking Dead"
2010
Hide child attributes
Hide child attributes
x >= 01
x >= 12
ISO-8601 GMT timestamp.
"2014-09-01T09:10:11Z"
External and internal identifiers for an item. Pass as many as you have — Simkl resolves to the canonical record.
Hide child attributes
Simkl internal ID. Most reliable.
53536
URL-safe slug returned in responses.
"attack-on-titan"
IMDb ID.
"tt0181852"
TMDb ID.
"296"
TVDB ID or slug.
153021
MyAnimeList ID.
"4246"
AniDB ID. Specifying just this is enough for anime lookups.
"10846"
AniList ID.
"21"
Kitsu ID.
"12"
aniSearch ID.
"2227"
Anime-Planet slug.
"one-piece"
LiveChart ID.
"321"
Letterboxd slug.
"the-truman-show"
Netflix movie ID.
"70210890"
Hulu episode ID.
Crunchyroll episode ID.
Trakt slug.
"john-wick-chapter-4-2023"
{
"simkl": 53536,
"imdb": "tt0181852",
"tmdb": 296
}
Was this page helpful?