Find and download subtitles from OpenSubtitles for your local video files.
subsnag is a small command-line tool that finds and downloads subtitles from
OpenSubtitles for your local video files. It
matches files by their OpenSubtitles movie hash when possible (the most
reliable match), and falls back to a title/year/episode search otherwise.
It is careful about the things that matter:
- Transient API failures (
429,5xx, network blips) retry with backoff and respectRetry-After. - Subtitles are written atomically — a failed or cancelled download never clobbers an existing file.
- One bad file does not abort the whole batch, and the run stops cleanly when the daily quota is exhausted.
subsnag talks to the official OpenSubtitles REST API v1 at
https://api.opensubtitles.com/api/v1. This is the modern REST API (not the
legacy XML-RPC service). Every request carries your consumer Api-Key and a
User-Agent; authenticated requests additionally send a Bearer token obtained
via subsnag login. Downloading a subtitle is a two-step flow: request a
short-lived download link (POST /download), then fetch the file from that
link.
go install github.com/panakour/subsnag@latestThis installs a subsnag binary into $GOBIN (or $GOPATH/bin). Alternatively,
download a prebuilt binary from
GitHub Releases.
Release builds target:
- macOS (
arm64) - Linux (
amd64) - Windows (
amd64)
Requires Go 1.26 or newer.
# build a local binary named ./subsnag
go build -o subsnag .
# or install into $GOBIN / $GOPATH/bin
go install .A Makefile is provided with convenience targets:
make build # go build -o subsnag .
make install # go install .
make test # go test ./...
make race # go test -race ./...
make vet # go vet ./...
make lint # golangci-lint run
make check # race + vet + lint + build (mirrors CI)
make fmt # gofmt -w .
make tidy # go mod tidy
make clean # rm -f subsnag-
Create a free account at https://www.opensubtitles.com.
-
Register a consumer application under your account's API / Consumers section to obtain an API key.
-
Save it for
subsnag:subsnag config set-key YOUR_API_KEY
Alternatively, set the
SUBSNAG_API_KEYenvironment variable or pass--api-keyon any command.
# 1. store your consumer API key
subsnag config set-key YOUR_API_KEY
# 2. log in with your OpenSubtitles account (token is saved for you)
subsnag login --username your_user
# 3. download subtitles for a file or a whole directory
subsnag download "The.Matrix.1999.1080p.BluRay.x264.mkv"
subsnag download -r ~/MoviesThe subtitle is written next to the video, e.g.
The.Matrix.1999.1080p.BluRay.x264.en.srt.
Global flags (available on every command):
| Flag | Description |
|---|---|
--config <path> |
Use an alternate config file location. |
--api-key <key> |
Override the API key for this invocation. |
--user-agent <ua> |
Override the User-Agent header. |
-v, --verbose |
Verbose output. |
Authenticate and store a session token.
subsnag login --username your_user # prompts for password securely
subsnag login --username your_user --password pw # non-interactive (less secure)
SUBSNAG_PASSWORD=pw subsnag login --username your_userThe username falls back to the saved config and then an interactive prompt.
The password may come from --password, the SUBSNAG_PASSWORD env var, or a
hidden terminal prompt. On success the token, username and account base URL are
saved, and your plan level / VIP status / allowed downloads are printed.
Clear the stored session token.
subsnag logoutPrint version, commit, build date, platform, and Go runtime.
subsnag version
subsnag --version # one-line formInspect and modify configuration.
subsnag config show # print config (the token is masked)
subsnag config path # print the config file path
subsnag config set-key KEY # store the API key
subsnag config set-agent UA # store the User-Agent
subsnag config set-langs en,es # store default languages (comma separated)Search OpenSubtitles and print a numbered table.
# free-text search
subsnag search the matrix
# filter by language, year and type
subsnag search -l en,es --year 1999 --type movie the matrix
# search by a local file's movie hash
subsnag search --hash "The.Matrix.1999.mkv"
# episode search
subsnag search -l en --season 1 --episode 2 breaking bad
# download the 2nd listed result into the current directory (or --output)
subsnag search -l en the matrix --download 2 --output ~/SubtitlesColumns: # | LANG | DOWNLOADS | HASH | RELEASE. The HASH column shows a
check mark (✓) when a result matched by movie hash.
Useful flags: -l/--lang, --imdb, --tmdb, --season, --episode,
--year, --type, --hash, --hearing-impaired, --page,
-d/--download N, -o/--output.
Download subtitles for files or directories.
# single file, default language(s)
subsnag download movie.mkv
# multiple languages
subsnag download -l en,es movie.mkv
# recurse into a directory, overwrite existing subtitles
subsnag download -r -f ~/Movies
# preview matches without downloading
subsnag download --dry-run -r ~/Movies
# pick a subtitle format and hearing-impaired preference
subsnag download --format srt --hearing-impaired exclude movie.mkvAfter the run a summary is printed:
3 downloaded, 1 skipped, 0 not found, 0 errors
The command exits non-zero only when all downloads failed; per-file errors
otherwise leave the exit code at 0. If authentication is required you will be
prompted to run subsnag login.
Useful flags: -l/--lang, -r/--recursive, -f/--force (overwrite),
--format (default srt), --dry-run, --hearing-impaired.
Use lowercase ISO-639-1 codes, optionally with a region suffix:
en— Englishes— Spanishpt-br— Brazilian Portuguesefr,de,it,nl, ...
Pass several at once as a comma-separated list (-l en,es,pt-br) and set your
defaults with subsnag config set-langs en,es.
OpenSubtitles enforces a daily download limit, and the free tier allows only
a limited number of downloads per day. Once the quota is exhausted, download
requests are rejected until it resets. subsnag login prints your
allowed_downloads, and the search download flow reports how many downloads
remain. Searching does not consume the download quota; only fetching a subtitle
file does.
Config is stored as JSON at:
$SUBSNAG_CONFIGif that environment variable is set, otherwise$XDG_CONFIG_HOME/subsnag/config.jsonifXDG_CONFIG_HOMEis set, otherwise~/.config/subsnag/config.jsonon every platform (Linux and macOS alike).
Run subsnag config path to print the exact location. The file contains your
session token and is written with 0600 permissions.
Environment variables (SUBSNAG_API_KEY, SUBSNAG_USER_AGENT) override the saved
config for a single invocation but are never written back to the file.
Requirements: Go 1.26+, golangci-lint, and goreleaser only for testing
release packaging locally.
go run . --help
go test -race ./...
go vet ./...
golangci-lint run
go build ./...make check runs the verification suite (race tests, vet, lint, build).
Repo-specific engineering notes for contributors and coding agents live in AGENTS.md.