Skip to content

plex

38 endpoints at a glance
MethodPathSummary
POST/plex/admin/libraries/{section_id}/syncAdmin Sync One
POST/plex/admin/libraries/refreshAdmin Refresh Libraries
GET/plex/admin/linked-usersAdmin Linked Users
DELETE/plex/admin/linked-users/{username}Admin Unlink User
GET/plex/admin/non-flac-albumsAdmin Non Flac Albums
POST/plex/admin/playlists/sync-allAdmin Sync All Playlists
GET/plex/admin/serverAdmin Server Info
GET/plex/admin/statsAdmin Stats
POST/plex/admin/sync-allAdmin Sync All
GET/plex/album/{canonical_album_id}/coverGet Album Cover
GET/plex/librariesList Libraries
GET/plex/libraries/{section_id}/albumsList Albums
GET/plex/libraries/{section_id}/artistsList Artists
GET/plex/libraries/{section_id}/episodesList Episodes
GET/plex/libraries/{section_id}/moviesList Movies
GET/plex/libraries/{section_id}/seasonsList Seasons
GET/plex/libraries/{section_id}/showsList Shows
GET/plex/libraries/{section_id}/tracksList Tracks
GET/plex/library/searchLibrary Search
DELETE/plex/meUnlink
GET/plex/meGet My Link
POST/plex/me/auth/cancelCancel Pin
POST/plex/me/auth/finishFinish Pin
POST/plex/me/auth/startStart Pin
POST/plex/me/favorites-playlist/syncSync My Favorites Playlist
GET/plex/me/home-usersList Home Users
POST/plex/me/home-users/{home_user_id}/provisionProvision Home User
GET/plex/me/playlistsList My Playlists
GET/plex/me/playlists/{playlist_id}Get My Playlist
GET/plex/me/playlists/{playlist_id}/coverGet My Playlist Cover
POST/plex/me/playlists/{playlist_id}/refreshRefresh My Playlist
POST/plex/me/playlists/syncSync My Playlists
GET/plex/play/album-tracks/{canonical_album_id}List Plex Playable Tracks
GET/plex/play/album-tracks/{canonical_album_id}/detailsList Plex Album Track Details
GET/plex/play/track/{canonical_id}Play Plex Track
POST/plex/play/track/{canonical_id}/timelinePost Timeline
GET/plex/play/track/{canonical_id}/transcode.mp3Transcode Plex Track
GET/plex/track/{canonical_id}/detailsGet Plex Track Details

plex


Get My Link

GET
/plex/me

Public link state for the caller.

link is null when not linked. has_pending_pin is true when
a PIN flow is mid-air — the SPA uses it to know whether to resume
polling on settings page refresh.

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Unlink

DELETE
/plex/me

Clear the caller's Plex link.

Library data (global) is untouched — disconnecting just drops the
identity mapping; if the same user re-links later they pick up the
existing mirror as-is.

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Start Pin

POST
/plex/me/auth/start

Kick off a fresh PIN-based link flow.

Returns {pin_id, code, auth_url, expires_at}. The SPA opens
auth_url in a new tab; the user signs in there; we poll via
POST /me/auth/finish.

Parameters

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Samples


Finish Pin

POST
/plex/me/auth/finish

Poll for completion of the caller's pending PIN.

One poll per call. SPA loops every ~2s until status != 'pending'.
Returns one of:
{status: 'pending'} — keep polling
{status: 'completed', link} — done; the user is linked
{status: 'expired', error} — PIN expired before completion
{status: 'no_pending_pin'} — nothing to poll (start one first)

Parameters

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Samples


Cancel Pin

POST
/plex/me/auth/cancel

Drop the pending PIN — call when the user closes the popup.

Parameters

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Samples


List Home Users

GET
/plex/me/home-users

List the caller's Plex Home users (excluding the caller).

412 when the caller hasn't linked Plex. 401 if the stored token
was revoked plex-side since last use; SPA should suggest a
re-link in that case.

Parameters

Responses

Successful Response

application/json
JSON
[
  
{
  
  
"additionalProperties": "string"
  
}
]

Playground

Samples


Provision Home User

POST
/plex/me/home-users/{home_user_id}/provision

Create an app account for a Plex Home user.

Status codes:

  • 201 — created. Body is the new user's public profile.
  • 400 — Plex returned a 4xx on switch (wrong PIN, no such Home
    user). Body's detail carries the plex.tv message.
  • 401 — owner's stored Plex token was rejected.
  • 409 — requested new_username already exists.
  • 412 — owner hasn't linked Plex yet.
  • 502 — Plex.tv transport / unexpected upstream error.

Parameters

Path Parameters

home_user_id*
Type
integer
Required

Request Body

application/json
JSON
{
  
"new_username": "string",
  
"new_password": "string",
  
"pin": "string"
}

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value
Body

Samples


List My Playlists

GET
/plex/me/playlists

List the caller's synced playlists.

Returns plain catalog rows (no items embedded in the listing — the
detail endpoint serves those). Optional playlist_type=audio
/video /photo filter for the SPA's tab strip.

Parameters

Query Parameters

playlist_type

Responses

Successful Response

application/json
JSON
[
  
{
  
  
"additionalProperties": "string"
  
}
]

Playground

Variables
Key
Value

Samples


Sync My Playlists

POST
/plex/me/playlists/sync

Fire-and-forget sync of the caller's Plex playlists.

Requires the caller to have linked Plex via the PIN flow first;
returns 412 Precondition Failed otherwise so the SPA can surface
a "Link your Plex account first" CTA.

Parameters

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Samples


Sync My Favorites Playlist

POST
/plex/me/favorites-playlist/sync

Reconcile the caller's favorite tracks against their per-user
Plex playlist ("<username> - Favorites").

Single-track add/remove already happens automatically inside the
favorite/unfavorite endpoints. This route is the manual catch-up
path: useful right after enabling the feature (to backfill
existing favorites), and as a "Sync now" button on Settings.

Side-effect of a successful first call: flips
preferences.plex_favorites_sync_enabled on if it isn't
already, since the user actively asked for it. The toggle in
Settings just calls this endpoint.

force_recreate=true deletes any existing playlist and rebuilds
from the current favorites list. Use when the playlist is
corrupted manually on Plex side.

412 if the user hasn't linked Plex.

Parameters

Query Parameters

force_recreate
Type
boolean
Default
false

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


Get My Playlist Cover

GET
/plex/me/playlists/{playlist_id}/cover

Stream the playlist's composite cover image from Plex.

The composite is Plex-side artwork (4 album covers stitched
together for audio playlists, item-art collage for video). Its URL
is on the playlist row but it needs an X-Plex-Token to fetch
— we can't put that in the browser (CORS + token leak), so we
proxy the same way the audio streaming endpoint does.

Returns 404 when the playlist doesn't have a composite_url
(empty playlists, some smart-playlist variants). The SPA falls
back to a typed emoji placeholder.

Ownership: caller must own the playlist (same scoping as the
detail endpoint). Cover image goes through unauthenticated by
the browser's image fetch, so this gate matters.

Route ordering matters: declared above
/me/playlists/{playlist_id:path} because that catch-all
would otherwise match playlist_id='<id>/cover'.

Parameters

Path Parameters

playlist_id*
Type
string
Required

Responses

Successful Response

application/json
JSON
[
]

Playground

Variables
Key
Value

Samples


Get My Playlist

GET
/plex/me/playlists/{playlist_id}

Single playlist with embedded items + per-item canonical resolution.

For each audio item we look up
track_external_refs._id == 'plex::<server_id>::<rating_key>'
in one $in query and attach canonical_track_id to the
matching items. The SPA uses that to render source pills and wire
inline playback to /plex/play/track/<canonical_id>.

playlist_id is the compound key <server_id>::<rating_key>,
which contains a : — declared as :path so FastAPI doesn't
truncate.

Parameters

Path Parameters

playlist_id*
Type
string
Required

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


Refresh My Playlist

POST
/plex/me/playlists/{playlist_id}/refresh

Re-fetch a single playlist's items from Plex.

Smart playlists are Plex-side filters; their contents can change
without the playlist row's updatedAt advancing. The detail
view auto-calls this on mount so the user sees current state.
Non-smart playlists also work here (just a refetch + overwrite).

Parameters

Path Parameters

playlist_id*
Type
string
Required

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


List Libraries

GET
/plex/libraries

All libraries from the global Plex server (cached). Any
authenticated user can read.

Parameters

Responses

Successful Response

application/json
JSON
[
  
{
  
  
"additionalProperties": "string"
  
}
]

Playground

Samples


List Artists

GET
/plex/libraries/{section_id}/artists

Parameters

Path Parameters

section_id*
Type
string
Required

Query Parameters

offset
Type
integer
Default
0
limit
Type
integer
Default
100

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


List Albums

GET
/plex/libraries/{section_id}/albums

Parameters

Path Parameters

section_id*
Type
string
Required

Query Parameters

offset
Type
integer
Default
0
limit
Type
integer
Default
100

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


List Tracks

GET
/plex/libraries/{section_id}/tracks

Parameters

Path Parameters

section_id*
Type
string
Required

Query Parameters

offset
Type
integer
Default
0
limit
Type
integer
Default
100

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


Library Search

GET
/plex/library/search

Substring search across the user's synced Plex music library.

Powers the "Add Plex link" modal on canonical detail views.
Active items only (soft-deleted Plex docs are excluded — the
canonical layer wouldn't merge from them anyway).

offset skips that many matches for the picker's "Load more".

Parameters

Query Parameters

q*
Type
string
Required
kind
Type
string
Default
"album"
limit
Type
integer
Default
10
offset
Type
integer
Default
0

Responses

Successful Response

application/json
JSON
[
  
{
  
  
"additionalProperties": "string"
  
}
]

Playground

Variables
Key
Value

Samples


List Movies

GET
/plex/libraries/{section_id}/movies

Parameters

Path Parameters

section_id*
Type
string
Required

Query Parameters

offset
Type
integer
Default
0
limit
Type
integer
Default
100

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


List Shows

GET
/plex/libraries/{section_id}/shows

Parameters

Path Parameters

section_id*
Type
string
Required

Query Parameters

offset
Type
integer
Default
0
limit
Type
integer
Default
100

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


List Seasons

GET
/plex/libraries/{section_id}/seasons

Parameters

Path Parameters

section_id*
Type
string
Required

Query Parameters

offset
Type
integer
Default
0
limit
Type
integer
Default
100

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


List Episodes

GET
/plex/libraries/{section_id}/episodes

Parameters

Path Parameters

section_id*
Type
string
Required

Query Parameters

offset
Type
integer
Default
0
limit
Type
integer
Default
100

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


Get Plex Track Details

GET
/plex/track/{canonical_id}/details

Project the Plex-side metadata for a canonical track for display.

Used by TrackDetailView to render the "🎵 Plex (full) · FLAC 16/44.1
· 23.4 MB" one-liner + the expandable "Plex details" block (plays,
last played, added, star rating).

Returns 404 if the canonical doesn't have a (live) Plex link. The SPA
suppresses the details block in that case. Resolves the freshest live
plex_tracks row (see :func:_resolve_live_plex_track_doc) so a
track whose file was replaced shows the new file's details rather
than a stale, soft-deleted row.

Parameters

Path Parameters

canonical_id*
Type
string
Required

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


List Plex Album Track Details

GET
/plex/play/album-tracks/{canonical_album_id}/details

Return {canonical_track_id: PlexTrackDetails} for every track
on this album that's Plex-linked.

Batch version of /plex/track/{canonical_id}/details — avoids
the N+1 problem when the SPA needs the quality one-liner for
every row of an album tracklist. Three queries total regardless
of tracklist size:

  1. media_tracks with album_id + sources.plex — gets
    the canonical-track set.
  2. track_external_refs with $in on those canonical_ids
    • source=plex — gets the compound Plex external_ids.
  3. plex_tracks with $in on those external_ids — fetches
    the raw payloads.

Parameters

Path Parameters

canonical_album_id*
Type
string
Required

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": {
  
  
"additionalProperties": "string"
  
}
}

Playground

Variables
Key
Value

Samples


List Plex Playable Tracks

GET
/plex/play/album-tracks/{canonical_album_id}

Return Plex-playable canonical tracks on this album, keyed three ways.

The album-detail tracklist iterates Deezer track rows (that's where
titles + 30s previews come from), but Plex play needs the canonical
ULID. Three lookup paths, tried in order by the SPA, so a row can
still resolve when the easier matchers don't apply:

  • by_deezer_id{deezer_track_id: canonical_track_id}.
    The fast path: stamped via the denormalised deezer_id field
    on the canonical track. Only fires for canonicals that already
    have a Deezer ref.
  • by_title_folded{title_folded: canonical_track_id}.
    Fallback for canonicals minted from a non-Deezer source (Plex /
    MB / LastFM) where deezer_id is empty. The SPA folds the
    Deezer row's title with the same rules backend uses to compute
    title_folded and looks it up here.
  • by_title_match{match_key: canonical_track_id}. Like
    by_title_folded but with trailing source annotations removed
    ("(Live)", "- 2011 Remaster", "(feat. …)"), which is what a
    Deezer tracklist carries and a Plex file tag usually does not.
    Keys claimed by two canonicals are dropped rather than guessed —
    see :func:api.canonical.match_key.build_match_map.
  • by_position{"<disc>::<track>": canonical_track_id}.
    Last-ditch fallback for manually-linked Plex tracks whose
    merged title diverges from Deezer's (Plex pulls titles from
    the audio file's tag, which often differs in capitalisation,
    bracketed annotations, etc.). On an album, disc + track number
    uniquely identify a song — so if the canonical and the Deezer
    row agree on those, that's a confident match. Disc defaults
    to 1 when missing (single-disc albums never set the field).

Single indexed query for all three maps; the SPA picks whichever
key resolves per row.

Parameters

Path Parameters

canonical_album_id*
Type
string
Required

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": {
  
  
"additionalProperties": "string"
  
}
}

Playground

Variables
Key
Value

Samples


Get Album Cover

GET
/plex/album/{canonical_album_id}/cover

Stream a Plex-album's thumb so the SPA can render it directly.

Plex stores album thumbs at /library/metadata/<ratingKey>/thumb
behind a token-authenticated GET. The path is relative to the Plex
server's host:port and never directly usable from a browser. The
album merger therefore omits Plex thumbs from the canonical's
cover_url field — but for libraries where Plex is the only
source, that leaves the SPA cover-less.

This proxy fills the gap: SPA hits /plex/album/<canonical_id> /cover whenever track.album.cover_url is null and
track.album.sources.plex is true. We resolve the canonical
album → Plex external_ref → ratingKey, then stream the bytes
back. Admin token is fine (same auth model as the audio + playlist
proxies); per-user token isn't needed for thumb reads.

404 when the album isn't Plex-linked; 503 if the server isn't
configured. Failures are explicit so the SPA can fall back to a
placeholder cleanly.

Parameters

Path Parameters

canonical_album_id*
Type
string
Required

Responses

Successful Response

application/json
JSON
[
]

Playground

Variables
Key
Value

Samples


Play Plex Track

GET
/plex/play/track/{canonical_id}

Direct-play proxy for a canonical track linked to Plex.

Returns the bytes Plex sends back from /library/parts/<partId>.
The original Range header is forwarded so the browser can seek.

Parameters

Path Parameters

canonical_id*
Type
string
Required

Responses

Successful Response

application/json
JSON
[
]

Playground

Variables
Key
Value

Samples


Post Timeline

POST
/plex/play/track/{canonical_id}/timeline

Report playback state for a Plex track on behalf of the user.

This is how Plex's LastFM scrobbler actually gets triggered.
The bytes-only streaming proxy doesn't generate any play state —
so Plex's server-side LastFM agent (and play history, last
played at, play count) never updates for SPA listens. The
/:/scrobble toggle bumps playcount but doesn't reliably
trigger the LastFM agent; the timeline events real Plex clients
send do.

Sequence the SPA fires per track:

  • On playing (first audio frame): state=playing, time_ms=0
  • Every ~10 s while playing: state=playing, time_ms=current
  • On pause / navigation away: state=paused, time_ms=current
  • On ended or user stop: state=stopped, time_ms=current

Plex's LastFM agent consumes those state transitions exactly the
way the actual LastFM scrobbler does — fires when the track was
played >50% or for 4+ minutes, whichever first.

Per-user token required: scrobbles are tied to the Plex account
that owns the token, so the server token wouldn't reach the
caller's LastFM. 412 if the user hasn't completed the Plex
OAuth pin flow.

Fire-and-forget at the HTTP boundary — Plex errors are logged
but never bubble to the SPA so playback isn't disrupted.

Parameters

Path Parameters

canonical_id*
Type
string
Required

Request Body

application/json
JSON
{
  
"state": "string",
  
"time_ms": 0,
  
"duration_ms": 0
}

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value
Body

Samples


Transcode Plex Track

GET
/plex/play/track/{canonical_id}/transcode.mp3

Transcode-to-MP3 fallback for direct-play codec compat.

Use when the source is FLAC / OGG / something the browser can't
play natively. Plex spins up a transcode session and streams an
MP3. Seek support depends on Plex's transcoder; in practice it's
less reliable than direct play, so the SPA only falls here on a
direct-play error event.

Parameters

Path Parameters

canonical_id*
Type
string
Required

Responses

Successful Response

application/json
JSON
[
]

Playground

Variables
Key
Value

Samples


Admin Server Info

GET
/plex/admin/server

Live /identity ping against the global server.

Used by the admin panel's Server card. Returns the unwrapped
MediaContainer (machineIdentifier, friendlyName,
version, platform, etc.) on success.

Parameters

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Samples


Admin Stats

GET
/plex/admin/stats

Aggregate Plex usage stats for the admin panel.

entities is the active count (excludes soft-deleted items);
entities_deleted separately surfaces the tombstoned items so
admins can see "your TV library shrank by N episodes since last
sync" at a glance. The historical records remain queryable via the
per-entity endpoints with include_deleted=true.

Parameters

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Samples


Admin Non Flac Albums

GET
/plex/admin/non-flac-albums

Plex albums that aren't fully FLAC — replace candidates.

Any album with ≥1 non-FLAC track, worst-affected first. Each row
links to the canonical album page when one exists. Read-only over the
local Plex mirror (no Plex API calls).

Parameters

Query Parameters

limit
Type
integer
Default
1000

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


Admin Linked Users

GET
/plex/admin/linked-users

One row per app user who has linked a Plex.tv identity.

Parameters

Responses

Successful Response

application/json
JSON
[
  
{
  
  
"additionalProperties": "string"
  
}
]

Playground

Samples


Admin Unlink User

DELETE
/plex/admin/linked-users/{username}

Force-unlink a user (admin override).

Path Parameters

username*
Type
string
Required

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}
Variables
Key
Value

Admin Refresh Libraries

POST
/plex/admin/libraries/refresh

Re-discover libraries on the global server.

Parameters

Responses

Successful Response

application/json
JSON
[
  
{
  
  
"additionalProperties": "string"
  
}
]

Playground

Samples


Admin Sync One

POST
/plex/admin/libraries/{section_id}/sync

Fire-and-forget per-library sync.

Music libraries with thousands of tracks routinely exceed the
SPA's 15s HTTP timeout, so the work runs on the event loop and
the SPA polls /admin/task-history/{task_id} for progress.

quick=true (music only): sync artists + albums fully but fetch
tracks only for new/changed albums — seconds on a stable library.

Parameters

Path Parameters

section_id*
Type
string
Required

Query Parameters

quick
Type
boolean
Default
false

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


Admin Sync All

POST
/plex/admin/sync-all

Fire-and-forget version of the cron job.

Multi-library sync across a non-trivial Plex server takes
minutes. The SPA polls /admin/task-history/{task_id}.
quick=true forwards to each library (music: new/changed-album
tracks only).

Parameters

Query Parameters

quick
Type
boolean
Default
false

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Variables
Key
Value

Samples


Admin Sync All Playlists

POST
/plex/admin/playlists/sync-all

Fire-and-forget sync of every linked user's Plex playlists.

Distinct from /admin/sync-all (which syncs the global library
via the admin token). Playlists are per-user, so this iterates
over every media-user with a stored plex_user_token and runs
sync_user_playlists for each. Per-user failures are isolated.

Parameters

Responses

Successful Response

application/json
JSON
{
  
"additionalProperties": "string"
}

Playground

Samples


Media — Blue Sapphire Software.