Mirror your watch history (and an optional live now playing status) into Simkl — from Trakt, Nuvio or Stremio, for every player, even the ones that can't talk to Simkl directly.
Trakt allows only a single connected community app on free accounts, and players like Nuvio and Fusion can't connect to Simkl at all. Relay reads your history from a source you already use and forwards new movies/episodes to your Simkl account through Simkl's official API. Nothing gets installed on the player.
Fusion ─▶ Trakt (public profile) ───┐
Nuvio ─▶ Nuvio Sync (your login) ───┼─▶ Relay worker ─▶ Simkl API
Stremio▶ Stremio account (login) ───┘ (/sync/history, /checkin)
Plex tip: Simkl has a native Plex integration — connect Plex to Simkl directly in Simkl's settings rather than through this relay.
| Source | How it reads | Auth stored | Live now-playing |
|---|---|---|---|
| Trakt | your public Trakt profile (no OAuth) | just the username | ✅ optional |
| Nuvio | Nuvio Sync API (sync_pull_watched_items) |
a Nuvio refresh token | — (history only) |
| Stremio | Stremio account library (datastore*) |
a Stremio auth key | ✅ optional |
The user picks the source in the wizard. For Nuvio/Stremio they sign in (email + password); the password is used only to obtain a token and is never stored.
Each connection carries a per-user Simkl OAuth access token obtained through
Simkl's PIN flow in the wizard (enter a short code at simkl.com/pin).
| What | Endpoint | Status |
|---|---|---|
| Watched | POST /sync/history |
✅ documented, reliable |
| Now playing | POST /checkin |
/checkin and is not yet verified against the current api.simkl.org spec |
Every request sends the required client_id, app-name, app-version params
and a descriptive User-Agent. POSTs are throttled to ≤ 1/second per
Simkl's rate limit (SEND_DELAY, default 1.1s).
Fully self-contained — docker compose up needs no external services:
| Service | Role |
|---|---|
db |
Postgres — schema created on first boot from db/init.sql |
web |
Next.js — landing page, pairing wizard, manage page |
worker |
Python — syncs each connection to Simkl every 5 min |
web and worker talk to Postgres directly. Per connection we store the
source, the Simkl access token, and (for Nuvio/Stremio) a source token. Each
connection gets a random manage link.
cp .env.example .env
# set SIMKL_CLIENT_ID (https://simkl.com/settings/developer/new/)
# set TRAKT_CLIENT_ID (https://trakt.tv/oauth/applications — only to read Trakt)
docker compose up -d --buildThen open http://<server-ip>:8088 and run the wizard.
curl -fsSL https://get.docker.com | sh
git clone https://github.com/synologyy/simkl-relay.git
cd simkl-relay
cp .env.example .env && nano .env # set SIMKL_CLIENT_ID + TRAKT_CLIENT_ID (+ POSTGRES_PASSWORD, APP_URL)
docker compose up -d --buildPoint your reverse proxy (Pangolin/newt, nginx, Caddy) at port 8088.
| Variable | Required | Notes |
|---|---|---|
SIMKL_CLIENT_ID |
✅ | Your Simkl API app — the write destination |
TRAKT_CLIENT_ID |
✅ | One Trakt API app, used only to read public profiles as a source |
POSTGRES_PASSWORD |
Password for the bundled Postgres | |
APP_URL |
Public URL for the manage link; blank = auto-detect from browser | |
WEB_PORT |
Host port for the UI (default 8088) |
|
SIMKL_API_URL |
Simkl API base (default https://api.simkl.com) |
|
SIMKL_APP_NAME / SIMKL_APP_VERSION |
Sent as required query params on every Simkl request |
Nuvio uses the public Nuvio API (https://api.nuvio.tv) with the documented
publishable key; override via NUVIO_PUBLISHABLE_KEY if needed.
- Live check-in is experimental. History (
/sync/history) is solid; the/checkinnow-playing call needs verifying against the current Simkl spec — seesimkl_checkin()inworker/worker.py. Failures never break history. - Public Trakt profile required for the Trakt source (no OAuth).
- Back catalog. Relay only forwards new activity going forward. Use Simkl's own import for your existing history.