Automated Steam playtime farming tool with queue management, resumable state, and reliable reconnection.
Farms games sequentially through a configurable queue, persists progress across restarts, and reconnects automatically after network interruptions with exponential backoff. Uses WebSocket transport to remain stable on restrictive networks and mobile environments (Android/Termux).
- Sequential game queue with per-game minute targets
- Persistent state — resumes exactly where it left off after any restart
- Exponential-backoff reconnection (10 s → 20 s → … → 10 min cap), unlimited retries for network issues
- WebSocket/HTTPS transport — avoids TLS record-header mismatches on proxied or mobile networks
- Automatic refresh token persistence — subsequent logins need no password or Steam Guard code
- Steam Guard support: mobile authenticator (via
shared_secret) or interactive email code - Log rotation at 10 MB
- PM2 / systemd ready
- Node.js >= 18.0.0
- A Steam account
npm install(see Installation)
git clone https://github.com/Tadakai/steam-harvest.git
cd steam-harvest
npm install
cp config.toml.example config.tomlEdit config.toml with your Steam credentials and game list (see Configuration), then run:
node harvest.jsOn first run, if no shared_secret is configured, you will be prompted for a Steam Guard code. After a successful login the refresh token is saved to .refresh_token — subsequent runs (and reconnections) are fully automatic.
All settings live in config.toml. Credentials can alternatively be supplied via environment variables, which take precedence over the config file.
| Config key | Env variable | Required | Description |
|---|---|---|---|
steam.username |
STEAM_HARVEST_USERNAME |
Yes | Steam account name |
steam.password |
STEAM_HARVEST_PASSWORD |
Yes | Steam password |
steam.shared_secret |
STEAM_HARVEST_SHARED_SECRET |
No | Base64 shared secret for unattended 2FA |
steam.persona |
— | No | Persona state while farming (0–7; 7 = Invisible) |
[games] |
— | Yes | appId = targetMinutes pairs |
[game_names] |
— | No | appId = "Display Name" pairs |
[steam]
username = "myaccount"
password = "mypassword"
shared_secret = "AAAA..."
persona = 7
[games]
730 = 12900
578080 = 19260
[game_names]
730 = "CS2"
578080 = "PUBG"node harvest.js # Start or resume farmingWith PM2 (recommended for always-on operation):
npm run pm2:start # Start under PM2 with auto-restart
npm run pm2:logs # Tail live logs
npm run pm2:stop # Stop
npm run pm2:restart # RestartProgress is saved every minute and on graceful shutdown (Ctrl+C / SIGTERM). State is stored in .harvest_state.json — delete it to start fresh.
If you see AccessDenied or an auth failure that discards the token, delete .refresh_token and run harvest.js again to log in interactively and get a new one.
Add your authenticator's shared_secret to config.toml or the STEAM_HARVEST_SHARED_SECRET environment variable. Without it, a TTY is required for the initial login.
Steam only records playtime for accounts that are not Steam Limited (accounts with no qualifying purchase). Playtime for VAC-banned or region-restricted games may also not accumulate.
Steam Harvest is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
See LICENSE for the full license text.