Skip to main content
null in a Simkl response is never noise — it’s one of five specific signals. This page is the master reference; schema descriptions link back here by type.

Type 1

Never happened yet — event hasn’t fired

Type 2

Doesn’t apply — key omitted entirely

Type 3

Empty result — whole response null

Type 4

Unknown — data not on file

Type 5

End state — journey complete

Type 1 — Never happened yet

Field present, value null — the event hasn’t fired for this user yet, but the field is a normal part of the model. Once it happens, the value flips to a real value and never reverts to null.
Where you’ll see it Handling

Type 2 — Doesn’t apply to this type

Key omitted entirely — not set to null, the property doesn’t exist on this object. Iterating keys won’t return it; 'fieldName' in obj returns false.
Where you’ll see it See Watchlist statuses for the full per-type matrix.
/sync/all-items/anime entries wrap in show:, by design. Each item in the anime array nests its media object under a key called show:, not anime:. This is intentional cross-catalog compatibility: apps that source their data from TMDB or TVDB treat anime as shows (those catalogs have no separate “anime” concept), so the show: wrapper keeps Simkl’s sync responses drop-in compatible with code written against TMDB/TVDB-shaped data. Anime-only fields (anime_type, mal/anidb/anilist/kitsu IDs) sit alongside on the outer item and on show.ids respectively.
Handling

Type 3 — Empty result set

The whole response is null or an empty array — the endpoint accepted the request but there are no items to return. The URL is valid; the call returns 200; the body is just empty.
Where you’ll see it Handling

Type 4 — Unknown / data not on file

Field present, value null — the data point is supposed to have a value but Simkl genuinely doesn’t have it yet. Render a placeholder; don’t display the literal null.
Where you’ll see it
network reflects the upstream catalog (TVDB). The value is what TVDB classifies the record under — for streaming-only shows TVDB lists the platform as the “network” ("Prime Video", "Netflix", "Apple TV+"), for traditional broadcast it’s the broadcaster ("HBO", "AMC", "CBS"). Don’t infer distribution model from this field; treat it as an opaque display string from the source catalog.
null vs empty string. Older Simkl records sometimes return an empty string "" for a field rather than null (e.g. en_title: "" on classic anime, before the API standardized on null). Both mean Type 4 — data not on file. Check both:
memo: {} is a Type 4 variant. On GET /sync/all-items?memos=yes, items the user has set a memo on return:
Items without a memo return an empty object:
NOT null, NOT missing — {}. The empty-object marker keeps the key shape consistent (client code can always do item.memo.text || '' without an existence check or a null-coalesce). When ?memos=yes is NOT set, the memo key is omitted entirely (Type 2).
Handling

Type 5 — End state reached

Field present, value null — the value used to be meaningful but the workflow has ended; the field is intentionally cleared. Render the “complete” state in your UI.
Where you’ll see it Handling

Empty / Missing response shape — by endpoint

The API uses five different shapes for “this request was valid but there’s no data”. The table below tells you what to expect at each endpoint so a single parsing path can handle them all.

The five shapes you’ll meet

200 [ ]

Empty array. Safe to iterate.

200 null

Top-level null. r.json() returns None.

200 { }

Empty object. len() is 0.

301 redirect

Answer in Location: header. No body.

200 fallthrough

Server returned unrelated discover data instead of an error.
Status-code lies. Five endpoints return 200 OK with an empty shape rather than 404 Not Found. Branch on the body shape, not just the status — see the rows tagged in the table below.Conversely, three CDN endpoints (/calendar/..., /discover/...) return 404 with a plain string body, not the JSON error envelope the rest of the API uses. r.json() will raise on these — wrap the parse.

Per-endpoint shape matrix

Defensive parser pattern

If you’re writing a generic Simkl client wrapper, this defensive pattern handles every shape above:

Pinning a shape in your code

If your integration depends on a specific row of this table, pin it as a comment in your code. When the table changes on a future doc release, your in-code reference makes the diff easier to spot during a routine dependency-update review.

Quick decision table

Watchlist statuses

Which statuses are valid per type. The reason watching and hold are omitted from movie blocks (Type 2).

Standard media objects

Field-by-field shape of Movie / Show / Anime / Episode — which fields can be null, which are always present.

Dates and timezones

ISO-8601, the Z suffix, and the 1970-01-01T00:00:01Z “very long time ago” placeholder.

Errors

How error envelopes differ from null responses, and when you’ll see each.