Monitoring and alerting for ban waves
Monitoring and alerting for ban waves
Ban waves are rarely random. Platforms like Amazon, eBay, TikTok, and most affiliate networks run periodic sweeps, usually triggered by a policy update, a manual report, or an internal risk model refresh. When one hits, accounts can drop from zero to banned in under an hour. If you are not monitoring, you find out the next morning when revenue is already gone.
This tutorial is for operators running ten or more accounts across one or more platforms. it covers building a lightweight monitoring stack that checks account health at regular intervals, fires alerts the moment something looks wrong, and logs enough history that you can spot a wave pattern before it wipes out everything. you do not need a DevOps background. you do need a VPS, basic comfort with the command line, and willingness to spend roughly $10 to $30 per month on infrastructure.
By the end you will have a cron-driven health checker, a Telegram or Slack alert pipeline, a simple SQLite log, and a dashboard you can actually read at 3am on your phone.
what you need
- VPS: a $6/month Hetzner CX11 or equivalent. Ubuntu 22.04 LTS. keep this separate from the IPs your accounts use.
- Python 3.10+ and pip
- Telegram bot (free) or Slack webhook (free on free tier): for alerts
- Playwright or requests + httpx: for account health checks. Playwright is heavier but handles JS-rendered dashboards. httpx is fine for API-based platforms.
- SQLite: pre-installed on most Linux boxes. for logging.
- Grafana + Prometheus (optional, for the 100+ account tier): Grafana Cloud free tier handles up to 10,000 active series.
- Antidetect browser profiles (if you are doing browser-based checks): see the antidetect browser reviews at antidetectreview.org/blog/ for what actually holds up under automation.
- Proxy per account for health checks: residential or ISP. do not check accounts from your VPS IP.
- Monthly budget estimate: $6 to $30 depending on proxy spend.
step by step
step 1: define what “banned” looks like per platform
Before writing a single line of code, document what a banned or restricted account actually returns for each platform you operate on. login redirects, HTTP 403, a JSON field like "account_status": "suspended", or a dashboard element that disappears. this varies wildly.
For example, on Amazon Seller Central a suspension redirects to https://sellercentral.amazon.com/performance/notifications with a specific page title. on TikTok Shop the API returns {"code": 10004} for a suspended seller. on eBay you get a redirect to /suspendedseller.
Write these down in a signals.yaml file:
platforms:
amazon_seller:
check_url: "https://sellercentral.amazon.com/home"
banned_indicator: "Your account has been deactivated"
check_type: "page_text"
ebay:
check_url: "https://www.ebay.com/mys/summary"
banned_indicator: "/suspendedseller"
check_type: "redirect_url"
tiktok_shop:
check_url: "https://seller-us.tiktok.com/api/shop/detail"
banned_indicator: "10004"
check_type: "response_body"
Expected output: a YAML file you will load in every subsequent script.
If it breaks: if you cannot reliably identify the ban signal through automation, add a manual check step and flag accounts for human review instead of declaring them banned.
step 2: set up the VPS and Python environment
SSH into your VPS and run:
sudo apt update && sudo apt install -y python3-pip python3-venv sqlite3
python3 -m venv ~/monitor-env
source ~/monitor-env/bin/activate
pip install httpx playwright pyyaml python-telegram-bot schedule
playwright install chromium
Create the project directory:
mkdir -p ~/banwatch/{logs,profiles,config}
Expected output: no errors, Playwright installs Chromium binaries.
If it breaks: if Playwright fails on a small VPS, use httpx only and skip browser-based checks for now. you can add Playwright later on a beefier box.
step 3: build the account health checker
Create ~/banwatch/checker.py:
import httpx
import yaml
import sqlite3
import json
from datetime import datetime
DB_PATH = "/root/banwatch/logs/accounts.db"
def init_db():
conn = sqlite3.connect(DB_PATH)
conn.execute("""
CREATE TABLE IF NOT EXISTS checks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
account_id TEXT,
platform TEXT,
status TEXT,
checked_at TEXT,
raw_response TEXT
)
""")
conn.commit()
conn.close()
def log_result(account_id, platform, status, raw):
conn = sqlite3.connect(DB_PATH)
conn.execute(
"INSERT INTO checks (account_id, platform, status, checked_at, raw_response) VALUES (?,?,?,?,?)",
(account_id, platform, status, datetime.utcnow().isoformat(), raw[:500])
)
conn.commit()
conn.close()
def check_account(account, signal_config, proxy_url):
platform = account["platform"]
cfg = signal_config["platforms"][platform]
try:
with httpx.Client(proxies=proxy_url, timeout=15, follow_redirects=True) as client:
r = client.get(cfg["check_url"], headers={"Cookie": account["cookie"]})
body = r.text
final_url = str(r.url)
if cfg["check_type"] == "page_text" and cfg["banned_indicator"] in body:
return "BANNED"
elif cfg["check_type"] == "redirect_url" and cfg["banned_indicator"] in final_url:
return "BANNED"
elif cfg["check_type"] == "response_body" and cfg["banned_indicator"] in body:
return "BANNED"
return "OK"
except Exception as e:
return f"ERROR: {str(e)[:80]}"
Expected output: functions that return "OK", "BANNED", or "ERROR: ..." for each account.
If it breaks: if cookie auth does not work because the platform uses token refresh, you will need to store full session state. Playwright with persistent context handles this better than raw httpx.
step 4: wire up Telegram alerts
Create a Telegram bot via @BotFather, get your token, and find your chat ID by messaging the bot and hitting https://api.[telegram](https://telegram.org/).org/bot<TOKEN>/getUpdates.
Create ~/banwatch/alerter.py:
import httpx
TELEGRAM_TOKEN = "YOUR_BOT_TOKEN"
TELEGRAM_CHAT_ID = "YOUR_CHAT_ID"
def send_alert(message: str):
url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage"
httpx.post(url, json={
"chat_id": TELEGRAM_CHAT_ID,
"text": message,
"parse_mode": "Markdown"
}, timeout=10)
Then in your main runner, call:
if status == "BANNED":
send_alert(f"*BAN DETECTED*\nAccount: {account['id']}\nPlatform: {account['platform']}\nTime: {datetime.utcnow().isoformat()} UTC")
Expected output: a Telegram message within seconds of a ban being detected.
If it breaks: if you hit Telegram rate limits (30 messages per second per bot), batch alerts into a single message grouping all banned accounts found in one check cycle.
step 5: schedule the checks with cron
Run checks every 15 minutes for high-risk accounts, every hour for stable ones. create ~/banwatch/run_checks.py as your main entry point, then schedule it:
crontab -e
Add:
*/15 * * * * /root/monitor-env/bin/python /root/banwatch/run_checks.py --tier high >> /root/banwatch/logs/cron.log 2>&1
0 * * * * /root/monitor-env/bin/python /root/banwatch/run_checks.py --tier standard >> /root/banwatch/logs/cron.log 2>&1
Expected output: cron runs silently, logs accumulate in cron.log.
If it breaks: if cron does not find your Python binary, use the full path from which python3 inside your venv.
step 6: add wave detection logic
A single ban could be a mistake. three bans in 90 minutes across the same platform is a wave. add this to your runner:
def detect_wave(platform: str, window_minutes: int = 90, threshold: int = 3) -> bool:
conn = sqlite3.connect(DB_PATH)
cutoff = (datetime.utcnow() - timedelta(minutes=window_minutes)).isoformat()
count = conn.execute(
"SELECT COUNT(*) FROM checks WHERE platform=? AND status='BANNED' AND checked_at > ?",
(platform, cutoff)
).fetchone()[0]
conn.close()
return count >= threshold
If detect_wave() returns True, fire a higher-priority alert with a different message, and optionally trigger a pause script that stops any active operations on that platform.
Expected output: wave alerts fire separately from single-account alerts, giving you clear signal on severity.
If it breaks: if your check frequency is low and you miss the wave window, tighten the cron schedule or lower the threshold.
step 7: build a simple read-only dashboard
For quick status checks, a static HTML page refreshed every 5 minutes is enough. generate it with a script:
import sqlite3
from datetime import datetime, timedelta
def generate_dashboard():
conn = sqlite3.connect(DB_PATH)
rows = conn.execute("""
SELECT account_id, platform, status, checked_at
FROM checks
WHERE checked_at > ?
ORDER BY checked_at DESC
LIMIT 200
""", ((datetime.utcnow() - timedelta(hours=24)).isoformat(),)).fetchall()
conn.close()
html = "<html><body><h1>Banwatch Status</h1><table border=1>"
for r in rows:
color = "red" if r[2] == "BANNED" else ("orange" if "ERROR" in r[2] else "green")
html += f"<tr style='color:{color}'><td>{r[0]}</td><td>{r[1]}</td><td>{r[2]}</td><td>{r[3]}</td></tr>"
html += "</table></body></html>"
with open("/var/www/html/banwatch.html", "w") as f:
f.write(html)
Serve it with nginx on your VPS, password-protected with HTTP basic auth. do not expose account IDs publicly.
Expected output: a color-coded HTML table at your VPS IP, updated every 5 minutes via cron.
If it breaks: if nginx is not installed, sudo apt install nginx and drop the file in /var/www/html/.
step 8: test the entire pipeline with a dummy banned account
Before relying on this in production, add a fake account entry to your config that points to a URL you control and that always returns your banned indicator string. run the checker manually and confirm:
- the database logs a BANNED result
- the Telegram alert fires
- the dashboard shows red
- if you add three such entries, wave detection fires
This is your smoke test. run it after any code change.
common pitfalls
Checking accounts from your VPS IP. the VPS IP that runs your monitor is not the same IP associated with your accounts. if you check Amazon Seller Central from a random Frankfurt datacenter IP without cookies, you will get garbage results or flag the account. always route health checks through the same proxy type the account normally uses. Cloudflare’s bot management docs explain exactly why datacenter IP headers get flagged.
Storing cookies in plaintext. cookies are session credentials. encrypt your accounts.yaml or use environment variables. a compromised VPS with plaintext cookies means account takeover.
Alert fatigue from ERROR statuses. network timeouts are not bans. if you alert on every ERROR, you will start ignoring your Telegram notifications. route errors to a separate lower-priority channel and only page yourself on confirmed BANNED or wave detection.
Missing the pre-ban signals. platforms often warn before banning. reduced listing visibility, payment holds, policy warnings. build secondary checks for these softer signals and log them. the eBay seller performance policy documents exactly what restricted status looks like before a full ban.
Single point of failure in your monitor. if your one VPS goes down during a ban wave, you go blind. consider running a minimal secondary checker on a different provider (a $4/month DigitalOcean droplet) that only fires Telegram alerts, with no dashboard or logging overhead.
scaling this
10 accounts: the setup above runs fine. cron, SQLite, one VPS, httpx. manual cookie updates monthly.
100 accounts: SQLite starts to show write contention if your checks are parallel. migrate to PostgreSQL or use SQLite WAL mode (PRAGMA journal_mode=WAL). add a job queue (Redis + RQ or just a simple task list file) so checks run concurrently without hammering your proxy pool. cookie management becomes a real job, look at session management tools or write a refresh script. at this scale, Grafana Cloud free tier handles your metrics volume without cost. for proxy management at this volume, the guides at proxyscraping.org/blog/ cover residential pool selection in detail.
1000 accounts: you are now running a small operations platform. the health checker needs to be a proper service, not a cron script. look at Celery with Redis as broker, Prometheus for metrics, Grafana for dashboards. check frequency needs to be intelligent: accounts that had issues recently get checked more often. you need a secrets manager (HashiCorp Vault or AWS Secrets Manager) for cookies and credentials. ban wave detection should feed into an automated response system that pauses operations, rotates proxies, and pages an on-call human. the monitoring architecture at this scale resembles what site reliability engineers document in the Google SRE book, specifically the signals section on symptoms versus causes.
account segmentation also matters: group accounts by risk profile (new vs. aged, high-volume vs. low-volume) and apply different check frequencies and alert thresholds per group.
where to go next
- Setting up residential proxies for account operations covers proxy pool management, rotation strategies, and what to do when a proxy provider gets flagged
- Account fingerprinting and browser profile hygiene explains what platforms actually log and how to keep profiles clean between sessions
- For the airdrop and web3 side of multi-account operations, airdropfarming.org/blog/ covers wallet monitoring and Sybil detection patterns that follow similar wave dynamics to platform bans
Written by Xavier Fok
disclosure: this article may contain affiliate links. if you buy through them we may earn a commission at no extra cost to you. verdicts are independent of payouts. last reviewed by Xavier Fok on 2026-05-19.