# FlexGet: a plugin-driven pipeline that turns any feed into a download folder

> FlexGet is a Python automation tool that reads RSS feeds, HTML pages, CSV files, and search results, filters them through plugin pipelines, and hands what survives to a watch directory. It installs with pip, ships a Docker image and a web interface, and requires Python 3.11 or newer.

**Flexget/Flexget** — The official FlexGet repository

- Repository: https://github.com/Flexget/Flexget
- Website: http://www.flexget.com
- Stars: 1,986 · Forks: 486
- Language: Python
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/flexget-flexget

## One YAML file describes the pipeline, not the site

FlexGet is a multipurpose automation tool for content like torrents, nzbs, podcasts, comics, series, and movies. Its distinguishing capability is source flexibility: it can use different kinds of sources such as RSS feeds, HTML pages, CSV files, and search engines, and there are even plugins for sites that do not provide any kind of useful feed.

That last clause is the point of the design. A conventional download automation script binds you to one site's HTML structure, and when the site changes, the script breaks. FlexGet splits that into two halves: plugins know how to read a source, and your configuration says which source and which rules.

The configuration is YAML, and the example in the README is short enough to be the whole mental model:

```yaml
tasks:
  tv:
    rss: http://example.com/torrents.xml
    series:
    - some series
    - another series
    download: /tvshows
```

Read it as a pipeline. The `rss` key names the source, the `series` key lists the things you want, and `download` names the destination directory. The task name `tv` is arbitrary and is how you refer to the task later. Everything else FlexGet does is a plugin sitting in that pipeline.

The project describes the payoff as usefulness in conjunction with applications which have watch directory support or provide an interface for external utilities. That is the division of labour that makes the tool work: FlexGet decides what to fetch, and something else on the other end of the watch directory does the transferring, which keeps the downloading logic out of the tool and lets you point it at whatever client you already use.

## The plugin ecosystem is the product

The README says there are numerous plugins that allow utilizing FlexGet in interesting ways and that more are being added continuously. That is the shortest possible description of the architecture, and it implies a plugin model rather than a fixed feature set.

A plugin in this design occupies a position in the task pipeline. An input plugin like `rss` or an HTML parser produces items, a filter plugin like `series` decides which items survive, and an output plugin like `download` decides what happens to them. The YAML keys in the example are the plugin names, which is why the configuration reads as a description of a pipeline rather than as a settings file.

The consequence is that FlexGet's capabilities are not bounded by what the core ships. A source FlexGet has never heard of can be handled by a plugin somebody else wrote, and the core does not need to change. The trade is that the tool's behaviour depends on plugins you may not have read, and a plugin can quietly change what your task does.

The project points to a cookbook for the range of what is possible with FlexGet, hosted alongside the main site, and the documentation set on readthedocs covers the API reference, the contributor guide, and the installation guide. The chat and help channels are also separate from the issue tracker, which is the usual arrangement for a project where a large share of questions are configuration questions rather than bugs.

## pip, Docker, or a script that skips the virtualenv

Installation has three documented shapes, and the differences matter.

The pip route is the standard one:

```bash
pip install flexget
```

A more detailed installation guide lives on the project site, and the Docker image is published on Docker Hub as flexget/flexget.

The third option exists for people who do not want to use virtualenv. There is a `flexget_vanilla.py` file that can be used to run FlexGet without virtualenv, with the note that you will need to install all required dependencies yourself. That file is present at the repository root, and it is worth knowing about because a system-wide install is exactly the situation where a dependency conflict shows up as something other than a clean error.

The Docker image is the most involved of the three, and its Dockerfile explains why. It is a two-stage build on `python:3.11-alpine`, pinned by digest. The builder stage installs ca-certificates, nodejs, build-base, libffi-dev, openssl-dev, and unzip, then copies `uv` from a pinned ghcr.io image, and runs a `bundle_webui.py` script before syncing the project with `uv sync --frozen --no-dev --group=all`. The final stage is a fresh alpine image with ca-certificates, nodejs, and tzdata, and it copies the built environment across with `--chown=app:app`.

Two details in there matter for operating it. The `--frozen` flag means the lockfile is respected exactly, so the image is reproducible against `uv.lock`. And the `VOLUME /config` with `WORKDIR /config` and `ENTRYPOINT ["flexget"]` means the container expects your configuration and state to live in a mounted volume at /config, and the entrypoint is the flexget command itself, so `docker run` arguments become FlexGet arguments.

The presence of nodejs in both stages is a tell: the web interface is a JavaScript bundle, and the build step that produces it is `scripts/bundle_webui.py`.

## The dependency list explains what FlexGet actually does

The project dependencies in pyproject.toml are the clearest available description of the tool's internals, and they divide into groups that map onto its features.

The scheduling and parsing side is apscheduler, feedparser, and beautifulsoup4 with html5lib. That is the input layer: RSS parsing, HTML parsing, and the scheduler that decides when a task runs.

The content recognition side is guessit at 4.2.1, and there is a comment in the dependency list explaining why the version is pinned there. The comment says guessit v4.3.0 causes the test `test_ambiguous` to fail, with a link to the upstream issue. A dependency pin with a named failing test and an upstream issue attached is a good sign about the maintainers, and it also tells you that release names are extracted from file names, which is why guessit is in the dependency set at all.

The web interface side is a stack of Flask packages: flask, flask-compress, flask-cors, flask-login, flask-restx, plus jinja2 for templates and jsonschema for validating what arrives. flask-restx is an API layer built on Flask, which is how the REST API reference in the documentation is served.

The transport and platform details are the interesting engineering choices. curl-cffi is used on Linux, Darwin, and Windows, and it is a curl binding that impersonates browser TLS fingerprints, which is the practical way to fetch pages that block a plain Python client. colorama is pulled in only on Windows, for the log output. pendulum handles the date and time handling. cryptography at 50.0 is there for certificate and crypto work, and loguru for logging. The `Typing:: Typed` classifier means the package ships type information, and the supported interpreters are 3.11 through 3.15.

The requirements.txt in the repository is generated, not hand-maintained, and the header says so: it was autogenerated by uv via `uv export --frozen --no-dev --no-emit-project --no-hashes --output-file=requirements.txt`. Every entry carries a `# via` comment naming its parent, which makes the dependency tree auditable.

## A long release history and a busy default branch

The release cadence says this is a mature, actively maintained project rather than a one-off script.

The three most recent tags are v3.22.1 on 2026-10-08, v3.22.2 on 2026-10-09, and v3.22.3 on 2026-10-12. Three patch releases in five days is a fast turnaround, which usually means bug fixes going out as they land rather than in a batched monthly release. The last push to the repository was on 2026-10-19, and the default branch is `develop`, so the branch that receives the work is the one that is not the released tag.

That combination, a `develop` default branch and frequent patch tags, is a specific workflow. Contributors work against `develop`, and releases are cut from it. For a user, it means pip installs get a tagged release and the repository checkout tracks the moving target, so a `develop` checkout and a pip install can behave differently.

The project advertises a Development Status of 5, Production/Stable, and classifies itself for end users on desktop as well as MacOS, Windows, POSIX, and Unix. The licence is MIT, with the licence file referenced in the project metadata as `license-files = [ "LICENSE" ]`.

There is also a `taskfile.yml` at the root, which is a Task runner file for developer commands, alongside `scripts/`, `tests/`, and `docs/`. The `uv.lock` file is committed, which is the lockfile the Docker build and the development workflow both rely on. A `.pre-commit-config.yaml` is present, and there is a `.github/` directory with the test workflow that the CI badge points at.

## Where FlexGet is the wrong tool

The honest limits are worth stating, because the tool is often recommended more broadly than it deserves.

First, the value is in the configuration file, and that is a commitment. A YAML pipeline with plugins per source is a program you have written, in a domain-specific format, and it will need maintenance as sites change and as your filtering rules grow. If your need is a single feed and a single directory, the configuration system is overhead and a plain script or a download client with a built-in filter is less work.

Second, the web interface and the REST API exist, but this is a self-hosted tool with a local state database and a local config. The API reference in the documentation describes a local service. If your requirement is a hosted multi-tenant service, this is the wrong shape.

Third, the plugin surface is where the surprises come from. The base install does not include every input source, so a site you expect to work may need an additional plugin you have not installed, and the failure mode is an empty task rather than an error. A plugin that changes the shape of the items also changes what your filters see.

Fourth, the content recognition is automated, and automated recognition is occasionally wrong. guessit extracts a release name from a filename, and the pinned version with the failing `test_ambiguous` test is a reminder that ambiguous names are a known hard case. If a misnamed release slips through, you find out when a file lands in your download directory with the wrong name.

Fifth, there is a security consideration that follows from the sources being untrusted. FlexGet fetches from arbitrary pages and feeds on the internet, parses them, and runs plugins written by third parties against that content. That is a combination where a parser bug in a dependency or a plugin is reachable from any site you configure, which is why the project ships a security advisory process and why the dependency set includes a TLS-impersonating HTTP client.

## Conclusion

FlexGet fits a self-hoster who wants one place to describe, in YAML, which feeds to watch and which rules to apply, and who wants that description to keep running on a schedule without a hand-written scraper per site. It is the wrong tool when you only need to download one podcast, and it is a poor fit for a hosted service requirement, because the project is built around a local configuration file and a local state database. Check three things before you install: that your Python is 3.11 or newer, that you read the plugin documentation for the sources you care about rather than assuming every site is supported, and that you understand the difference between the core pip install and the Docker image, because the image bundles a web UI and expects a volume mounted at /config.

## FAQ

### How do I install FlexGet?

Install it with `pip install flexget`, and see the installation guide on the project site for the detailed steps. A Docker image is published on Docker Hub as flexget/flexget, and for people who do not want to use virtualenv there is a `flexget_vanilla.py` script in the repository that runs FlexGet without one, with the note that you must install all required dependencies yourself.

### What does a basic FlexGet configuration look like?

FlexGet uses a YAML based configuration file with a `tasks` section. A typical task names an input such as `rss`, a filter such as `series` with a list of names, and an output such as `download` with a destination directory, for example `rss: http://example.com/torrents.xml` with `download: /tvshows`.

### Which Python version does FlexGet need?

Python 3.11 or newer, with classifiers for 3.11 through 3.15, and it is Python 3 only. The package is MIT licensed and is marked with a Development Status of 5, Production/Stable.

### What can FlexGet read as a source?

Different kinds of sources including RSS feeds, html pages, csv files, and search engines, and there are plugins for sites that do not provide any kind of useful feed. The project says plugins are added continuously, and the cookbook on the project site shows the range of configurations.

## Sources

- [Flexget/Flexget on GitHub](https://github.com/Flexget/Flexget)
- [License: MIT](https://github.com/Flexget/Flexget/blob/develop/LICENSE)
- [Project website](http://www.flexget.com)
- [README](https://github.com/Flexget/Flexget/blob/develop/README.md)
- [Releases](https://github.com/Flexget/Flexget/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/flexget-flexget
