Skip to main content

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.

All API dates are returned in ISO 8601 format with the Z (UTC / GMT) timezone marker:
2015-03-15T15:30:11Z
Some legacy endpoints still return dates in GMT-05:00 (New York) — this is being phased out, but for now treat the timezone field as authoritative rather than assuming UTC.
Convert to the user’s local timezone in your app — don’t assume the server’s locale.
const utc = new Date('2015-03-15T15:30:11Z');
const local = utc.toLocaleString();        // user's locale

Per-endpoint format matrix

Different endpoints use different date formats depending on whether the field is a user activity timestamp, a broadcast schedule, or a catalog release date. Use the per-field reference below to know what to parse for each one. The four formats you’ll meet:
FormatExampleWhen
ISO_UTC_Z2026-05-13T16:08:00ZDefault for user-generated timestamps (activities, watchlist, playback, ratings, profile). Convert to the user’s profile timezone for display.
ISO_WITH_OFFSET2010-10-31T21:00:00-05:00Broadcast schedules — the offset is the originating network’s timezone (US shows are -05:00, anime are +09:00). Use it as authoritative; don’t assume UTC.
DATE_ONLY2010-07-15Catalog release dates (year-month-day, no time).
YEAR_ONLY2010Integer year-of-release on every catalog item.
Plus the 1970-01-01T00:00:01Z placeholder for “watched, date unknown” — see the section below.

Endpoint → field → format

EndpointField(s)FormatNotes
GET /movies/{id}released, release_dates[*].results[*].release_dateDATE_ONLYCatalog release; no time component.
GET /tv/{id}first_aired, last_airedISO_UTC_ZSeries-level air-window markers.
GET /anime/{id}first_aired, last_airedISO_UTC_ZSame as TV.
GET /tv/episodes/{id}[*].dateISO_WITH_OFFSETPer-episode air time in the network’s local timezone (e.g. -05:00 for US shows).
GET /anime/episodes/{id}[*].dateISO_WITH_OFFSETPer-episode air time in +09:00 (Asia/Tokyo) for the canonical Japanese broadcast.
GET /tv/airing[*].dateISO_WITH_OFFSETOriginating network’s timezone.
GET /anime/airing[*].dateISO_WITH_OFFSETAsia/Tokyo for Japan-originated anime.
GET /sync/activitiesevery *_at field (all, tv_shows.completed, anime.dropped, etc. — 27 fields total)ISO_UTC_ZAll UTC; convert to profile timezone for display.
GET /sync/all-items/{type}<type>[*].added_to_watchlist_at, [*].last_watched_at, [*].user_rated_atISO_UTC_ZAll user-activity timestamps in UTC.
GET /sync/playback/{type}[*].paused_atISO_UTC_ZPause-event timestamp in UTC.
POST /users/settingsuser.joined_atISO_UTC_ZAccount creation date.
GET /calendar/{type}.json (CDN)[*].dateISO_WITH_OFFSETEpisode-level air time.
GET /calendar/{type}.json (CDN)[*].release_dateDATE_ONLYCatalog release date alongside the air-time entry.
GET /calendar/{year}/{month}/{type}.json (CDN)same as rolling calendarmixedSame shape, different month window.
Every catalog endpointyearYEAR_ONLY (integer)Release year as an int — convenient for filtering.
ISO_WITH_OFFSET is authoritative — don’t strip it. The broadcast date field on episode/airing/calendar endpoints encodes the originating network’s timezone deliberately. Stripping the offset and assuming UTC will silently shift airing schedules by hours (US shows by 5, Japanese anime by 9). Always parse the timezone too.
Missing dates show up as null (Type 4 — “data not on file”), not as the literal string "null" or epoch. See Null and missing values for the full nullability model. The 1970-01-01T00:00:01Z placeholder is a different concept — it explicitly means “the user watched this but doesn’t remember when”, not “we don’t know” — see the dedicated section below.

User timezone preference

Each Simkl member has a configurable timezone on their profile, set at simkl.com/settings/ (with the date and time format alongside it). Third-party apps can read the user’s timezone via POST /users/settings — it’s returned as the account.timezone field, an IANA timezone name (e.g. "America/New_York", "Europe/Madrid", "Asia/Tokyo"):
{
  "user": {  },
  "account": {
    "id": 12345,
    "timezone": "Europe/Madrid",
    "type": "vip"
  }
}
Use the profile timezone for absolute timestamps when the user is signed in. That’s the single source of truth that keeps Simkl-rendered dates consistent across the user’s phone, tablet, TV, and the simkl.com web UI.
ScenarioWhat to use
Authenticated user, absolute date ("Watched on Apr 15 at 8 PM")account.timezone from POST /users/settings.
Authenticated user, relative date ("Watched 2 hours ago")Device timezone is fine — relative deltas don’t depend on absolute zone.
Unauthenticated / pre-loginDevice timezone as a fallback.
// Cache account.timezone after one call to /users/settings.
const PROFILE_TZ = 'America/New_York';

new Date('2026-05-13T16:08:00Z').toLocaleString('en-US', {
  timeZone: PROFILE_TZ,
  dateStyle: 'medium',
  timeStyle: 'short',
});
// → "May 13, 2026, 12:08 PM"
The user can change the setting at any time on simkl.com, but it’s overwhelmingly set-and-forget in practice — don’t re-fetch POST /users/settings on a timer or on every app launch / wake. Instead, gate the re-fetch on /sync/activities, which returns a settings.all timestamp alongside the per-list timestamps. The pattern matches every other Sync surface:
// On app launch / when you'd otherwise hit /users/settings:
const a = await getActivities();
if (a.settings.all !== local.lastSettingsSync) {
  const settings = await fetchUsersSettings();
  local.timezone        = settings.account.timezone;
  local.lastSettingsSync = a.settings.all;
}
That way most launches do zero extra calls (the activities check is the cheap “is anything new?” gate you’re running for the rest of the sync loop anyway), and you only re-fetch the heavier settings payload on the rare occasion the user actually changed something.
The simkl.com settings UI also lets the user pick a date format (e.g. MM/DD/YYYY) and a time format (12-hour AM/PM vs 24-hour). Those preferences are not currently exposed by POST /users/settings — only account.timezone and account.type come back. Until they’re added to the API, render dates and times using the device’s locale conventions (e.g. 'en-US' for AM/PM, 'en-GB' for 24-hour) inside the user’s profile timezone.

”Very long time ago” placeholder

1970-01-01T00:00:01Z
Simkl uses this near-epoch timestamp as a deliberate placeholder meaning “the user watched this but doesn’t remember when” — typically because they watched the title years ago and don’t recall the precise date. It is not a NULL, a bug, or missing data. It can appear on any field that carries a watch timestamp: watched_at, last_watched_at, per-episode watched_at inside seasons[].episodes[], and similar. On simkl.com, the “When did you watch this?” date picker offers it as a smart-suggestion card labelled “Very long time ago” with the helper line “I don’t remember”. That’s the canonical user-facing label — clients should mirror it (translated to the user’s locale).

Writing the placeholder

When a user picks the “Very long time ago / I don’t remember” option in your app’s date picker, POST 1970-01-01T00:00:01Z on the watched_at field. This works on every write endpoint that accepts watch timestamps — POST /sync/history, POST /sync/watched, and so on:
{
  "movies": [
    { "ids": { "simkl": 53536 }, "watched_at": "1970-01-01T00:00:01Z" }
  ]
}
Simkl stores this as a “watched, date unknown” entry and treats it consistently across the API and the simkl.com UI.

Reading the placeholder

Any watched_at / last_watched_at value at or near 1970-01-01T00:00:01Z is the placeholder. Detection rule: any timestamp before 2000-01-01 is effectively the “date unknown” placeholder. That guard handles both the canonical epoch+1 second value and any near-epoch variations from legacy data. Render it in your UI as “Very long time ago” (matching simkl.com’s label) or a localized equivalent — never as the literal January 1, 1970, which looks like a parsing error or a corrupt date to end users.
Node
function formatWatchedAt(iso) {
  const d = new Date(iso);
  if (d.getFullYear() < 2000) return 'Very long time ago';
  return d.toLocaleDateString();
}
Python
from datetime import datetime, timezone

def format_watched_at(iso: str) -> str:
    dt = datetime.fromisoformat(iso.replace('Z', '+00:00'))
    if dt.year < 2000:
        return 'Very long time ago'
    return dt.astimezone().strftime('%Y-%m-%d')

Date picker UX — mirror simkl.com

The simkl.com “When did you watch this?” date picker is the reference UX for date selection across the Simkl ecosystem. Apps that surface their own date pickers should mirror its three-tier structure: Common times (quick-pick buttons):
  • Just now — current timestamp
  • Today — today at the current time
  • Yesterday — yesterday at the current time
  • Last week — 7 days ago at the current time
Smart suggestions (context-aware cards):
  • Episode Air Date — pre-fills the episode’s original broadcast date (label: “Original broadcast”). Useful when a user watched live or wants to record a viewing at the air-date for stats / archival purposes.
  • Very long time ago — the 1970-01-01T00:00:01Z placeholder. Helper line: “I don’t remember”. The option that maps to this documented sentinel.
Recently used (history): the last two or three timestamps the user picked across recent date selections, surfaced so they don’t have to re-build a complex date if they’re tagging multiple items at the same time. Plus the full calendar grid for explicit picks. Third-party apps that build a “When did you watch this?” experience without the Very long time ago option force users into picking a fake date — that’s worse data for everyone (the user’s history, Simkl’s stats, your app’s later analytics) than just recording an honest “I don’t remember”.

Why this exists

Many users adopt Simkl long after they’ve watched hundreds of titles over the years. They want to record those watches honestly without inventing fake dates. The placeholder lets a client offer the “I don’t remember” option that maps to a stable, documented server value. simkl.com renders these entries with the “Very long time ago” label in history lists, sorts them after dated entries, and never displays the literal 1970-01-01 to end users — clients should do the same.