๐ด Steam Idle Bot
Farm Steam playtime and trading-card drops on autopilot โ accurately, efficiently, and without babysitting.
โจ Why Steam Idle Bot?
Most idlers blindly run every game in your library. This one is selective and accurate:
- ๐ด Targeted idling โ detects which games have trading cards and how many drops remain, then idles only those.
- ๐ง Gets faster every run โ persistent no-drop caches skip fully farmed games, while short-lived in-session caches avoid re-scraping games that were just confirmed to still have drops.
- ๐ Trustworthy verdicts โ before relying on a Steam web session, the bot verifies itโs actually logged in. A logged-out/expired session is detected and reported instead of silently idling drained games.
- ๐ช Self-healing auth โ if your configured cookies arenโt a valid community session, it can pull a fresh one from a browser youโre already signed into Steam with.
- ๐ฅ๏ธ Readable terminal UI โ a live panel with game names, cards remaining, and idle time, plus a full session report when you stop.
- ๐ Two backends โ the built-in Python Steam client (handles Steam Guard / 2FA) or delegation to a local
steam-utilityinstall, with automatic fallback. - ๐ Rotates mid-session โ inventory snapshots can prove a game dropped all known remaining cards before badge pages update, so the next refresh can swap it out.
- โก Modern & tested โ
uv-managed, reproducible environments, full test suite, type-checked.
๐ Quick Start (under 5 minutes)
-
Install uv (if you donโt have it):
curl -LsSf https://astral.sh/uv/install.sh | sh -
Clone & install:
git clone https://github.com/bernardopg/steam-idler-python.git cd steam-idler-python uv sync -
Configure:
cp .env.example .env # Edit .env โ set USERNAME, PASSWORD, and ideally STEAM_API_KEY -
Preview (no Steam contact):
./run.sh --dry-run -
Run:
./run.sh # terminal ./run-gui.sh # desktop GUI (Tkinter)Enter your Steam Guard code if prompted (Python backend only).
๐ก Get an API key. A free Steam Web API key enables automatic library sync and badge-based drop filtering. Without it, the bot falls back to your manual
GAME_APP_IDSlist.
๐ฆ Requirements
- Python 3.12+ โ
uvinstalls and manages it for you. - A Steam account with games that support trading cards.
- Steam Web API key (recommended) โ for library sync and badge progress.
- An authenticated web session (for drop filtering) โ see Authentication & card-drop accuracy.
- (Optional) a dedicated/secondary Steam account.
โ๏ธ Configuration
Settings load from environment variables and a .env file (copy .env.example). Precedence: CLI flags โ environment variables โ .env โ defaults. A legacy config.py is also read if present, but .env is the recommended path.
Core
| Variable | Description | Default |
|---|---|---|
USERNAME, PASSWORD |
Steam login credentials (required). | โ |
STEAM_API_KEY |
Unlocks library sync and badge progress. | (none) |
USE_OWNED_GAMES |
Auto-fetch your full library via the API. | true |
GAME_APP_IDS |
Manual game list (JSON [570,730] or CSV 570,730) used when not syncing owned games. |
[570,730] |
EXCLUDE_APP_IDS |
App IDs to always skip. | [] |
MAX_GAMES_TO_IDLE |
Max simultaneous games (Steam hard limit: 32). | 30 |
FILTER_TRADING_CARDS |
Only idle games that have trading cards. | true |
FILTER_COMPLETED_CARD_DROPS |
Skip games whose drops are exhausted. | true |
Idling backend
| Variable | Description | Default |
|---|---|---|
IDLING_BACKEND |
python (built-in steam client) or steam_utility (delegate to a local install). |
python |
STEAM_UTILITY_PATH |
Path to a local steam-utility-multiplataform checkout (auto-discovered in sibling dirs if empty). |
(auto) |
The Python backend handles Steam Guard / 2FA and builds an authenticated web session. If it fails to initialize, log in, start, or reconnect, the bot automatically falls back to the steam_utility backend.
Authentication & cookies
| Variable | Description | Default |
|---|---|---|
STEAM_WEB_COOKIES |
Authenticated cookies for community scraping. Accepts a JSON object, a browser-export JSON array, or k=v; k=v. |
{} |
AUTO_BROWSER_COOKIES |
If the configured session isnโt a valid community login, recover cookies from a locally logged-in browser. | true |
BROWSER_COOKIES_BROWSER |
Which browser to read from: auto, chrome, firefox, edge, brave, chromium, opera, vivaldi, librewolf. |
auto |
Caches
| Variable | Description | Default |
|---|---|---|
ENABLE_CARD_CACHE |
Master switch for both on-disk caches. | true |
CARD_CACHE_PATH |
โHas trading cardsโ cache. | .cache/trading_cards.json |
CARD_CACHE_TTL_DAYS |
TTL for the trading-card cache. | 30 |
DROP_CACHE_PATH |
โNo drops remainingโ cache (per account). | .cache/no_drop_cards.json |
DROP_CACHE_TTL_DAYS |
TTL for the no-drop cache. | 90 |
Logging & performance
| Variable | Description | Default |
|---|---|---|
LOG_LEVEL |
DEBUG, INFO, WARNING, ERROR, CRITICAL. Use INFO for clean output. |
INFO |
LOG_FILE |
Optional log file path. | (none) |
API_TIMEOUT |
Per-request timeout (seconds). | 10 |
RATE_LIMIT_DELAY |
Base delay between API calls; auto-increases on HTTP 429. Raise for huge libraries. | 1.0 |
MAX_CHECKS |
Cap the number of trading-card lookups (perf tuning). | (none) |
SKIP_FAILURES |
Suppress non-timeout errors during card checks. | false |
๐ Authentication & card-drop accuracy
Filtering out games with no remaining drops requires reading your authenticated Steam community badge pages. This is the single most important thing to get right โ otherwise the bot idles games that have nothing left to drop.
The catch: the steamLoginSecure cookie is audience-scoped. A token copied from store.steampowered.com has audience web:store and is rejected by steamcommunity.com โ pages load logged-out, every game looks ambiguous, and filtering becomes meaningless. You need a web:community token.
The bot defends against this automatically:
- Session verification โ before trusting any verdict, it probes a community page to confirm youโre actually logged in. If not, it logs a clear warning and stays conservative (excludes unknowns) instead of idling drained games.
- Browser recovery โ with
AUTO_BROWSER_COOKIES=true(default), it pulls a validweb:communitysession straight from a browser youโre signed into Steam with. Because community tokens are short-lived, this keeps the bot self-healing across runs.
Your options, simplest first:
- โ
Stay logged into Steam in your browser and leave
AUTO_BROWSER_COOKIES=true. Nothing else to do. - ๐ Use the Python backend (
IDLING_BACKEND=python): logging in once (with 2FA) mints a proper community session. - ๐ Paste cookies manually into
STEAM_WEB_COOKIESโ make sure they come fromsteamcommunity.com, not the store.
โน๏ธ If the badge API reports no
cards_remaining(common once all your badges are completed), the bot reads the count directly from the badge page so the panel and report still show real numbers.
โถ๏ธ Running the bot
./run.sh --dry-run # preview config + chosen games, no Steam contact
./run.sh # normal run (terminal)
./run-gui.sh # desktop GUI
./run.sh --max-games 10 # cap idled games
./run.sh --no-trading-cards # skip card filtering (idle the raw list)
./run.sh --keep-completed-drops # include fully-farmed games
STEAM_IDLE_SKIP_SYNC=1 ./run.sh # skip the runner's preflight uv sync
STEAM_IDLE_RUNNER_VERBOSE=1 ./run.sh # show uv sync output while preparing
uv run python -m steam_idle_bot --dry-run # direct module entry
The terminal runner prints a compact banner, writes bot output to logs/runs/run_*.log, and keeps Python out of a shell pipeline so Ctrl+C reaches the bot directly. Normal runs clear stale exported Steam Idle Bot environment overrides so .env wins; set STEAM_IDLE_PRESERVE_ENV=1 when exported variables are intentional.
CLI flags
| Flag | Purpose |
|---|---|
--dry-run |
Print config and chosen games without contacting Steam. |
--gui |
Launch the desktop GUI (same as ./run-gui.sh). |
--no-trading-cards |
Skip card detection; use the raw game list. |
--keep-completed-drops |
Include games that already exhausted their drops. |
--max-games N |
Override max concurrent games. |
--config PATH |
Load a custom configuration file. |
--no-cache |
Disable the on-disk caches for this run. |
--max-checks N |
Cap trading-card lookups (large libraries). |
--skip-failures |
Suppress non-timeout warnings during checks. |
See the Usage Guide for combined-flag recipes.
๐ง How it works
owned games โโถ has trading cards? โโถ drops remaining? โโถ exclusions โโถ idle (max 32)
(badge catalog (badge API or (config +
+ store API, authenticated session-drained)
cached) scraping, cached)
- Library โ fetched via the Steam Web API (with names), or your manual list.
- Has cards โ resolved from the badge catalog, with a store-API fallback; store results are cached on disk and badge payloads are cached briefly in memory.
- Drops remaining โ preferred from the badge API; when it lacks data, falls back to authenticated community-page scraping. Games confirmed without drops are cached and skipped on future runs; games recently confirmed with drops are trusted for a short in-session window to avoid redundant refresh traffic.
- Idle loop โ starts idling, then re-runs the pipeline every
REFRESH_INTERVAL_SECONDS, reconnecting (and failing over backends) as needed. Each refresh also compares current inventory to the pre-run snapshot; when inventory-confirmed drops equal all known remaining cards for a game, that app is excluded from the next selection so another candidate can take its slot even if Steam badge pages lag.
For a deeper architectural map, see CLAUDE.md in the repo root.
๐ฅ๏ธ What youโll see
A live panel while idling:
๐ฎ Steam Idle Bot โ em idle agora
โโโโโฌโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโ
โ # โ App ID โ Jogo โ Cartas restantes โ Tempo idle โ
โโโโโผโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโค
โ 1 โ 391540 โ Undertale โ 3 โ 0 min โ
โ 2 โ 362890 โ Black Mesa โ 2 โ 0 min โ
โโโโโดโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโ
18 games idling โข cards remaining: 51 โข session: 0 min
Tip: run with
LOG_LEVEL=INFO(the default) for this clean view.DEBUGadds verbose per-game lines, useful only for troubleshooting.
The final session report now shows a drop Source. remaining-count means before/after badge-page counts decreased; inventory means Steam inventory proved a new card even if badge pages lagged; count+inventory means both sources agreed. When you see Cards: 3 โ 3 with an inventory-confirmed drop, the report explains that the badge/scraper count lagged and inventory was used for the total.
๐ Troubleshooting
| Symptom | Fix |
|---|---|
Missing credentials |
Set USERNAME/PASSWORD in .env (no placeholder values). |
| Login failed | Check credentials/2FA; confirm the account isnโt locked. |
| โNOT authenticated against steamcommunityโ warning | Your session is store-only/expired. Stay logged into Steam in a browser (with AUTO_BROWSER_COOKIES=true), use IDLING_BACKEND=python, or paste community cookies. See Authentication. |
| Idles drained games / misses real ones | Same root cause as above โ the session isnโt a valid community login. |
โCards remainingโ shows ? |
The badge API has no cards_remaining for a fully-completed profile; counts are read from badge pages when the session is authenticated. |
| No games to idle | Add an API key, or run --no-trading-cards / --keep-completed-drops. |
| First run is slow | Expected โ it scans the whole library once, then caches. Later runs are fast. |
| Import errors | Run uv sync; ensure Python 3.12+. |
For deeper diagnosis, set LOG_LEVEL=DEBUG and open an issue with redacted logs (never paste cookies, tokens, or your API key).
๐งช Developer guide
steam-idler-python/
โโโ .env.example
โโโ run.sh / run-gui.sh
โโโ pyproject.toml / uv.lock
โโโ src/steam_idle_bot/
โ โโโ __main__.py # entry point
โ โโโ main.py # SteamIdleBot orchestrator
โ โโโ gui.py # Tkinter GUI
โ โโโ config/ # Pydantic settings
โ โโโ steam/ # backends + card/badge/cookie services
โ โโโ utils/ # logging, tracker, exceptions
โโโ tests/
โโโ docs/
uv sync --dev # install dev deps
uv run pytest -q # run tests
uv run pytest -q --cov=src/steam_idle_bot --cov-report=term-missing
uv run ruff check . # lint
uv run ruff format . # format
uv run mypy src # type-check
CI runs the suite on Python 3.12โ3.14. PRs welcome โ include tests, update docs, and describe your changes. Enable the pre-commit guard with git config core.hooksPath .githooks (it blocks committing config.py and cache/venv files).
๐ Security
- Never commit secrets.
.env,config.py, and.cache/are git-ignored. Keep your API key and cookies local. - Steam cookies are credentials โ treat them like passwords.
- Prefer a dedicated/secondary Steam account.
- Vulnerability reporting and accepted-advisory notes: SECURITY.md.
โ๏ธ Responsible use & license
For educational and personal use. Follow Steamโs Terms of Service and your local laws. Not affiliated with Valve. Licensed under MIT.
๐ Resources
- Usage cheatsheet ยท Security policy
- GitHub repository ยท Report an issue
- Enjoying it? Star the repo โญ