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

# Standard media objects

> The shape of Movie, Show, Anime, and Episode objects every endpoint accepts and returns.

Every endpoint that talks about a movie, show, anime, or episode uses the same JSON shapes. Learn them once and you can read or write any endpoint.

## The `ids` object

The `ids` object is the heart of every media object. It's how Simkl identifies what you mean across all the catalogs you can integrate with. Pass as many IDs as you have — Simkl resolves to the canonical record and returns the rest.

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "ids": {
    "simkl": 53536,
    "imdb":  "tt0181852",
    "tmdb":  296
  }
}
```

### Supported ID keys

<Warning>
  **Read either `ids.simkl` or `ids.simkl_id` — same integer, two key names.** Depending on the endpoint, Simkl's canonical catalog ID comes back as `ids.simkl` or `ids.simkl_id`. Robust reader code accepts both:

  ```js theme={"theme":{"light":"github-light","dark":"vesper"}}
  const id = item.ids.simkl ?? item.ids.simkl_id;
  ```

  The integer is **globally unique and permanent** — use it as your primary key for caching and cross-referencing.

  **`slug` is NOT unique** — multiple titles can share one (e.g. three different *Superman* movies live at `/movies/2059585/superman`, `/movies/260740/superman`, `/movies/951252/superman`). The slug is a URL hint only; never identify a record by slug alone.

  **External IDs in `ids` (`imdb`, `tmdb`, `tvdb`, `mal`, `anidb`, …) are echo-only.** To resolve one to a Simkl record, use [`/redirect`](/api-reference/simkl/redirect) — it returns the canonical Simkl URL with the `simkl` ID baked in.

  **`tmdb` is the one external ID that isn't globally unique** — TMDB keeps separate sequences for `movie` and `tv`, so the same numeric `tmdb` value can refer to either. When resolving a TMDB ID via [`/redirect`](/api-reference/simkl/redirect), always pass `type=movie` or `type=tv` alongside `tmdb`. When reading TMDB IDs back from responses, treat the container key (`movies` / `shows` / `anime`) as part of the ID's identity.
</Warning>

| Key           | Type         | Example                                                                                                                                                                    |
| ------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `simkl`       | integer      | `53536` — Simkl's canonical ID. **Globally unique and permanent.**                                                                                                         |
| `imdb`        | string       | `tt0181852`                                                                                                                                                                |
| `tmdb`        | integer      | `296` — **Not unique across types**; always pair with `type=movie` or `type=tv` when resolving via `/redirect`. TMDB has no anime type; anime is filed under `tv` on TMDB. |
| `tvdb`        | int / string | `153021` or `the-walking-dead`                                                                                                                                             |
| `mal`         | integer      | `4246`                                                                                                                                                                     |
| `anidb`       | integer      | `10846`                                                                                                                                                                    |
| `anilist`     | integer      | `21`                                                                                                                                                                       |
| `kitsu`       | integer      | `12`                                                                                                                                                                       |
| `anisearch`   | integer      | `2227`                                                                                                                                                                     |
| `animeplanet` | string       | `one-piece`                                                                                                                                                                |
| `livechart`   | integer      | `321`                                                                                                                                                                      |
| `letterboxd`  | string       | `the-truman-show`                                                                                                                                                          |
| `netflix`     | integer      | `70210890`                                                                                                                                                                 |
| `traktslug`   | string       | `john-wick-chapter-4-2023`                                                                                                                                                 |
| `crunchyroll` | integer      | `656647` (episode-level)                                                                                                                                                   |
| `hulu`        | integer      | `681868` (episode-level)                                                                                                                                                   |

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

## Movie

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "title": "Terminator 3: Rise of the Machines",
  "year":  2003,
  "ids": {
    "simkl": 53536,
    "imdb":  "tt0181852",
    "tmdb":  296
  }
}
```

When `simkl` is known, the minimum is just:

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{ "ids": { "simkl": 53536 } }
```

## Show

A show object can include nested seasons and episodes for partial sync:

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "title": "The Walking Dead",
  "year":  2010,
  "ids": {
    "simkl": 2090,
    "tvdb":  153021,
    "imdb":  "tt1520211"
  },
  "seasons": [
    {
      "number": 1,
      "episodes": [
        { "number": 1 },
        { "number": 2 }
      ]
    }
  ]
}
```

## Anime

Same JSON shape as Show, plus `anime_type` and an optional flat `episodes` list. Simkl uses AniDB as its primary metadata source — episodes follow the **anime-native model** by default (each cour is its own title, episodes restart at 1). Simkl also accepts Western TVDB/TMDB-style `season` + `number` coordinates and cross-maps both to the same canonical episode.

<Card title="Handling anime — full guide" icon="dragon" href="/guides/anime" horizontal>
  Two integration paths (TMDB/TVDB-primary with `use_tvdb_anime_seasons`, or anime-native with AniDB/MAL/AniList/Kitsu IDs), the cross-mapping rules, write/read recipes, and edge cases like Attack on Titan and Solo Leveling.
</Card>

<Tip>
  **Anime can go under either `shows[]` / `show` or `anime[]` / `anime`.** Simkl resolves items by their `ids` first, then routes to the correct catalog — the key you pick is for clarity, not routing. Match the field to your data type when known; fall back to `shows` / `shows[]` when you only have TMDB / TVDB IDs.

  | Endpoint family                                                                                                                                                                                                                                                                                         | Accepted top-level keys                                                   |
  | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
  | [`/scrobble/start`](/api-reference/simkl/scrobble-start), [`/pause`](/api-reference/simkl/scrobble-pause), [`/stop`](/api-reference/simkl/scrobble-stop), [`/checkin`](/api-reference/simkl/scrobble-checkin)                                                                                           | `movie`, `show`, `anime`, `episode` *(singular objects, one per request)* |
  | [`/sync/history`](/api-reference/simkl/add-to-history), [`/sync/history/remove`](/api-reference/simkl/remove-from-history), [`/sync/add-to-list`](/api-reference/simkl/add-to-list), [`/sync/ratings`](/api-reference/simkl/add-ratings), [`/sync/ratings/remove`](/api-reference/simkl/remove-ratings) | `movies[]`, `shows[]`, `anime[]`, `episodes[]` *(plural arrays, batched)* |
  | [`/sync/watched`](/api-reference/simkl/get-watched)                                                                                                                                                                                                                                                     | top-level array of items, no wrapping key                                 |

  **Anime-only IDs work anywhere** — `anidb`, `mal`, `anilist`, `kitsu`, `anisearch`, `animeplanet`, `livechart` inside `ids` resolve correctly whether the wrapper is `shows[]` or `anime[]`. For example, either of these adds Attack on Titan:

  ```json POST /sync/history — either form works theme={"theme":{"light":"github-light","dark":"vesper"}}
  { "anime": [ { "ids": { "anidb": 9541 } } ] }

  { "shows": [ { "ids": { "anidb": 9541 } } ] }
  ```

  **Caveat: `not_found` always buckets under `shows`.** If a write fails to resolve, the response's `not_found.shows` array carries the failed entry regardless of whether you sent it under `shows[]` or `anime[]`. There is no `not_found.anime` bucket.
</Tip>

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "title": "Attack on Titan",
  "year":  2013,
  "anime_type": "tv",
  "ids": {
    "simkl": 39687,
    "mal":   16498,
    "tvdb":  267440,
    "imdb":  "tt2560140",
    "anidb": 9541
  },
  "episodes": [
    { "number": 1 },
    { "number": 2 }
  ]
}
```

### `anime_type`

Anime items include an `anime_type` field with one of these values:

* `tv`
* `special`
* `ova`
* `movie`
* `music video`
* `ona`

## Episode

Inside a `seasons[].episodes[]` array (or a flat `episodes` array), an episode is identified by `season` + `number`, **or** directly by `ids`. Optionally include `watched_at` to record when it was watched.

<Tip>
  **Prefer `season` + `number` over episode IDs whenever you can.** The numeric `S1E4` coordinate is stable forever — it never changes once an episode airs. External episode IDs (TVDB / AniDB) get re-issued when the source catalog merges duplicates, re-numbers a season, or replaces a record, and **a stale episode ID returns `404`**. The `season` + `number` pair is the cheapest, most robust identifier for scrobble / sync writes — only fall back to `episode.ids` when your integration genuinely doesn't know which season/number it's dealing with (e.g. Plex-style scrapers that resolve a file to a single episode ID without season context).
</Tip>

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "watched_at": "2014-09-01T09:10:11Z",
  "season": 1,
  "number": 4
}
```

If you only have an episode ID (no season/number), pass it via `ids`. For episode-ID lookups in scrobble / sync writes, Simkl accepts **only `tvdb` and `anidb`** at the episode level. If both are sent, `tvdb` is tried first. Episode-level `imdb` and `tmdb` IDs do **not** exist on Simkl — those external services only assign IDs at the show/movie level.

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "watched_at": "2014-09-01T09:10:11Z",
  "ids": {
    "tvdb":  4274616,
    "anidb": 142278
  }
}
```

<Note>
  Catalog responses (e.g. `/tv/episodes/{id}`) may **echo** additional episode-level keys like `simkl` (Simkl's internal episode ID), `hulu`, or `crunchyroll`. Those are informational — they're returned for cross-reference but are not accepted for episode-ID lookup. Always send `season` + `number` (preferred) or `tvdb` / `anidb` when identifying an episode in a request.
</Note>

## Tips for sending media objects

<AccordionGroup>
  <Accordion title="Pass as much identifying data as you can">
    `title`, `year`, and any IDs you have. Simkl uses all of it to disambiguate. If two movies share a title and year, IDs save the day.
  </Accordion>

  <Accordion title="When sending IDs, simkl wins">
    If you send `simkl: 53536` and `imdb: "tt9999999"` and they disagree, Simkl uses the `simkl` ID. Send only one source of truth when you can.
  </Accordion>

  <Accordion title="Anime episode numbering — read this once">
    Simkl uses the anime-native model (each cour is its own title, episodes restart at 1) — same as AniDB / MAL / AniList / Kitsu / AniSearch / Anime-Planet / LiveChart / ANN. Western TV catalogs (TVDB / TMDB / IMDB) use the franchise-with-seasons model instead. The Scrobble and Sync endpoints accept both schemes and Simkl maps automatically — the response includes both `season`/`number` (anime-native) and `tvdb_season`/`tvdb_number` (Western style) for reference. See [Anime episode numbering](/guides/scrobble#anime-episode-numbering) for the full mapping rules.
  </Accordion>
</AccordionGroup>
