Point it at a folder
ApexFeeder Docs
Installing ApexFeeder, connecting local folders and cloud accounts, how it identifies your media, pairing a player, and every configuration option.
What ApexFeeder is
ApexFeeder is a lightweight media file server for local and cloud libraries. Point it at a folder of movies or TV shows: it reads the filenames, looks each title up on The Movie Database, downloads posters and descriptions, and serves the result through a small web UI you can play from — and to ApexPlayer on your Apple devices.
It is deliberately much smaller than Plex, Jellyfin or Emby. No transcoding, no plugin system, no accounts. Find your media, describe it properly, stream it.
ApexFeeder is free, and free of cost. TMDB's API is free for non-commercial use, and keeping ApexFeeder free is what keeps that access available.
Installing and running
- Node.js
- Version 22.6 or newer.
- ffmpeg / ffprobe
- Optional. Used for durations, codec details and reading embedded subtitle tracks.
npm install
npm run build
npm start Then open http://127.0.0.1:8770 and add a folder. There is nothing to configure — released builds ship with ApexFeeder's own TMDB key.
Building from source? The project credential is never committed, so a source build asks for a TMDB key on first run, or reads APEXFEEDER_TMDB_KEY from the build environment.
Adding libraries
A library is a folder ApexFeeder watches. It can live on the machine or in a connected cloud account, and you can add as many as you like — everything merges into one catalogue.
- 1 Open the web UI and go to Libraries → Add.
- 2 Pick a local path with the folder picker, or choose a connected cloud account.
- 3 Say whether it holds movies or TV.
- 4 ApexFeeder scans it, identifies what it finds and fetches artwork. Anything uncertain lands in Review.
Connecting cloud storage
Dropbox, Google Drive and OneDrive are all optional, and each needs credentials compiled into the build you are running. Connect an account under Settings, then add a library pointing into it.
| Provider | Live updates | Playback | Reconnect needed |
|---|---|---|---|
| Dropbox | Seconds — a held-open connection | Direct from Dropbox, nothing through ApexFeeder | No |
| OneDrive | Polled once a minute, folder-scoped | Direct from Microsoft | No |
| Google Drive | Polled against the whole account | Proxied through ApexFeeder | Roughly weekly |
Google Drive access uses a scope Google classifies as restricted. A lasting refresh token requires a Google-verified app, which means a paid third-party security assessment — so ApexFeeder stays in testing mode, where refresh tokens expire after exactly seven days. Past that, Drive access quietly stops until you press Connect Google Drive again in Settings. One click, no rebuild, nothing needed from whoever runs the server.
Day to day, Dropbox and OneDrive are the better experience of the three: both push changes promptly and both hand out temporary links so playback streams straight from the provider.
How it identifies files
ApexFeeder parses the filename, then looks the result up on TMDB. It copes with the conventions people actually use — a plain title, a title with a year, a scene release name, Season folders, SxxExx numbering, multi-episode files and dated daily shows.
Movies/
Pulp Fiction (1994).mkv
The.Matrix.1999.1080p.BluRay.x264-GROUP.mkv
TV/
The Wire/
Season 01/
S01E03.mkv When ApexFeeder is confident, it matches silently. When it is not — two films share a title, or the year does not line up — the file goes to the Review tab with its best guess attached, so confirming is one click. Once confirmed, that decision is locked and no future scan will second-guess it, even if you rename the file.
- Every file is keyed on an identity that survives renames: an inode locally, a permanent file id in each cloud provider.
- Reorganising a library updates a path rather than losing the item, so matches, artwork and watch state survive.
- Filesystems that report no usable inode — exFAT, some network mounts — fall back to path-based identity and simply lose rename tracking.
- Nothing is ever written to your media folders.
Subtitles
ApexFeeder does the part only a server can do well and leaves the rest to the player. It never searches subtitle providers and never adjusts timing — that is ApexPlayer's job.
- Sidecar files next to a local video — Movie.en.srt, Movie.fr.forced.srt — are found during a scan.
- Embedded tracks are read with ffprobe and listed.
- A subtitle ApexPlayer finds elsewhere can be pushed back to ApexFeeder and cached, so every other device gets it without a second download.
- Cached subtitles go in ApexFeeder's data directory, never into your media folder or cloud drive.
Subtitles are tracked per file rather than per title. A film held in two qualities can have different intro padding or framerate, so a subtitle timed against one copy is not assumed safe for the other — unlike watch state and favourites, which are shared across every copy.
Pairing a player
ApexFeeder binds to loopback by default. To let ApexPlayer reach it from a phone or TV, bind to the network:
APEXFEEDER_HOST=0.0.0.0 npm start Nothing is exposed by doing that. Every endpoint except the health check and pairing requires a paired device, and the admin interface is served only to the machine ApexFeeder runs on.
- 1 Open ApexPlayer and pick this server. It shows a four-digit code.
- 2 In ApexFeeder, go to Settings — the request appears with the device's name and address.
- 3 Check the code matches, then Approve.
- 4 The device can now browse and stream. Revoke it any time from the same screen; access stops immediately.
The four-digit code is not a credential — it exists so you can tell one pending request from another. Two things do the real work: the access token is handed only to whoever presents the matching 256-bit pairing secret, held by the requesting device alone, and only the local machine can approve a request. Someone else on your network cannot approve their own, whether or not they know the code.
A paired device can read the catalogue and stream. It cannot browse your filesystem, add libraries, change settings or approve other devices.
Discovery and ports
- ApexFeeder announces itself over mDNS/Bonjour as _apexfeeder._tcp. The announcement is link-local — routers do not forward it — and carries only a server id, name and version.
- A second path answers a plain UDP broadcast of "who is ApexFeeder?" on port 27359, for clients that speak that convention instead. It deliberately does not answer Jellyfin's or Plex's query strings, so it never appears as a false match in their clients.
- Both are skipped when bound to loopback. APEXFEEDER_ANNOUNCE=false turns them off; =true forces them on.
- Port 8770 is tried first so a bookmark keeps working. If it is taken, ApexFeeder moves to the next free port and logs where it landed.
- The address it settled on is written to runtime.json in the data directory, which is how a launcher finds the UI without assuming a port.
- Only one instance may use a data directory at a time; a record left by a crash is detected and reclaimed.
Set APEXFEEDER_STRICT_PORT=true to fail instead of moving ports — wanted in Docker or behind a reverse proxy, where a port that silently moves breaks whatever points at it.
Configuration
Everything is optional; the defaults are meant to just work.
| Variable | Default | Purpose |
|---|---|---|
| APEXFEEDER_DATA_DIR | ~/.apexfeeder | Database and artwork cache |
| APEXFEEDER_HOST | 127.0.0.1 | Bind address |
| APEXFEEDER_PORT | 8770 | Preferred port |
| APEXFEEDER_PORT_ATTEMPTS | 20 | How many ports to try before giving up |
| APEXFEEDER_STRICT_PORT | false | Fail instead of moving to another port |
| APEXFEEDER_ANNOUNCE | auto | Advertise over mDNS; off on loopback |
| APEXFEEDER_SERVER_NAME | hostname | Name shown to discovering clients |
| APEXFEEDER_LOG_LEVEL | info | Log level |
| APEXFEEDER_TMDB_KEY | — | TMDB key, overriding the bundled one |
| APEXFEEDER_DROPBOX_APP_KEY | — | Without it, Dropbox is unavailable |
| APEXFEEDER_GOOGLE_CLIENT_ID | — | Google OAuth client id |
| APEXFEEDER_GOOGLE_CLIENT_SECRET | — | Only if your credentials issue one |
| APEXFEEDER_ONEDRIVE_CLIENT_ID | — | Microsoft Entra app id; no secret needed |
| APEXFEEDER_TMDB_API_BASE | TMDB | Point at a caching proxy instead |
The TMDB key
ApexFeeder ships with the project's key, so you do not need one. You can still supply your own under Settings → Metadata source, and it takes priority over the shared one. Worth doing if you have a very large library, or if matching starts failing — the project key is shared by every install, so it shares one rate limit. Either a v3 API key or a v4 read access token works; ApexFeeder detects which you pasted.
This product uses the TMDB API but is not endorsed or certified by TMDB.
FAQs
Nothing, and that is structural rather than promotional. TMDB's API is free for non-commercial use, and keeping ApexFeeder free of cost is what keeps that access available.
It is deliberately much smaller. No transcoding, no plugins, no accounts, no library curation step — you point it at folders and it serves them. It also treats cloud storage as a first-class source rather than something to sync down first. If you want live TV, DVR, transcoding for incompatible devices or a plugin ecosystem, run Plex or Jellyfin; ApexPlayer speaks to those too.
Not strictly. The parser handles the usual conventions — a movie named after the film, with or without a year, scene-style release names, Season folders, SxxExx episode numbers and dated daily shows. Tidier names simply match more often without visiting the Review tab.
No. Nothing is ever written to your media folders or cloud drives. Artwork, the catalogue database and any cached subtitles live in ApexFeeder's own data directory, ~/.apexfeeder by default.
The scope ApexFeeder uses is one Google classifies as restricted, and a lasting refresh token requires a Google-verified app — which means a paid third-party security assessment, commonly $800 to $4,500 or more. That is not something a free project takes on, so ApexFeeder stays in Google's testing mode, where refresh tokens expire after exactly seven days. Reconnecting is one click in Settings. Dropbox and OneDrive have no equivalent.
The code is not a credential — it exists so you can tell one pending request from another. The token is only ever handed to whoever presents the matching 256-bit pairing secret, which the requesting device alone holds, and only the machine ApexFeeder runs on can approve a request. Someone on your network cannot approve their own, code or no code.
Browse the catalogue and stream. It cannot browse your filesystem, add libraries, change settings or approve other devices — those are restricted to the machine itself. Revoke any device from Settings and its access stops immediately.
Binding to 0.0.0.0 is how a phone or TV reaches it, and every endpoint except the health check and pairing requires a paired device. The admin interface is served only to the machine ApexFeeder runs on. That said, this is software you operate — exposing it to the public internet is a different question, and not something ApexFeeder is designed for.
Almost certainly the format. ApexFeeder never transcodes, so the file has to be something the playing device can decode natively. Re-encoding that file solves it, as does serving it through a transcoding server instead.
No — released builds ship with the project's key. You can supply your own under Settings → Metadata source, and it takes priority. Worth doing for a very large library, since the shared key is used by every install and shares one rate limit.
Something missing?
Ask in Discord. If enough people hit the same thing, it ends up on this page.
This product uses the TMDB API but is not endorsed or certified by TMDB.