Overview
What the tool does and which ban types it can detect
The Steam Ban Monitor is a Python script that monitors a list of Steam accounts (SteamID64) for changes in ban status using the official Steam Web API. When a ban status changes, the script posts a Discord embed notification to a configured webhook.
Supported ban types
- VAC bans
- Game bans (Steam-visible βGame ban on recordβ)
- Community bans
- Economy / Trade restrictions
- In-game bans that are not reflected on Steam profile
- Server-only bans on community servers
- Any internal game ban system not exposed via Steam Web API
Requirements
Software, access keys, and network requirements
Software
- Windows 10 / Windows 11
- Python 3.9+ (recommended: latest stable)
- Python package:
requests
Accounts / Keys
- Steam Web API Key
- Discord Webhook URL (channel where notifications should be posted)
Security: Never publish your Steam API key or Discord webhook URL. If exposed, rotate both immediately.
Folder structure
Recommended layout for script, config and state storage
SteamBanMonitor/
βββ steam_ban_monitor.py
βββ config.json
βββ ban_state.json (auto-created / updated)
βββ start_monitor.bat (recommended for Task Scheduler)
The file ban_state.json is created automatically and stores the last known ban state per SteamID.
This enables change detection (new ban vs. unban vs. ban update).
Installation
Create folder, install dependencies, verify Python
1) Create a directory
C:\SteamBanMonitor\
2) Place files
Copy the following into the folder:
steam_ban_monitor.pyconfig.json
3) Install Python dependency
pip install requests
Configuration
How to configure API keys, webhook, and SteamID64 list
The tool is configured via config.json. All monitored accounts must be provided as SteamID64
(17-digit numeric IDs).
Example config.json
{
"steam_api_key": "YOUR_STEAM_API_KEY",
"discord_webhook_url": "YOUR_DISCORD_WEBHOOK_URL",
"steam_ids": [
"76561198084321450",
"76561197998666186"
],
"state_file": "ban_state.json"
}
Fields
| Field | Description |
|---|---|
steam_api_key | Steam Web API key used to query ban and profile data. |
discord_webhook_url | Discord webhook endpoint to receive embed notifications. |
steam_ids | Array of SteamID64 values to monitor. |
state_file | Local JSON file storing last known ban status per SteamID. |
Tip: If you previously used a wrong SteamID64, the API may return an empty list. Always verify SteamIDs before adding them.
Main Script β steam_ban_monitor.py
Complete Python source code (copy, modify, run)
Create a file named steam_ban_monitor.py in the same folder as config.json,
then paste the code below. The script checks Steam ban status and posts Discord embed notifications on changes
(including unbans). The current Steam name (persona name) is included in the alert.
import json
import os
import time
import requests
CONFIG_FILE = "config.json"
def load_config():
with open(CONFIG_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def load_state(state_file):
if not os.path.exists(state_file):
return {}
with open(state_file, "r", encoding="utf-8") as f:
return json.load(f)
def save_state(state_file, state):
with open(state_file, "w", encoding="utf-8") as f:
json.dump(state, f, indent=2)
def fetch_bans(steam_api_key, steam_ids):
url = "https://api.steampowered.com/ISteamUser/GetPlayerBans/v1/"
params = {
"key": steam_api_key,
"steamids": ",".join(steam_ids)
}
resp = requests.get(url, params=params, timeout=10)
resp.raise_for_status()
return resp.json().get("players", [])
def fetch_persona_names(steam_api_key, steam_ids):
# Returns dict: { steamid: personaname }
url = "https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/"
params = {
"key": steam_api_key,
"steamids": ",".join(steam_ids)
}
resp = requests.get(url, params=params, timeout=10)
resp.raise_for_status()
data = resp.json()
result = {}
players = data.get("response", {}).get("players", [])
for p in players:
sid = p.get("steamid")
if sid:
result[sid] = p.get("personaname", "")
return result
def send_discord_embed(webhook_url, embed):
payload = {"embeds": [embed]}
resp = requests.post(webhook_url, json=payload, timeout=10)
resp.raise_for_status()
def player_state_from_api(player):
return {
"VACBanned": bool(player.get("VACBanned")),
"NumberOfVACBans": int(player.get("NumberOfVACBans", 0)),
"NumberOfGameBans": int(player.get("NumberOfGameBans", 0)),
"CommunityBanned": bool(player.get("CommunityBanned")),
"EconomyBan": player.get("EconomyBan", "none")
}
def has_any_ban(state):
if not state:
return False
return (
state.get("VACBanned", False)
or state.get("NumberOfVACBans", 0) > 0
or state.get("NumberOfGameBans", 0) > 0
or state.get("CommunityBanned", False)
or (state.get("EconomyBan", "none") != "none")
)
def pick_color_for_state(state):
# VAC = red, Game ban (no VAC) = orange, Community/Economy only = yellow, No bans = green
if not has_any_ban(state):
return 0x00FF00
if state.get("VACBanned", False) or state.get("NumberOfVACBans", 0) > 0:
return 0xFF0000
if state.get("NumberOfGameBans", 0) > 0:
return 0xFFA500
return 0xFFFF00
def build_embed(steam_id, persona_name, old_state, new_state):
profile_url = f"https://steamcommunity.com/profiles/{steam_id}"
old_has = has_any_ban(old_state)
new_has = has_any_ban(new_state)
if old_has and not new_has:
title = f"β
Unban detected β {persona_name}"
desc = "A monitored Steam account appears to have no active bans anymore."
elif not old_has and new_has:
title = f"π¨ New ban detected β {persona_name}"
desc = "A monitored Steam account has received a new ban or restriction."
else:
title = f"β οΈ Ban status changed β {persona_name}"
desc = "The ban status of a monitored Steam account has changed."
vac_banned = new_state.get("VACBanned", False) or new_state.get("NumberOfVACBans", 0) > 0
vac_status = "BANNED" if vac_banned else "Not banned"
fields = [
{"name": "Steam Name", "value": persona_name or "(unknown)", "inline": False},
{"name": "Steam ID", "value": steam_id, "inline": False},
{"name": "VAC Status", "value": f"{vac_status} (Total VAC bans: {new_state.get('NumberOfVACBans', 0)})", "inline": True},
{"name": "Game Bans", "value": str(new_state.get("NumberOfGameBans", 0)), "inline": True},
{"name": "Community Ban", "value": "Yes" if new_state.get("CommunityBanned", False) else "No", "inline": True},
{"name": "Economy / Trade Ban", "value": new_state.get("EconomyBan", "none"), "inline": True},
{"name": "Profile", "value": profile_url, "inline": False},
]
return {
"title": title,
"description": desc,
"color": pick_color_for_state(new_state),
"fields": fields
}
def main():
cfg = load_config()
api_key = cfg["steam_api_key"]
webhook_url = cfg["discord_webhook_url"]
steam_ids = cfg["steam_ids"]
state_file = cfg.get("state_file", "ban_state.json")
state = load_state(state_file)
print("Starting Steam Ban Monitor...")
while True:
try:
bans = fetch_bans(api_key, steam_ids)
names = fetch_persona_names(api_key, steam_ids)
updated = False
for player in bans:
steam_id = player["SteamId"]
persona_name = names.get(steam_id, "(unknown)")
new_state = player_state_from_api(player)
old_state = state.get(steam_id)
if old_state is None:
# First time seeing this SteamID: store baseline
state[steam_id] = new_state
updated = True
print(f"[Init] Saved initial state for {steam_id} ({persona_name}).")
# If already banned on first run, send one initial alert
if has_any_ban(new_state):
fake_old = {
"VACBanned": False,
"NumberOfVACBans": 0,
"NumberOfGameBans": 0,
"CommunityBanned": False,
"EconomyBan": "none"
}
embed = build_embed(steam_id, persona_name, fake_old, new_state)
send_discord_embed(webhook_url, embed)
print(f"[Discord] Initial ban notification sent for {steam_id} ({persona_name}).")
continue
if new_state != old_state:
state[steam_id] = new_state
updated = True
embed = build_embed(steam_id, persona_name, old_state, new_state)
send_discord_embed(webhook_url, embed)
print(f"[Discord] Notification sent for {steam_id} ({persona_name}).")
if updated:
save_state(state_file, state)
except Exception as e:
print(f"[Error] {e}")
# Check interval: 300 seconds = 5 minutes
time.sleep(300)
if __name__ == "__main__":
main()
If you remove a ban and restart the script after deleting or resetting ban_state.json, the script will treat the current state as the baseline.
Unban notifications require a previous βbannedβ state to be stored in ban_state.json.
Startup Script β start_monitor.bat
Starts the Python script from a fixed folder
Create a file named start_monitor.bat in your monitor folder and adjust the path if needed.
This is the recommended entry point for Windows Task Scheduler.
@echo off
cd /d C:\SteamBanMonitor
python steam_ban_monitor.py
How it works
Polling loop, state comparison and notification rules
The script runs in a loop. Every interval (default: 5 minutes), it calls:
ISteamUser/GetPlayerBans (ban status) and ISteamUser/GetPlayerSummaries
(current Steam persona name). It then compares the result with the previously stored state in
ban_state.json.
Change detection rules
- If any ban-related field changes, a Discord embed is posted.
- Unbans are detected when a previously banned account has no bans/restrictions anymore.
- On first run, the state is saved. If an account is already banned, an initial alert can be sent (depending on script version).
Discord notifications
Embed layout, fields, and color-coding
Notifications are delivered as Discord embeds. Each embed includes Steam Name, SteamID64, ban status summary and a profile link.
Color-coding
| Condition | Color |
|---|---|
| VAC ban present | Red |
| Game ban present (without VAC) | Orange |
| Community/Economy restriction only | Yellow |
| No bans (unban detected) | Green |
Tip: Keeping the CMD window visible is useful for real-time debugging.
You will see [Init], [Change] and [Discord] logs.
Windows Task Scheduler
Run automatically on startup and keep the script running
Recommended start file
Create start_monitor.bat inside your monitor directory:
@echo off
cd /d C:\SteamBanMonitor
python steam_ban_monitor.py
Create a scheduled task
- Open Task Scheduler β click Create Taskβ¦
- Name: Steam Ban Monitor
- Enable: Run with highest privileges
- Trigger: At startup
- Action: Start a program β Program:
C:\SteamBanMonitor\start_monitor.bat - Start in:
C:\SteamBanMonitor - Save task β optionally run it manually once for validation
API limitations
What Steam does not provide (and update delays)
Steamβs Web API does not provide the specific game title for which a game ban was issued. It typically only provides counts and boolean flags. Additionally, some profile changes may appear on the Steam profile page before the API returns the updated values.
Important: The Steam profile page may show a ban immediately while the API may update later. The monitor can only react to what the API returns.
Troubleshooting
Common issues and how to resolve them quickly
No Discord notification received
- Verify the webhook URL is correct and still valid.
- Confirm the SteamID64 is correct (wrong IDs can lead to an empty API response).
- Check the CMD window for
[Error]entries. - Ensure the monitored ban type is visible via Steam Web API (not only in-game).
API returns empty players list
If the API output looks like {"players": []}, the SteamID64 is usually invalid or formatted incorrectly.
Re-check the SteamID64 value (17 digits, numeric).
Unban not detected
- API updates can be delayed; wait for the next polling cycles.
- If you restart the script after deleting the state file, the tool will treat the current state as baseline.
Security notes
Protect API keys and webhook URLs
Do not store secrets in public repositories. Treat steam_api_key and
discord_webhook_url as sensitive credentials. Rotate them immediately if leaked.
Recommended practice: keep config.json out of public source control or use environment variables
for secrets (advanced).