A wishlist monitoring tool for game developers publishing on Steam.
Stop refreshing Steamworks. Wishlist Pulse syncs your complete wishlist history from Steam's official Wishlist Data API, detects anomalies in real time, and pushes what matters — adds, removes, purchases, gifts, regional surges — straight to Telegram and Discord. Spot the impact of a trailer drop within hours, catch unexpected regional spikes after an influencer mention, and build a full timeline of every game you're tracking — all without opening Steamworks once.
Single binary (~4 MB) for Windows, macOS, and Linux (including ARM). Built-in web dashboard with charts and anomaly highlights, SQLite storage, minimal RAM — runs happily on a Raspberry Pi.
1. Install:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/hortopan/steam-wishlist-pulse/releases/download/v0.1.10/wishlist-pulse-installer.sh | shOr via Homebrew, Docker, or download a binary.
2. Run it:
wishlist-pulse3. Open http://localhost:3000 — the setup wizard will ask for your Steam Financial API Group Web API Key and walk you through the rest. (Steamworks → Users & Permissions → Manage Groups → create a Financial API Group with General and Financial permissions)
That's it. Telegram and Discord bots are optional — add them later from the dashboard.
- Real-time Telegram & Discord notifications — adds, deletes, purchases, gifts, with deltas
- Anomaly detection — highlights unusual activity using a modified z-score algorithm so you can cut through the noise and catch what matters
- Configurable alert modes — receive every update or only anomalies, with four sensitivity presets (Relaxed → Very Sensitive) plus full custom tuning
- Track multiple games from a single instance
- Full historical data synced from Steam for spotting trends
- Web dashboard — manage games, view stats, configure alerts visually
- Telegram & Discord bot commands — track/untrack games and manage subscriptions from chat
- Two access levels — Admin (full control) and Read-only (dashboard view)
A built-in admin panel served from the same binary — no separate deploy:
- View all tracked games with latest stats and store images
- Add/remove games by App ID or Steam store URL
- Configure Steam API key and bot tokens
- Manage channel subscriptions
- Configure anomaly detection sensitivity and notification preferences
- Secured with Argon2 password hashing, JWT sessions, rate-limited login, and HTTPS cookies
By default, every wishlist change triggers a notification. If that's too noisy, switch to Anomalies only mode — you'll only be notified when the change is statistically unusual compared to recent history.
| Mode | Behaviour |
|---|---|
| Every update | Notify on every change (default). Anomalous metrics are still highlighted |
| Anomalies only | Notify only when unusual activity is detected. Normal changes are recorded silently |
When anomaly detection doesn't have enough history yet (fewer than 3 snapshots), it falls back to notifying on every change so you never miss early data.
Sensitivity presets
All presets are one-click selectable from the dashboard's Alerts tab:
| Preset | Lookback | Sensitivity (up/down) | Min absolute | MAD floor | Best for |
|---|---|---|---|---|---|
| Relaxed | 14 days | 3.0 / 3.0 | 10 | 10% | Noisy games with frequent churn |
| Balanced | 14 days | 2.0 / 2.0 | 5 | 5% | Good default for most games |
| Sensitive | 7 days | 1.5 / 1.5 | 2 | 2% | Low-traffic games or early warnings |
| Very Sensitive | 7 days | 1.0 / 1.0 | 1 | 0% | Critical monitoring — flags nearly any deviation |
| Custom | — | — | — | — | Full manual control over all parameters |
How anomaly detection works
The detector uses a modified z-score (Median + MAD) over the lookback window. Each metric — adds, deletes, purchases, gifts — is evaluated independently, with separate sensitivity thresholds for upward spikes and downward drops. Country-level anomalies are detected too, so you can spot regional surges after a localized event.
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/hortopan/steam-wishlist-pulse/releases/download/v0.1.10/wishlist-pulse-installer.sh | shbrew install hortopan/tap/wishlist-pulsedocker run -p 3000:3000 -v wishlist-pulse-data:/data ghcr.io/hortopan/steam-wishlist-pulse:latestMulti-arch image (amd64/arm64) available on GitHub Container Registry.
Prebuilt binaries for all platforms are available on the Releases page:
| Platform | File |
|---|---|
| Apple Silicon macOS | wishlist-pulse-aarch64-apple-darwin.tar.xz |
| Intel macOS | wishlist-pulse-x86_64-apple-darwin.tar.xz |
| x64 Windows | wishlist-pulse-x86_64-pc-windows-msvc.zip |
| ARM64 Linux | wishlist-pulse-aarch64-unknown-linux-musl.tar.xz |
| x64 Linux | wishlist-pulse-x86_64-unknown-linux-musl.tar.xz |
Build from source
Requires Rust toolchain and Node.js.
git clone git@github.com:hortopan/steam-wishlist-pulse.git
cd wishlist-pulse-bot
cargo build --release
./target/release/wishlist-pulseOptions can be set via CLI flags, environment variables, or both (passwords are env-only):
| Flag | Env Var | Default | Description |
|---|---|---|---|
--bind-web-interface |
BIND_WEB_INTERFACE |
0.0.0.0:3000 |
Web UI address |
--database-path |
DATABASE_PATH |
~/.local/share/wishlist-pulse/data.db |
SQLite database location |
| — | ADMIN_PASSWORD |
(set via UI) | Admin password (env only, seeds on first boot) |
| — | READ_PASSWORD |
(set via UI) | Read-only password (env only, seeds on first boot) |
| — | FORCE_PASSWORD_RESET |
false |
Overwrite existing password(s) with ADMIN_PASSWORD / READ_PASSWORD (recovery) |
--poll-interval-minutes |
POLL_INTERVAL_MINUTES |
5 |
Steam polling interval |
--insecure |
— | false |
Disable HTTPS cookie requirement (dev only) |
| — | ENCRYPTION_SECRET |
(none) | Passphrase for encrypting sensitive data at rest |
Everything else — API keys, Telegram config, tracked games — is managed through the dashboard.
Recovering a lost password
ADMIN_PASSWORD and READ_PASSWORD only seed the database on first boot — once a password is stored, those env vars are ignored. To recover a lost password, set FORCE_PASSWORD_RESET=1 alongside the password you want to overwrite, then restart once:
FORCE_PASSWORD_RESET=1 ADMIN_PASSWORD='new-strong-password' wishlist-pulseAfter it boots successfully (look for Admin password reset from env (FORCE_PASSWORD_RESET) in the logs), remove FORCE_PASSWORD_RESET, ADMIN_PASSWORD, and READ_PASSWORD from your environment — FORCE_PASSWORD_RESET so future restarts don't keep overwriting, and the password vars so a plaintext password isn't sitting in /proc/<pid>/environ or ps eww output longer than necessary. All existing browser sessions are invalidated — you'll need to log in again.
You can reset both passwords in the same boot by setting ADMIN_PASSWORD and READ_PASSWORD together with FORCE_PASSWORD_RESET=1.
HTTPS & cookies
By default, session cookies are marked Secure and require HTTPS. If you're accessing the dashboard over plain HTTP (e.g. locally, on a LAN, or in a test environment), the login page will let you know. You have two options:
-
Put the app behind an HTTPS reverse proxy (nginx, Caddy, etc.) — recommended for any internet-facing setup.
-
Start with
--insecureto allow cookies over plain HTTP:wishlist-pulse --insecure
Encryption at rest
API keys, bot tokens, and other secrets you enter through the dashboard are stored in the SQLite database. By default they are stored in plaintext. Set ENCRYPTION_SECRET to any strong passphrase and all sensitive values will be encrypted with AES-256-GCM before being written to disk. This is strongly recommended — especially if the database file lives on shared storage or is included in backups. The secret is used to derive an encryption key via HKDF-SHA256; changing it later will require re-entering your stored credentials.
Bot Commands — both Telegram and Discord support the same core commands
| Command | What it does |
|---|---|
/track <app_id> |
Start tracking a game |
/untrack <app_id> |
Stop tracking a game |
/list |
Show all tracked games |
/subscribe |
Subscribe a channel to a game's updates |
/unsubscribe |
Unsubscribe from a game |
/subscriptions |
List active subscriptions |
/status |
Check bot and polling status |
/whoami |
Show your user ID (Discord only) |
Why Wishlist Pulse?
Steam wishlists are the closest thing indie developers have to a demand signal before launch. They're a leading indicator of first-week sales (industry median ~10% wishlist-to-sales conversion), and wishlist velocity — the rate wishlists change — is often more predictive than the total count alone. Every wishlister also receives email notifications on launch day and during 20%+ sales, making your wishlist count functionally a platform-native mailing list you can't afford to ignore.
The problem is that Steamworks' Wishlist Reporting updates a few times per day, but at unpredictable times. After a trailer drop, a festival demo, or an influencer mention, you end up refreshing the stats page dozens of times hoping to catch movement.
In March 2026, Valve opened the GetAppWishlistReporting API, providing programmatic access to wishlist adds, deletes, purchases, gifts, plus country and language breakdowns.
Wishlist Pulse sits on top of that API. It polls for each of your tracked games, diffs against the last snapshot, and notifies you on Telegram and Discord whenever something changes. It uses the same data Valve publishes — it won't get you numbers any faster than Steam makes them available — but it means you stop refreshing and the data comes to you. Plus, every snapshot is stored locally, so you build up a full history for spotting trends, measuring event uplift, and tracking velocity over time.
Under the Hood
A single Rust binary running four concurrent subsystems: a polling loop that diffs wishlist snapshots, a Telegram bot and a Discord bot for commands and notifications, and a web server serving the embedded Svelte dashboard. All data lives in SQLite (WAL mode) — no external database needed.
| Backend | Rust — Axum, Teloxide, Serenity, rusqlite |
| Frontend | Svelte + TypeScript (Vite), embedded at compile time via rust-embed |
| Auth | Argon2 + JWT |
MIT — see LICENSE for details.




