🇺🇸 English · 🇧🇷 Português
HOWL
Hoard Optimizer, What to Launch
Ranks your Steam library by quality × time invested using data from HowLongToBeat, Metacritic and Steam Reviews. No more decision paralysis staring at your backlog.
Why
I own more games than I will ever play. Every time I opened Steam I scrolled the same list, recognised nothing, and closed it — the library had become a decision problem, not a collection.
The information to solve it already exists, just scattered: Metacritic knows whether a game is good, Steam reviews know whether people actually enjoyed it, and HowLongToBeat knows what it will cost me in hours. howl puts the three together and sorts what is left.
It is a personal tool that turned out to be worth sharing. It reads your library, never writes to it.
Requirements
- Python 3.11+
- Steam API Key (free, instructions below)
- Steam account with public profile and library (or private, using your own key)
Installation
pip install howl-steam-backlog
Or with pipx (recommended for CLI tools — isolated environment):
pipx install howl-steam-backlog
For development:
git clone https://github.com/albertosca/howl.git
cd howl
uv sync --extra dev # resolves from uv.lock into .venv
uv run --extra dev pytest # run the suite
Dependencies are pinned in uv.lock, so uv sync reproduces the exact
versions the tests were last green against. pip install -e ".[dev]" in a
plain venv still works and is what CI does, but it resolves fresh and
ignores the lock.
After installing, the howl command becomes available:
howl --help
Usage
Run howl --setup once first — it walks you through the Steam API key and writes the config for you. Details in Configuration.
# First run: populates the cache (may take ~5 min for 300 games)
howl --username my_steam_id --verbose
# Top 10 good and short games (default)
howl --username my_steam_id
# Interactive visual interface
howl --username my_steam_id --tui
# Filter by genre, progress and formula
howl --username my_steam_id --genre "action,rpg" --not-started --sort quick-wins
# Filter by release era
howl --username my_steam_id --era "2010-2015,2015-2020"
# See what's available in the cache
howl --list-genres
howl --list-tags
howl --list-collections
With STEAM_USERNAME set in the environment, --username can be omitted.
Sort formulas (--sort)
| Name | Formula | When to use |
|---|---|---|
shortest |
composite / √h | Good and short games — default |
longest |
composite × √h | Epics worth every hour |
rated |
Metacritic only | Most critically acclaimed |
loved |
Steam % positive | Most loved by players |
quick-wins |
composite² / h | Maximum quality in less time |
hidden-gems |
steam × (1 − mc/100) | Loved by players, ignored by critics |
composto |
0.5×mc + 0.5×steam | Configurable weighted average |
composite = weighted average of Metacritic and Steam reviews (tune with --weight-mc / --weight-steam).
Games without Metacritic or Steam reviews get zero weight on that source (the other takes 100%).
Available filters
| Flag | Values | Default |
|---|---|---|
--sort |
see table above | shortest |
--genre |
comma-separated | — |
--genre-any |
comma-separated | — |
--exclude-genre |
comma-separated | — |
--era |
pre-2005 2005-2010 2010-2015 2015-2020 2020+ unknown |
all |
--not-started |
— | — |
--in-progress |
— | — |
--all-progress |
— | not-finished |
--category |
all singleplayer coop |
all |
--min-hours / --max-hours |
hours (HLTB) | — |
--collection |
Steam collection name | — |
--top |
integer | 10 |
--show-finished |
— | excluded |
TUI (visual interface)
howl --username my_steam_id --tui
| Key | Action |
|---|---|
f |
Open/close filter panel |
g |
Toggle genres column |
t |
Toggle Steam categories column |
s |
Save current result to CSV + Markdown |
q |
Quit |
The filter panel applies all changes in real time.
Configuration
Quick way: howl --setup
howl --setup
The interactive wizard walks you through the required variables, validates them live against the Steam API, and writes them to ~/.config/howl/.env (loaded automatically, created with 0600 permissions). If you still have a legacy .env in the current directory, it offers to migrate it.
Manual way: environment variables
| Variable | Required | What it is |
|---|---|---|
STEAM_API_KEY |
yes | Steam Web API key |
STEAM_USERNAME |
yes | Vanity URL of your profile (e.g. gabelogannewell) |
STEAM_VDF_PATH |
no | Path to sharedconfig.vdf (collection filters) |
HOWL_FINISHED_COLLECTION |
no | Name of your Steam collection for finished games — they are excluded unless --show-finished |
IGDB_CLIENT_ID |
no | Twitch app Client ID (scores for delisted games) |
IGDB_CLIENT_SECRET |
no | Twitch app Client Secret |
STEAM_API_KEY:
- Go to https://steamcommunity.com/dev/apikey
- Log in with your Steam account
- Fill "Domain Name" with any value (e.g.
localhost) - Copy the generated key
STEAM_USERNAME:
The vanity URL of your Steam profile: steamcommunity.com/id/gabelogannewell → username is gabelogannewell
IGDB_CLIENT_ID and IGDB_CLIENT_SECRET (optional): IGDB complements Metacritic for games where Steam returns no score (delisted, very old, etc.).
- Go to https://dev.twitch.tv/console and log in with your Twitch account (create one if needed — it's free)
- Click Register Your Application
- Fill in:
- Name: anything (e.g.
howl-igdb) - OAuth Redirect URLs:
http://localhost - Category:
Website Integration
- Name: anything (e.g.
- Click Create
- In the app list, click Manage
- Copy the Client ID
- Click New Secret and copy the Client Secret (shown only once)
With the credentials configured, run:
howl --migrate-igdb
The OAuth token is generated and refreshed automatically. You don't need to repeat this process.
STEAM_VDF_PATH (optional):
Required to use --collection and automatic exclusion of finished games.
| System | Default location |
|---|---|
| macOS | ~/Library/Application Support/Steam/userdata/<steamid>/7/remote/sharedconfig.vdf |
| Linux | ~/.steam/steam/userdata/<steamid>/7/remote/sharedconfig.vdf |
| Windows | C:\Program Files (x86)\Steam\userdata\<steamid>\7\remote\sharedconfig.vdf |
The <steamid> is numeric (different from the vanity URL). Find it in: Steam → Settings → Account → Steam account ID.
Credentials live in ~/.config/howl/.env (respects $XDG_CONFIG_HOME). The wizard writes this file for you; to set it up by hand, create it with one KEY=value per line:
# ~/.config/howl/.env
STEAM_API_KEY=your_key_here
STEAM_USERNAME=your_username
# STEAM_VDF_PATH is optional (collection filters)
STEAM_VDF_PATH=/path/to/sharedconfig.vdf
Prefer this over exporting the keys in your shell profile (~/.zshenv, ~/.bashrc): shell exports leak the values to every process on your system.
Language
howl speaks English and Brazilian Portuguese. howl --setup asks which one you want and stores the answer in ~/.config/howl/.env.
To override it for a single run:
howl --lang pt-BR
Resolution order, highest first: --lang → HOWL_LANG environment variable → HOWL_LANG in ~/.config/howl/.env → your operating system locale → English.
Cache
HLTB, Steam Reviews and game detail data are cached in ~/.cache/howl/games_cache.json (or $XDG_CACHE_HOME/howl/, or $HOWL_CACHE_DIR) to avoid repeated requests, so howl finds it no matter which directory you run it from. Manual overrides live in ~/.config/howl/howl_overrides.json. Files from older versions in the current directory (.cache/games_cache.json, howl_overrides.json) are copied there automatically on first run. To update:
# Re-fetch everything from scratch
howl --username my_steam_id --refresh
# Fill missing fields in old entries (genres, release_year)
howl --migrate-cache
# Fetch IGDB scores for games without Metacritic (requires IGDB_CLIENT_ID and IGDB_CLIENT_SECRET)
howl --migrate-igdb
--migrate-cache is useful if you had a cache from earlier versions that didn't have all fields.
--migrate-igdb fills critic scores via IGDB for games where Steam returns no Metacritic — especially useful for delisted games (e.g. the original Deus Ex: Human Revolution). It can be re-run at any time; games that already have IGDB data in the cache are skipped.
Troubleshooting
"Username not found on Steam"
Check that the vanity URL is correct. Visit https://steamcommunity.com/id/your_id/ — if it redirects to your profile, it's right.
Games without Metacritic or Steam reviews
Normal — not all games have data. They still show up but with score 0 in some formulas. Use --sort loved or --sort rated to see only games with data.
"0 entries to migrate"
The cache is already complete. If you just built the cache with howl --verbose, all games already come with every field.
Cache with few games If the Steam library is set to private, the API returns 0 games. Go to Steam → Profile → Edit → set games to public (you can switch back to private afterwards).
How it's built
Python 3.11+, Textual for the TUI, argparse for the CLI, no runtime dependency beyond the four API clients.
The quality gate runs in this order and all four must pass before anything is committed:
.venv/bin/ruff check --fix steam_hltb/ tests/
.venv/bin/ruff format steam_hltb/ tests/
.venv/bin/mypy steam_hltb # strict
.venv/bin/pytest # 402 tests, --cov-fail-under=100
Coverage is at 100% with the gate enforced, so the badge cannot drift from reality — if coverage drops, CI fails. mypy runs in strict mode. Dependencies are locked with uv.
One design note worth calling out, since it is the least obvious part of the codebase: the two translation catalogs hold 294 entries between them, and none of them is asserted individually. Three structural tests cover all of them — key parity between catalogs, placeholder parity within each pair, and a boundary test against the running Python's argparse internals. A missing or malformed translation fails the suite instead of reaching a terminal.
.venv/bin/pytest tests/test_score.py -v # a single module
Metadata
Release files for howl-steam-backlog 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| howl_steam_backlog-0.4.0.tar.gz | 40.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| howl_steam_backlog-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 87.2 kB
Release files / howl_steam_backlog-0.4.0.tar.gz
| Download URL | howl_steam_backlog-0.4.0.tar.gz |
|---|---|
| Size | 40.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3b1a40fde5d5e1e030d31a97ddabf4891b2070a05020af26b0a8133c3d4c01c7
|
|
BLAKE2b-256 checksum How to use checksums |
9241f25f6da70887356dcfe2b3318e0de5128ef379055524976fd9d5fcce605c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logRelease files / howl_steam_backlog-0.4.0-py3-none-any.whl
| Download URL | howl_steam_backlog-0.4.0-py3-none-any.whl |
|---|---|
| Size | 46.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e4d03d59c96ed27344a5c0167dac88bb4d4a743cb07043314a1936250664344b
|
|
BLAKE2b-256 checksum How to use checksums |
a2cf9ecb4c39b610d16e816b386adae52a8c51566688bd334a022ea6599ae9a8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency log