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
User Access Token (Authorization Code)
800 points/minute Helix bucket
JSON
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
https://api.twitch.tv/helixSetting 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.
- 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)
- 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
- 3Have the moderator visit this URL and authorize — Twitch redirects to your callback with a ?code= parameter
- 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
- 5Store the access_token and refresh_token securely in a database (never in code or .env files for user tokens)
- 6Refresh the token: POST to https://id.twitch.tv/oauth2/token with grant_type=refresh_token and the stored refresh_token
- 7Validate hourly: GET https://id.twitch.tv/oauth2/validate — returns 401 if expired or revoked, triggering a refresh
1import os2import requests34CLIENT_ID = os.environ['TWITCH_CLIENT_ID']5CLIENT_SECRET = os.environ['TWITCH_CLIENT_SECRET']67def 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_SECRET16 }17 )18 resp.raise_for_status()19 return resp.json() # Contains access_token and refresh_token2021def 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 == 2002728# Load tokens from your secure storage (database, secrets manager)29ACCESS_TOKEN = os.environ['MOD_ACCESS_TOKEN']30REFRESH_TOKEN = os.environ['MOD_REFRESH_TOKEN']3132if 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 databaseSecurity 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
/moderation/bansBans 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
broadcaster_id | string | required | Query parameter. The broadcaster's user ID (the channel being moderated). |
moderator_id | string | required | Query parameter. The user ID of the moderator calling the endpoint. Must match the authenticated user's token. |
data.user_id | string | required | The Twitch user ID of the user to ban or timeout. |
data.duration | number | optional | Timeout duration in seconds (1 to 1,209,600 = 14 days). Omit entirely for a permanent ban. |
data.reason | string | optional | Human-readable reason for the moderation action. Shown in mod logs. |
Request
1{"data": {"user_id": "98765432", "duration": 600, "reason": "Spam detected by automod"}}Response
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"}]}/moderation/chatDeletes a specific chat message or all messages in the chat. Requires moderator:manage:chat_messages scope.
| Parameter | Type | Required | Description |
|---|---|---|---|
broadcaster_id | string | required | Query parameter. The broadcaster's user ID. |
moderator_id | string | required | Query parameter. The moderator's user ID — must match the authenticated user. |
message_id | string | optional | The ID of the specific message to delete. Omit to clear all chat messages (equivalent to /clear). |
Response
1HTTP 204 No Content/eventsub/subscriptionsCreates 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).
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | required | Use 'channel.chat.message' version '1' to receive all chat messages. |
condition.broadcaster_user_id | string | required | The channel (broadcaster) to monitor chat for. |
condition.user_id | string | required | The user ID of the account connecting (the bot/moderator account). |
Request
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
1{"data": [{"id": "abc123", "status": "enabled", "type": "channel.chat.message", "version": "1"}]}/moderation/bannedChecks if a specific user is banned in the channel. Useful for verifying a ban was applied or checking before taking action.
| Parameter | Type | Required | Description |
|---|---|---|---|
broadcaster_id | string | required | The broadcaster's user ID. |
user_id | string | optional | Filter results to a specific user ID. |
Response
1{"data": [{"user_id": "98765432", "user_login": "spammer", "expires_at": "2026-05-22T10:10:00Z"}]}Step-by-step automation
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).
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.
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.
1# Messages are received via WebSocket — no cURL equivalent2# 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.
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.
1# Timeout user for 60 seconds2curl -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"}}'78# Delete a specific chat message9curl -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>'1213# Permanent ban14curl -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.
1import asyncio2import json3import os4import re5import time6import requests7import websockets8import logging9from collections import defaultdict, deque1011logging.basicConfig(level=logging.INFO)12logger = logging.getLogger(__name__)1314CLIENT_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']1819# Moderation rules20BANNED_PHRASES = ['buy followers', 'free nitro']21SPAM_RE = re.compile(r'(.)\1{4,}')22user_msg_times = defaultdict(deque)23mod_actions = deque()2425def hdrs():26 return {'Authorization': f'Bearer {ACCESS_TOKEN}', 'Client-Id': CLIENT_ID,27 'Content-Type': 'application/json'}2829def 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) < 4003435def 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 None5051def 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'] = duration61 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=body65 )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}')7677async 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 continue95 e = ev['payload']['event']96 violation = get_violation(e['message']['text'], e['chatter_user_id'])97 if violation:98 action, reason, duration = violation99 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)103104asyncio.run(run_bot())Error handling
{"status": 403, "message": "The user in the moderator_id parameter is not a moderator"}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.
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>.
Not retryable until the user is appointed as a moderator. Monitor for this error and alert your ops team.
{"status": 401, "message": "Invalid OAuth token"}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.
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.
Refresh the token immediately, then retry the original request.
{"status": 400, "message": "The message specified does not exist"}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.
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.
Not retryable. Ignore the 400 and continue processing the next message.
{"status": 429, "error": "Too Many Requests"}The 800 points/minute Helix bucket is exhausted. During heavy spam raids, rapid successive moderation calls can drain the bucket within seconds.
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.
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.
{"status": 422, "message": "The user in the user_id parameter is the broadcaster"}Attempting to timeout or ban the broadcaster in their own channel — not allowed.
Add a check before calling moderation endpoints: if the target user_id matches the broadcaster_user_id, skip the moderation action.
Not retryable. Skip and log.
Rate Limits for Twitch Moderation API
| Scope | Limit | Window |
|---|---|---|
| Helix REST (per user access token) | 800 points | per minute |
| EventSub WebSocket per client_id+user_id | max_total_cost 10 | across all WebSocket connections (3 max, 300 subs/connection) |
1import time23def 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 continue12 if resp.status_code in (400, 422):13 return resp # expected non-retryable errors14 resp.raise_for_status()15 return resp16 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
advancedReplace 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
advancedDetect 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
advancedSubscribe 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/monthZapier'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.
- + Simple to configure for basic stream event automation
- + No server required
- + 1,000+ destination apps for alert routing
- - 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/monthMake 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.
- + Can call arbitrary Helix REST endpoints via HTTP module
- + Webhook trigger can receive EventSub notifications
- + 1,000 free operations/month
- - 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/monthn8n 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.
- + Self-hosted with no per-execution cost
- + HTTP node can call all Helix moderation endpoints
- + Webhook trigger can handle EventSub chat message notifications
- - 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.
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?
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.
Need this automated?
Our team has built 600+ apps with API automations. We can build this for you.
Book a free consultation