Skip to main content
RapidDev - Software Development Agency
API AutomationsTwitchBearer Token

How to Automate Twitch Chat Moderation using the API

Automate Twitch chat moderation using POST /moderation/bans for timeouts and bans, DELETE /moderation/chat to delete messages, and the channel.chat.message EventSub subscription to read chat in real time. IRC chat commands (/ban, /timeout) were deprecated February 18, 2023 and tmi.js v1 is unmaintained — use Helix REST endpoints with a User Access Token carrying moderator:manage:banned_users and moderator:manage:chat_messages scopes.

Need help automating? Talk to an expert
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced7 min read1-2 hoursTwitchMay 2026RapidDev Engineering Team
TL;DR

Automate Twitch chat moderation using POST /moderation/bans for timeouts and bans, DELETE /moderation/chat to delete messages, and the channel.chat.message EventSub subscription to read chat in real time. IRC chat commands (/ban, /timeout) were deprecated February 18, 2023 and tmi.js v1 is unmaintained — use Helix REST endpoints with a User Access Token carrying moderator:manage:banned_users and moderator:manage:chat_messages scopes.

API Quick Reference

Auth

User Access Token (Authorization Code)

Rate limit

800 points/minute Helix bucket

Format

JSON

SDK

Available

Understanding the Twitch Moderation API

All Twitch chat moderation now goes through Helix REST endpoints — the legacy IRC chat commands (/ban, /timeout, /clear, /uniquechat) were deprecated February 18, 2023 and no longer function over IRC. PubSub was decommissioned April 14, 2025. The only supported path for a 2026-era moderation bot is: subscribe to channel.chat.message via EventSub to receive messages in real time, analyze content against your rules, then call POST /moderation/bans for bans/timeouts or DELETE /moderation/chat for message deletion.

tmi.js v1, the historically popular Node.js IRC client, has not had a new release since July 2022 and the homepage explicitly warns that IRC chat commands are broken. Do not use it for new projects. The recommended libraries are @twurple/api v8.1.4 (Node.js) and twitchAPI v4.5.0 (Python), both of which have native EventSub and Helix REST support.

All moderation endpoints require a User Access Token — not an App Access Token — obtained via the Authorization Code flow from a broadcaster or a user the broadcaster has granted moderator status. The token must carry moderator:manage:banned_users for bans and timeouts and moderator:manage:chat_messages for message deletion. The moderator user ID and broadcaster user ID must both be passed as query parameters on every moderation call. Full reference: https://dev.twitch.tv/docs/api/reference/#ban-user

Base URLhttps://api.twitch.tv/helix

Setting Up Twitch Moderation API Authentication

Moderation endpoints require a User Access Token from an account that is either the broadcaster or an active moderator in the target channel. The Authorization Code flow involves the moderator visiting an OAuth URL and authorizing your app. Refresh tokens for Confidential clients do not expire — store them securely and use them to refresh user tokens before the ~60-day expiry. Validate the token hourly via GET https://id.twitch.tv/oauth2/validate.

  1. 1Register an app at dev.twitch.tv/console/apps with Client Type: Confidential. Set OAuth Redirect URL to your server's callback endpoint (e.g., https://yourdomain.com/auth/callback)
  2. 2Build the authorization URL: https://id.twitch.tv/oauth2/authorize?response_type=code&client_id=<CLIENT_ID>&redirect_uri=<REDIRECT_URI>&scope=moderator:manage:banned_users+moderator:manage:chat_messages+user:read:chat
  3. 3Have the moderator visit this URL and authorize — Twitch redirects to your callback with a ?code= parameter
  4. 4Exchange the code for tokens: POST to https://id.twitch.tv/oauth2/token with grant_type=authorization_code, client_id, client_secret, code, redirect_uri
  5. 5Store the access_token and refresh_token securely in a database (never in code or .env files for user tokens)
  6. 6Refresh the token: POST to https://id.twitch.tv/oauth2/token with grant_type=refresh_token and the stored refresh_token
  7. 7Validate hourly: GET https://id.twitch.tv/oauth2/validate — returns 401 if expired or revoked, triggering a refresh
auth.py
1import os
2import requests
3
4CLIENT_ID = os.environ['TWITCH_CLIENT_ID']
5CLIENT_SECRET = os.environ['TWITCH_CLIENT_SECRET']
6
7def refresh_user_token(refresh_token: str) -> dict:
8 """Exchange refresh token for a new access token."""
9 resp = requests.post(
10 'https://id.twitch.tv/oauth2/token',
11 data={
12 'grant_type': 'refresh_token',
13 'refresh_token': refresh_token,
14 'client_id': CLIENT_ID,
15 'client_secret': CLIENT_SECRET
16 }
17 )
18 resp.raise_for_status()
19 return resp.json() # Contains access_token and refresh_token
20
21def validate_token(access_token: str) -> bool:
22 resp = requests.get(
23 'https://id.twitch.tv/oauth2/validate',
24 headers={'Authorization': f'OAuth {access_token}'}
25 )
26 return resp.status_code == 200
27
28# Load tokens from your secure storage (database, secrets manager)
29ACCESS_TOKEN = os.environ['MOD_ACCESS_TOKEN']
30REFRESH_TOKEN = os.environ['MOD_REFRESH_TOKEN']
31
32if not validate_token(ACCESS_TOKEN):
33 tokens = refresh_user_token(REFRESH_TOKEN)
34 ACCESS_TOKEN = tokens['access_token']
35 # Store the new tokens back to your database

Security notes

  • Store User Access Tokens in a database with encryption, not in environment variables — they are user-specific and scoped to a person's account
  • Store CLIENT_ID and CLIENT_SECRET in environment variables — never in code
  • Validate the token at startup and every hour; refresh immediately on 401 responses
  • Use a Confidential client type for server-side apps — Public clients cannot keep secrets and their refresh tokens expire after 30 days of inactivity
  • If the moderator's account is unbanned or removed as a mod, all existing tokens for that account will fail with 403 on moderation endpoints — monitor for this
  • Never log full access tokens — log only user ID and token expiry time for debugging

Key endpoints

POST/moderation/bans

Bans or times out a user in the broadcaster's channel. Pass duration (in seconds) for a timeout; omit duration for a permanent ban. Requires moderator:manage:banned_users scope.

ParameterTypeRequiredDescription
broadcaster_idstringrequiredQuery parameter. The broadcaster's user ID (the channel being moderated).
moderator_idstringrequiredQuery parameter. The user ID of the moderator calling the endpoint. Must match the authenticated user's token.
data.user_idstringrequiredThe Twitch user ID of the user to ban or timeout.
data.durationnumberoptionalTimeout duration in seconds (1 to 1,209,600 = 14 days). Omit entirely for a permanent ban.
data.reasonstringoptionalHuman-readable reason for the moderation action. Shown in mod logs.

Request

json
1{"data": {"user_id": "98765432", "duration": 600, "reason": "Spam detected by automod"}}

Response

json
1{"data": [{"broadcaster_id": "12345678", "moderator_id": "11111111", "user_id": "98765432", "created_at": "2026-05-22T10:00:00Z", "end_time": "2026-05-22T10:10:00Z"}]}
DELETE/moderation/chat

Deletes a specific chat message or all messages in the chat. Requires moderator:manage:chat_messages scope.

ParameterTypeRequiredDescription
broadcaster_idstringrequiredQuery parameter. The broadcaster's user ID.
moderator_idstringrequiredQuery parameter. The moderator's user ID — must match the authenticated user.
message_idstringoptionalThe ID of the specific message to delete. Omit to clear all chat messages (equivalent to /clear).

Response

json
1HTTP 204 No Content
POST/eventsub/subscriptions

Creates a channel.chat.message EventSub subscription to receive all chat messages in real time. Use User Access Token with user:read:chat scope (plus user:bot and channel:bot or moderator status for App Access Token usage).

ParameterTypeRequiredDescription
typestringrequiredUse 'channel.chat.message' version '1' to receive all chat messages.
condition.broadcaster_user_idstringrequiredThe channel (broadcaster) to monitor chat for.
condition.user_idstringrequiredThe user ID of the account connecting (the bot/moderator account).

Request

json
1{"type": "channel.chat.message", "version": "1", "condition": {"broadcaster_user_id": "12345678", "user_id": "11111111"}, "transport": {"method": "websocket", "session_id": "<session_id_from_welcome_message>"}}

Response

json
1{"data": [{"id": "abc123", "status": "enabled", "type": "channel.chat.message", "version": "1"}]}
GET/moderation/banned

Checks if a specific user is banned in the channel. Useful for verifying a ban was applied or checking before taking action.

ParameterTypeRequiredDescription
broadcaster_idstringrequiredThe broadcaster's user ID.
user_idstringoptionalFilter results to a specific user ID.

Response

json
1{"data": [{"user_id": "98765432", "user_login": "spammer", "expires_at": "2026-05-22T10:10:00Z"}]}

Step-by-step automation

1

Subscribe to channel.chat.message via EventSub WebSocket

Why: EventSub WebSocket is the correct modern approach for reading chat — PubSub and IRC (/ban, /timeout) are both dead for moderation purposes.

Connect to wss://eventsub.wss.twitch.tv/ws and wait for the welcome message containing a session_id. Use that session_id to create a channel.chat.message subscription via POST /eventsub/subscriptions with your User Access Token. The subscription condition requires both broadcaster_user_id (the channel) and user_id (your bot/moderator user ID).

request.sh
1# First get the session_id from WebSocket connection (automated in Python/JS)
2# Then create the subscription:
3curl -X POST 'https://api.twitch.tv/helix/eventsub/subscriptions' \
4 -H 'Authorization: Bearer <user_access_token>' \
5 -H 'Client-Id: <client_id>' \
6 -H 'Content-Type: application/json' \
7 --data '{
8 "type": "channel.chat.message",
9 "version": "1",
10 "condition": {
11 "broadcaster_user_id": "12345678",
12 "user_id": "11111111"
13 },
14 "transport": {
15 "method": "websocket",
16 "session_id": "<session_id_from_welcome>"
17 }
18 }'

Pro tip: The WebSocket connection has a 10-second keepalive_timeout_seconds — Twitch sends a session_keepalive ping regularly. If you do not receive any message (including keepalives) within that window, the server considers the connection dead and drops it. Implement reconnection logic.

Expected result: Your app is subscribed to channel.chat.message and receiving live chat events via WebSocket. Each notification contains the message text, sender user_id and user_name, and the message_id needed for deletion.

2

Analyze Messages Against Moderation Rules

Why: Your bot needs to evaluate each message before taking action — combining keyword blocking, spam detection (repeated chars/emotes), and rate-of-posting checks prevents both false positives and missed violations.

For each channel.chat.message event, extract the message text and the sender's user_id. Run it through your moderation rules: banned phrase list, regex patterns for spam (e.g., 5+ consecutive identical characters), posting rate (more than 3 messages in 5 seconds from one user). Based on severity, dispatch to delete_message, timeout_user, or ban_user functions. Use a dictionary (Python) or Map (JavaScript) to track per-user message timestamps for rate-based detection.

request.sh
1# Messages are received via WebSocket no cURL equivalent
2# This shows the structure of a channel.chat.message event payload:
3# event.broadcaster_user_id = '12345678'
4# event.chatter_user_id = '98765432'
5# event.message.text = 'buy followers cheap!!!'
6# event.message_id = 'abc123'
7# Use these fields to trigger moderation actions (see next step)

Pro tip: Never make moderation API calls synchronously inside your message analysis loop — use an async queue. A bot that takes 200ms per API call will fall behind during high-traffic spam raids.

Expected result: Each incoming chat message is evaluated against your rule set. Violations return a structured object indicating the action type, reason string, and duration — which is passed to the moderation API calls in the next step.

3

Execute Moderation Actions via Helix REST

Why: IRC /ban and /timeout no longer work — all banning, timing out, and message deletion must go through Helix REST endpoints with the moderator's User Access Token.

Three separate Helix endpoints handle the moderation actions: POST /moderation/bans for bans and timeouts (pass duration for timeout, omit for permanent ban), DELETE /moderation/chat?message_id=<id> for single message deletion (omit message_id to clear all chat), and DELETE /moderation/bans to unban. Both broadcaster_id and moderator_id are required query parameters on every call.

request.sh
1# Timeout user for 60 seconds
2curl -X POST 'https://api.twitch.tv/helix/moderation/bans?broadcaster_id=12345678&moderator_id=11111111' \
3 -H 'Authorization: Bearer <user_access_token>' \
4 -H 'Client-Id: <client_id>' \
5 -H 'Content-Type: application/json' \
6 --data '{"data": {"user_id": "98765432", "duration": 60, "reason": "Spam"}}'
7
8# Delete a specific chat message
9curl -X DELETE 'https://api.twitch.tv/helix/moderation/chat?broadcaster_id=12345678&moderator_id=11111111&message_id=abc123' \
10 -H 'Authorization: Bearer <user_access_token>' \
11 -H 'Client-Id: <client_id>'
12
13# Permanent ban
14curl -X POST 'https://api.twitch.tv/helix/moderation/bans?broadcaster_id=12345678&moderator_id=11111111' \
15 -H 'Authorization: Bearer <user_access_token>' \
16 -H 'Client-Id: <client_id>' \
17 -H 'Content-Type: application/json' \
18 --data '{"data": {"user_id": "98765432", "reason": "Banned phrase violation"}}'

Pro tip: Implement a circuit breaker that pauses moderation at 400 actions/minute — half the 800/min cap. This prevents the bot from getting rate limited mid-raid when it matters most.

Expected result: Violating users are timed out or banned, and offending messages are deleted from chat within milliseconds of being analyzed. A circuit breaker prevents the moderation bot from exhausting the 800 points/minute Helix rate limit during spam raids.

Complete working code

This complete moderation bot connects to Twitch EventSub WebSocket to receive channel.chat.message events, evaluates messages against banned phrases and spam patterns, and calls Helix REST to timeout users or delete messages. Includes circuit breaker logic for raid protection and token validation.

automate_chat_moderation.py
1import asyncio
2import json
3import os
4import re
5import time
6import requests
7import websockets
8import logging
9from collections import defaultdict, deque
10
11logging.basicConfig(level=logging.INFO)
12logger = logging.getLogger(__name__)
13
14CLIENT_ID = os.environ['TWITCH_CLIENT_ID']
15ACCESS_TOKEN = os.environ['MOD_ACCESS_TOKEN']
16BROADCASTER_ID = os.environ['BROADCASTER_ID']
17MOD_USER_ID = os.environ['MOD_USER_ID']
18
19# Moderation rules
20BANNED_PHRASES = ['buy followers', 'free nitro']
21SPAM_RE = re.compile(r'(.)\1{4,}')
22user_msg_times = defaultdict(deque)
23mod_actions = deque()
24
25def hdrs():
26 return {'Authorization': f'Bearer {ACCESS_TOKEN}', 'Client-Id': CLIENT_ID,
27 'Content-Type': 'application/json'}
28
29def circuit_ok():
30 now = time.time()
31 while mod_actions and mod_actions[0] < now - 60:
32 mod_actions.popleft()
33 return len(mod_actions) < 400
34
35def get_violation(text, user_id):
36 t = text.lower()
37 for p in BANNED_PHRASES:
38 if p in t:
39 return ('ban', f'Banned phrase: {p}', None)
40 if SPAM_RE.search(text):
41 return ('timeout', 'Character spam', 60)
42 now = time.time()
43 times = user_msg_times[user_id]
44 while times and times[0] < now - 5:
45 times.popleft()
46 times.append(now)
47 if len(times) > 3:
48 return ('timeout', 'Message flood', 30)
49 return None
50
51def take_action(action, user_id, msg_id, reason, duration=None):
52 if not circuit_ok():
53 logger.warning('Circuit breaker: pausing moderation')
54 time.sleep(5)
55 mod_actions.append(time.time())
56 try:
57 if action in ('ban', 'timeout'):
58 body = {'data': {'user_id': user_id, 'reason': reason}}
59 if action == 'timeout':
60 body['data']['duration'] = duration
61 resp = requests.post(
62 f'https://api.twitch.tv/helix/moderation/bans'
63 f'?broadcaster_id={BROADCASTER_ID}&moderator_id={MOD_USER_ID}',
64 headers=hdrs(), json=body
65 )
66 resp.raise_for_status()
67 if msg_id:
68 requests.delete(
69 f'https://api.twitch.tv/helix/moderation/chat'
70 f'?broadcaster_id={BROADCASTER_ID}&moderator_id={MOD_USER_ID}&message_id={msg_id}',
71 headers={'Authorization': f'Bearer {ACCESS_TOKEN}', 'Client-Id': CLIENT_ID}
72 )
73 logger.info(f'{action} {user_id}: {reason}')
74 except Exception as e:
75 logger.error(f'Moderation action failed: {e}')
76
77async def run_bot():
78 while True:
79 try:
80 async with websockets.connect('wss://eventsub.wss.twitch.tv/ws') as ws:
81 welcome = json.loads(await ws.recv())
82 session_id = welcome['payload']['session']['id']
83 requests.post(
84 'https://api.twitch.tv/helix/eventsub/subscriptions',
85 headers=hdrs(),
86 json={'type': 'channel.chat.message', 'version': '1',
87 'condition': {'broadcaster_user_id': BROADCASTER_ID, 'user_id': MOD_USER_ID},
88 'transport': {'method': 'websocket', 'session_id': session_id}}
89 ).raise_for_status()
90 logger.info('Moderation bot connected')
91 async for raw in ws:
92 ev = json.loads(raw)
93 if ev.get('metadata', {}).get('message_type') != 'notification':
94 continue
95 e = ev['payload']['event']
96 violation = get_violation(e['message']['text'], e['chatter_user_id'])
97 if violation:
98 action, reason, duration = violation
99 take_action(action, e['chatter_user_id'], e['message_id'], reason, duration)
100 except Exception as e:
101 logger.error(f'Connection error: {e}. Reconnecting in 5s...')
102 await asyncio.sleep(5)
103
104asyncio.run(run_bot())

Error handling

403{"status": 403, "message": "The user in the moderator_id parameter is not a moderator"}
Cause

The user whose token is being used is not an active moderator in the broadcaster's channel, or their moderator status was removed after the token was issued.

Fix

Have the broadcaster run /mod <your_bot_username> in their channel. Verify moderator status via GET /moderation/moderators?broadcaster_id=<id>&user_id=<mod_id>.

Retry strategy

Not retryable until the user is appointed as a moderator. Monitor for this error and alert your ops team.

401{"status": 401, "message": "Invalid OAuth token"}
Cause

The User Access Token has expired (~60 days) or was revoked by the user. Unlike App Access Tokens, User Access Tokens can be revoked at any time by the user.

Fix

Use the stored refresh token to request a new access token via POST https://id.twitch.tv/oauth2/token with grant_type=refresh_token. Validate tokens hourly.

Retry strategy

Refresh the token immediately, then retry the original request.

400{"status": 400, "message": "The message specified does not exist"}
Cause

Attempting to delete a message that was already deleted (by another mod, the broadcaster, or Twitch's AutoMod) or that is older than the deletion window.

Fix

Treat 400 on DELETE /moderation/chat as a success — the message is gone regardless of who deleted it. Suppress 400 errors for message deletion specifically.

Retry strategy

Not retryable. Ignore the 400 and continue processing the next message.

429{"status": 429, "error": "Too Many Requests"}
Cause

The 800 points/minute Helix bucket is exhausted. During heavy spam raids, rapid successive moderation calls can drain the bucket within seconds.

Fix

Implement a circuit breaker that pauses at 400 actions/minute. Use the Ratelimit-Reset header to determine when the bucket refills. Prioritize bans/timeouts over message deletions when the bucket is low.

Retry strategy

Wait until the Unix timestamp in the Ratelimit-Reset header, then retry. For extreme raids, pause all moderation for 10-15 seconds to let the bucket recover.

422{"status": 422, "message": "The user in the user_id parameter is the broadcaster"}
Cause

Attempting to timeout or ban the broadcaster in their own channel — not allowed.

Fix

Add a check before calling moderation endpoints: if the target user_id matches the broadcaster_user_id, skip the moderation action.

Retry strategy

Not retryable. Skip and log.

Rate Limits for Twitch Moderation API

ScopeLimitWindow
Helix REST (per user access token)800 pointsper minute
EventSub WebSocket per client_id+user_idmax_total_cost 10across all WebSocket connections (3 max, 300 subs/connection)
retry-handler.ts
1import time
2
3def mod_request_with_retry(fn, max_retries=3):
4 for attempt in range(max_retries):
5 resp = fn()
6 if resp.status_code == 429:
7 reset_ts = int(resp.headers.get('Ratelimit-Reset', time.time() + 60))
8 wait = max(reset_ts - int(time.time()), 1)
9 print(f'Rate limited. Waiting {wait}s (attempt {attempt+1})')
10 time.sleep(wait)
11 continue
12 if resp.status_code in (400, 422):
13 return resp # expected non-retryable errors
14 resp.raise_for_status()
15 return resp
16 raise Exception('Max retries exceeded')
  • Implement a circuit breaker at 400 moderation actions/minute — this leaves 400 points of headroom so the bot never fully exhausts the 800/min bucket during raids
  • Prioritize ban and timeout calls over message deletion — clearing a user from chat is more effective than deleting individual messages during a spam raid
  • Use an async queue for moderation actions rather than calling Helix synchronously in the message handler — this prevents the WebSocket message loop from blocking
  • Monitor the Ratelimit-Remaining header on every response and log a warning when it drops below 200

Security checklist

  • Store User Access Tokens in an encrypted database, not environment variables — they are tied to a specific user's account and must be refreshed on rotation
  • Store CLIENT_ID and CLIENT_SECRET in environment variables only — never in code or database
  • Validate the User Access Token hourly via GET https://id.twitch.tv/oauth2/validate; refresh immediately on 401 responses
  • Use a dedicated moderator bot account, not the broadcaster's personal account — if the bot token is compromised, the broadcaster's account is not at risk
  • Guard against banning the broadcaster (user_id === broadcaster_user_id) and your own bot account — both will return 422 errors and should be filtered before calling moderation endpoints
  • Log all moderation actions (action type, user_id, reason, timestamp) to a database for accountability and appeal handling
  • Implement an allowlist of trusted moderator user IDs that the bot never targets with automated moderation actions

Automation use cases

AI-Powered Context-Aware Moderation

advanced

Replace simple keyword matching with an AI model call (e.g., Anthropic or OpenAI API) to evaluate whether a message violates community guidelines in context, reducing false positives for borderline cases.

Raid Protection Mode

advanced

Detect a raid (sudden influx of new viewers all posting similar messages) and automatically activate subscriber-only mode via PATCH /channels until the raid subsides.

Multi-Channel Moderation Dashboard

advanced

Subscribe to channel.chat.message for multiple broadcaster channels simultaneously and route all violations to a single mod dashboard with per-channel action controls.

No-code alternatives

Don't want to write code? These platforms can automate the same workflows visually.

Zapier

Free tier (100 tasks/month); Starter from $19.99/month

Zapier's Twitch integration supports basic event triggers but does not have native real-time chat monitoring or Helix moderation endpoint support as of 2026 — better suited for post-stream notifications than live moderation.

Pros
  • + Simple to configure for basic stream event automation
  • + No server required
  • + 1,000+ destination apps for alert routing
Cons
  • - No real-time chat message monitoring capability
  • - Cannot directly call POST /moderation/bans or DELETE /moderation/chat
  • - Not suitable for latency-sensitive moderation (Zapier polling interval: minutes, not seconds)

Make

Free tier (1,000 ops/month); Core from $9/month

Make can receive Twitch EventSub webhook events and call HTTP endpoints including Helix moderation APIs, but building real-time chat moderation requires a custom HTTP module setup.

Pros
  • + Can call arbitrary Helix REST endpoints via HTTP module
  • + Webhook trigger can receive EventSub notifications
  • + 1,000 free operations/month
Cons
  • - No native channel.chat.message support — requires custom webhook handling
  • - Message-by-message processing at Make's latency is not suitable for real-time moderation
  • - Complex scenarios with HTTP calls can be slow relative to a direct bot

n8n

Self-hosted free; Cloud Starter from €20/month

n8n can receive EventSub webhook notifications via its webhook node and execute HTTP requests to Helix moderation endpoints, but real-time chat moderation at scale requires a purpose-built bot, not a workflow orchestrator.

Pros
  • + Self-hosted with no per-execution cost
  • + HTTP node can call all Helix moderation endpoints
  • + Webhook trigger can handle EventSub chat message notifications
Cons
  • - n8n is not designed for the latency requirements of real-time chat moderation (sub-500ms response)
  • - WebSocket EventSub transport is not natively supported — webhook transport requires a public URL
  • - Self-hosted n8n requires server maintenance

Best practices

  • Use EventSub WebSocket (channel.chat.message) to read chat — never IRC chat commands, which were deprecated February 18, 2023, or tmi.js v1, which is unmaintained
  • Implement a circuit breaker that pauses moderation at 400 actions/minute to leave headroom below the 800/min Helix rate limit during spam raids
  • Run moderation actions in an async queue, not synchronously in the message loop — this prevents the bot from falling behind during high-traffic periods
  • Validate User Access Tokens hourly and refresh before the ~60-day expiry — users can revoke at any time, and a revoked token returns 401 on every moderation call
  • Filter your own bot account and the broadcaster user ID before any moderation call — attempting to act on them returns 422 errors
  • Log all automated moderation actions with user_id, reason, and timestamp to an audit database for transparency and appeal handling

Ask AI to help

Copy one of these prompts to get a personalized, working implementation.

ChatGPT / Claude Prompt

I'm building a Twitch chat moderation bot using EventSub WebSocket for channel.chat.message events and Helix REST for bans and message deletion. I am NOT using tmi.js or IRC commands (those are deprecated). Issue: [describe your problem — e.g., '403 on POST /moderation/bans', 'messages not arriving via WebSocket', 'circuit breaker triggering too early']. My code: [paste relevant section]. Helix response: [paste response body and status code]. What is wrong with my moderation setup?

Lovable / V0 Prompt

Build a Twitch chat moderation dashboard UI. Show a real-time chat feed with messages from the monitored channel. Each message shows username, message text, and action buttons: Delete (red), Timeout 60s (orange), Timeout 600s (yellow), and Ban (dark red). Add a filter input to search the chat feed by keyword. Show a Moderation Log panel on the right with timestamped entries of all bot and manual actions. Connect to a REST API at /api/chat/messages (SSE stream) and /api/moderation/action (POST endpoint). Use Tailwind and React.

Frequently asked questions

Can I still use /ban and /timeout commands via IRC or tmi.js?

No. IRC chat moderation commands (/ban, /timeout, /clear, etc.) were deprecated February 18, 2023 and no longer function. tmi.js v1 has not had a new npm release since July 2022 and explicitly warns about this on its homepage. Use POST /moderation/bans (for bans/timeouts) and DELETE /moderation/chat (for message deletion) via Helix REST with a User Access Token carrying the correct scopes.

Does the bot need to be an actual moderator in the channel?

Yes. The user whose token is used in moderator_id must be an active moderator in the broadcaster's channel. The broadcaster appoints mods by running /mod <username> in their channel. If the mod status is removed while the bot is running, all moderation calls return 403. Monitor for 403 errors and alert your team when this happens.

What scopes does the User Access Token need?

At minimum: moderator:manage:banned_users (for POST /moderation/bans) and moderator:manage:chat_messages (for DELETE /moderation/chat). For reading chat via EventSub WebSocket, add user:read:chat. You need user:write:chat to send messages. Always request the minimal set of scopes needed.

How do I handle spam raids without exhausting the rate limit?

Implement a circuit breaker that pauses at 400 moderation actions per minute — half the 800/min Helix bucket. This leaves headroom so the bot stays operational throughout the raid. Also prioritize banning/timing out repeat offenders over deleting individual messages — removing a user from chat is more effective than deleting each of their messages.

Can I use an App Access Token instead of a User Access Token for moderation?

No. POST /moderation/bans and DELETE /moderation/chat require a User Access Token from an account with active moderator status in the channel. App Access Tokens (from Client Credentials flow) are not user-specific and cannot perform moderation actions on behalf of a moderator.

Is the Twitch API free to use?

Yes, the Twitch Helix API and EventSub are free with no per-request charges. You need a free Twitch developer account and a registered application at dev.twitch.tv/console/apps.

Can RapidDev help build a custom Twitch moderation bot?

Yes. RapidDev has built 600+ apps including Twitch moderation systems, multi-channel dashboards, and real-time stream bots. Book a free consultation at rapidevelopers.com.

RapidDev

Need this automated?

Our team has built 600+ apps with API automations. We can build this for you.

Book a free consultation

Skip the coding — we'll build it for you

Our experts have built 600+ API automations. From prototype to production in days, not weeks.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.