Reddit provides the richest moderation API of any social platform: approve (POST /api/approve), remove (POST /api/remove), ban users (POST /r/{sub}/api/friend?type=banned), and read modmail (GET /api/mod/conversations). Requires OAuth2 with modposts, modmail, and modothers scopes. Mod bots are free forever per Reddit's 2023 guarantee. Rate limit: 60 RPM. Critical: SUBREDDIT_NOTALLOWED (403) fires if the bot lacks moderator permissions.
API Quick Reference
OAuth 2.0
60 requests/minute
JSON
Available
Understanding the Reddit Moderation API
Reddit's moderation API is one of the most comprehensive in social media, giving programmatic access to every action a human moderator can take in the Reddit interface. The key endpoints span approval, removal, spam marking, user banning, flair assignment, and modmail management. All moderation actions require both OAuth2 user-context authentication AND moderator status on the target subreddit.
The automation pattern is: poll the modqueue (GET /r/{sub}/about/modqueue) for items awaiting review, evaluate each against your rules (keyword matching, account age, karma thresholds), then approve or remove. More sophisticated bots also auto-ban users who consistently violate rules and send escalation messages to modmail.
Reddit officially guaranteed in 2023 that mod bots will remain free forever — they're exempt from the commercial API pricing tier. This means your moderation automation costs nothing at 60 RPM, and you don't need an enterprise agreement. The only requirement is that the bot account has moderator permissions on the subreddit.
https://oauth.reddit.comSetting Up Reddit Moderation API Authentication
Moderation requires user-context OAuth2 with specific mod scopes. The script-type app password grant is best for single-subreddit bots. The authenticated Reddit account must have moderator privileges on the target subreddit — the API returns SUBREDDIT_NOTALLOWED (403) if it doesn't. Grant the bot account moderator status in your subreddit's mod tools first.
- 1Grant your bot Reddit account moderator status on the target subreddit (Mod Tools → Moderators → Add Moderator)
- 2Go to reddit.com/prefs/apps and create a new script-type app
- 3Set redirect URI to http://localhost:8080
- 4Note the client_id (below app name) and client_secret
- 5Request token via POST to https://www.reddit.com/api/v1/access_token with Basic auth
- 6Include in body: grant_type=password, username=bot_username, password=bot_password
- 7Required scopes: modposts (approve/remove posts), modmail (read/send modmail), modothers (ban users)
- 8Set User-Agent: <platform>:<app-id>:<version> (by /u/<bot_username>)
1import requests2import os34def get_mod_token():5 CLIENT_ID = os.environ['REDDIT_CLIENT_ID']6 CLIENT_SECRET = os.environ['REDDIT_CLIENT_SECRET']7 BOT_USERNAME = os.environ['REDDIT_MOD_USERNAME']8 BOT_PASSWORD = os.environ['REDDIT_MOD_PASSWORD']9 USER_AGENT = f'python:com.example.modbot:v1.0 (by /u/{BOT_USERNAME})'1011 r = requests.post(12 'https://www.reddit.com/api/v1/access_token',13 auth=requests.auth.HTTPBasicAuth(CLIENT_ID, CLIENT_SECRET),14 data={15 'grant_type': 'password',16 'username': BOT_USERNAME,17 'password': BOT_PASSWORD18 },19 headers={'User-Agent': USER_AGENT}20 )21 r.raise_for_status()22 data = r.json()23 print(f'Authenticated as {BOT_USERNAME} with scopes: {data.get("scope")}')24 return data['access_token'], USER_AGENTSecurity notes
- •Store all credentials in environment variables — never hardcode bot username/password
- •The bot account needs moderator status on the subreddit BEFORE making moderation API calls
- •Use a dedicated mod bot account, not a personal Reddit account, to isolate mod actions from personal activity
- •Access tokens expire after 1 hour — implement automatic refresh in your bot loop
- •Log all moderation actions (approve/remove/ban) with timestamps and reasons for mod team accountability
Key endpoints
/r/{subreddit}/about/modqueueReturns posts and comments awaiting moderator review. This is the primary input for automated moderation — poll this for new items to evaluate.
| Parameter | Type | Required | Description |
|---|---|---|---|
only | string | optional | Filter to 'links' (posts only) or 'comments' (comments only) |
limit | number | optional | Number of items to return, max 100 |
after | string | optional | Pagination cursor (fullname) |
Response
1{"kind": "Listing", "data": {"children": [{"kind": "t3", "data": {"id": "abc123", "name": "t3_abc123", "title": "Spam post", "author": "spammer1", "selftext": "Buy cheap stuff here", "banned_by": null}}]}}/api/approveApproves a post or comment that is in the modqueue or was auto-filtered. Requires modposts scope. Makes the item visible to all users.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | required | Fullname of the post (t3_xxx) or comment (t1_xxx) to approve |
Request
1{"id": "t3_abc123"}Response
1{}/api/removeRemoves a post or comment. Set spam=true to mark it as spam (trains Reddit's spam filter). Requires modposts scope.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | required | Fullname of the post (t3_xxx) or comment (t1_xxx) to remove |
spam | boolean | optional | If true, marks as spam and trains the spam filter |
Request
1{"id": "t3_abc123", "spam": false}Response
1{}/r/{subreddit}/api/friendBans or unbans a user from a subreddit. Use type=banned to ban. Requires modothers scope.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | required | Reddit username to ban |
type | string | required | Relationship type: banned, muted, wikibanned, contributor |
ban_reason | string | optional | Mod-facing reason (max 300 chars), not shown to user |
ban_message | string | optional | Message sent to banned user |
duration | number | optional | Ban duration in days (omit for permanent) |
Request
1{"name": "spammer1", "type": "banned", "ban_reason": "Spam and self-promotion", "ban_message": "Your account has been banned for spam. Appeal via modmail.", "duration": 30}Response
1{}/api/mod/conversationsLists modmail conversations. Use this to check for appeals or escalated issues. Requires modmail scope.
| Parameter | Type | Required | Description |
|---|---|---|---|
state | string | optional | Filter: new, inprogress, archived, all |
limit | number | optional | Number of conversations to return |
entity | string | optional | Subreddit name to filter by |
Response
1{"conversations": {"abc123": {"id": "abc123", "subject": "Appeal: my post was removed", "participant": {"name": "user1"}, "isInternal": false, "lastUpdated": "2026-04-15T10:30:00Z"}}}Step-by-step automation
Authenticate with Moderation Scopes
Why: Without modposts, modmail, and modothers scopes on a moderator account, every moderation API call returns 403 SUBREDDIT_NOTALLOWED.
Authenticate with the password grant using a bot account that has mod privileges on your subreddit. Verify the scopes in the token response match what you expect. Build a token refresh mechanism that runs before every API call after the first 55 minutes.
1curl -X POST https://www.reddit.com/api/v1/access_token \2 -u 'YOUR_CLIENT_ID:YOUR_CLIENT_SECRET' \3 -H 'User-Agent: script:com.example.modbot:v1.0 (by /u/modbot_username)' \4 -d 'grant_type=password&username=modbot_username&password=modbot_password'Pro tip: After getting the token, call GET /api/v1/me to verify the bot account username matches your intended account. A wrong username with moderator status on a different subreddit will cause confusing permission errors.
Expected result: A valid access token with modposts, modmail, modothers scopes active for the bot account.
Poll the Modqueue for Items to Review
Why: The modqueue surfaces posts and comments that AutoModerator flagged or that need manual mod review — it's the primary input for your automated bot.
Call GET /r/{subreddit}/about/modqueue every 30-60 seconds. Process each item against your rules before it's manually reviewed by a human moderator. Track processed fullnames to avoid re-evaluating items. You can also monitor GET /r/{subreddit}/new for posts that slipped through filters.
1curl -X GET 'https://oauth.reddit.com/r/mysubreddit/about/modqueue?limit=100' \2 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \3 -H 'User-Agent: script:com.example.modbot:v1.0 (by /u/modbot_username)'Pro tip: Fetch user account age and karma for each modqueue item author using GET /user/{username}/about. Account age < 30 days and karma < 100 are strong spam signals that can auto-remove without keyword matching.
Expected result: A list of modqueue items (posts and comments) with their fullnames, authors, content, and metadata.
Evaluate Rules and Approve or Remove
Why: Automated rule evaluation removes clear spam and approves trusted posts without human review, letting moderators focus on edge cases.
Build a scoring function that evaluates content against keyword rules, account age/karma thresholds, and domain blocks. Approve items that clearly meet community standards (trusted users, high karma, on-topic). Remove items that clearly violate rules (spam keywords, new accounts, blocked domains). Skip borderline items for human review.
1# Approve a post2curl -X POST https://oauth.reddit.com/api/approve \3 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \4 -H 'User-Agent: script:com.example.modbot:v1.0 (by /u/modbot_username)' \5 -d 'id=t3_abc123'67# Remove a post (non-spam)8curl -X POST https://oauth.reddit.com/api/remove \9 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \10 -H 'User-Agent: script:com.example.modbot:v1.0 (by /u/modbot_username)' \11 -d 'id=t3_abc123&spam=false'1213# Remove as spam14curl -X POST https://oauth.reddit.com/api/remove \15 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \16 -H 'User-Agent: script:com.example.modbot:v1.0 (by /u/modbot_username)' \17 -d 'id=t3_abc123&spam=true'Pro tip: Set mark_as_spam=true only for clear spam (keyword matches, known spam accounts). Regular rule violations should use spam=false to avoid unfairly training Reddit's spam filter against legitimate users who made rule mistakes.
Expected result: Each modqueue item is approved, removed, or skipped based on your rule evaluation, with reasons logged.
Ban Repeat Violators and Send Escalations to Modmail
Why: Some users repeatedly violate rules — automated banning with a message escalates efficiently compared to manual banning after each individual removal.
Track violations per user in a local database. When a user exceeds your threshold (e.g., 3 removals in 7 days), ban them with POST /r/{sub}/api/friend and include a ban_message. For edge cases or complex appeals, create a modmail entry using POST /api/mod/conversations to route to human moderators.
1# Ban a user from the subreddit2curl -X POST https://oauth.reddit.com/r/mysubreddit/api/friend \3 -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \4 -H 'User-Agent: script:com.example.modbot:v1.0 (by /u/modbot_username)' \5 -H 'Content-Type: application/x-www-form-urlencoded' \6 -d 'name=spamuser1&type=banned&ban_reason=Repeated+spam+violations&ban_message=Your+account+has+been+banned+for+repeated+spam.+Appeal+via+modmail.&duration=30'Pro tip: Use temporary bans (30-90 days) for first bans rather than permanent ones. Permanent bans from a bot can feel disproportionate and generate modmail appeals. Escalate to permanent bans only for egregious cases that require human moderator review.
Expected result: Repeat violators are automatically banned with a message explaining the reason and appeal process.
Complete working code
This bot polls the modqueue every 30 seconds, evaluates each item against spam/quality rules, auto-approves trusted users, removes clear violations, tracks repeat offenders for banning, and logs all actions to SQLite for audit review.
1import requests2import sqlite33import time4import os5import logging6from datetime import datetime, timedelta, timezone78logging.basicConfig(level=logging.INFO)9log = logging.getLogger('modbot')1011SUBREDDIT = os.environ.get('REDDIT_SUBREDDIT', 'mysubreddit')12USER_AGENT = f"python:com.example.modbot:v1.0 (by /u/{os.environ['REDDIT_MOD_USERNAME']})"13SPAM_WORDS = ['buy cheap', 'free crypto', 'click here now', 'earn fast']14MIN_KARMA = 2015tk = {'token': None, 'expires': datetime.now(timezone.utc)}16processed = set()1718def headers():19 if datetime.now(timezone.utc) >= tk['expires'] - timedelta(minutes=5):20 r = requests.post(21 'https://www.reddit.com/api/v1/access_token',22 auth=requests.auth.HTTPBasicAuth(os.environ['REDDIT_CLIENT_ID'], os.environ['REDDIT_CLIENT_SECRET']),23 data={'grant_type': 'password', 'username': os.environ['REDDIT_MOD_USERNAME'], 'password': os.environ['REDDIT_MOD_PASSWORD']},24 headers={'User-Agent': USER_AGENT}25 )26 r.raise_for_status()27 d = r.json()28 tk['token'] = d['access_token']29 tk['expires'] = datetime.now(timezone.utc) + timedelta(seconds=d['expires_in'])30 return {'Authorization': f"Bearer {tk['token']}", 'User-Agent': USER_AGENT}3132def init_db(path='modbot.db'):33 db = sqlite3.connect(path)34 db.execute('CREATE TABLE IF NOT EXISTS actions (id TEXT, username TEXT, action TEXT, reason TEXT, ts TEXT)')35 db.execute('CREATE TABLE IF NOT EXISTS violations (username TEXT, reason TEXT, ts TEXT)')36 db.commit()37 return db3839def get_violations(db, username):40 cutoff = (datetime.now(timezone.utc) - timedelta(days=7)).isoformat()41 return db.execute('SELECT COUNT(*) FROM violations WHERE username=? AND ts>?', (username, cutoff)).fetchone()[0]4243def evaluate(data):44 content = ' '.join(filter(None, [data.get('title',''), data.get('selftext',''), data.get('body','')])).lower()45 for w in SPAM_WORDS:46 if w in content:47 return 'remove_spam', f'Keyword: {w}'48 return 'skip', 'Manual review'4950def run_bot(db):51 log.info(f'Mod bot started for r/{SUBREDDIT}')52 while True:53 try:54 r = requests.get(f'https://oauth.reddit.com/r/{SUBREDDIT}/about/modqueue',55 headers=headers(), params={'limit': 100})56 r.raise_for_status()57 items = r.json()['data']['children']58 for item in items:59 d = item['data']60 name = d['name']61 if name in processed:62 continue63 processed.add(name)64 author = d.get('author', '[deleted]')65 if author in ('[deleted]', 'AutoModerator'):66 continue67 action, reason = evaluate(d)68 if action == 'skip':69 continue70 # Apply action71 endpoint = 'approve' if action == 'approve' else 'remove'72 payload = {'id': name}73 if action == 'remove_spam':74 payload['spam'] = 'true'75 requests.post(f'https://oauth.reddit.com/api/{endpoint}', headers=headers(), data=payload)76 log.info(f'{action.upper()} {name} (u/{author}): {reason}')77 # Log action78 db.execute('INSERT INTO actions VALUES (?,?,?,?,?)', (name, author, action, reason, datetime.now(timezone.utc).isoformat()))79 # Track violations80 if action.startswith('remove'):81 db.execute('INSERT INTO violations VALUES (?,?,?)', (author, reason, datetime.now(timezone.utc).isoformat()))82 violations = get_violations(db, author)83 if violations >= 3:84 requests.post(f'https://oauth.reddit.com/r/{SUBREDDIT}/api/friend',85 headers=headers(),86 data={'name': author, 'type': 'banned', 'ban_reason': f'{violations} violations/7d',87 'ban_message': f'Banned for repeated violations. Appeal via modmail.', 'duration': 30})88 log.info(f'BANNED u/{author} ({violations} violations)')89 db.commit()90 time.sleep(0.5)91 except Exception as e:92 log.error(f'Bot error: {e}')93 time.sleep(30)9495if __name__ == '__main__':96 run_bot(init_db())Error handling
403 Forbidden — {"message": "Forbidden", "error": 403} or USER_REQUIREDThe bot account is not a moderator on the target subreddit, or the OAuth2 app is missing the required mod scopes (modposts, modothers, modmail).
Grant the bot account moderator status in the subreddit's Mod Tools → Moderators panel. Verify the OAuth2 scopes include modposts, modothers, and modmail.
No retry — fix moderator permissions and re-authenticate.
401 UnauthorizedAccess token expired (1-hour TTL) or the bot account's password changed, invalidating all tokens.
Refresh the access token using the password grant. If the account password changed, re-authenticate with the new credentials.
Immediate retry after successful token refresh.
Too Many Requests60 RPM global rate limit exceeded. Polling too frequently or applying actions to many items in rapid succession.
Read X-Ratelimit-Remaining header and slow down when it approaches zero. Add 0.5s delays between moderation actions.
Wait until X-Ratelimit-Reset, then resume with increased delays between requests.
errors: [["RATELIMIT", "you are doing that too much", "ratelimit"]]Per-action rate limiting for certain moderation operations (especially banning) when applied rapidly.
Add delays between ban actions (2-5 seconds). Check json.errors on all moderation action responses.
Sleep 60-300 seconds after encountering RATELIMIT in response body, then retry.
Rate Limits for Reddit Moderation API
| Scope | Limit | Window |
|---|---|---|
| Global (with OAuth) | 60 requests | per minute |
| Per-action spam protection | Variable — returned in response body | Rolling |
1import time2import requests34def mod_action_with_retry(url, data, max_retries=3):5 for attempt in range(max_retries):6 r = requests.post(url, headers=headers(), data=data)7 if r.status_code == 429:8 time.sleep(int(r.headers.get('X-Ratelimit-Reset', 60)))9 continue10 r.raise_for_status()11 errors = r.json().get('errors', []) if r.content else []12 if errors and errors[0][0] == 'RATELIMIT':13 time.sleep(60 * (attempt + 1))14 continue15 return r16 raise Exception('Max retries exceeded')- Add 0.5-second delays between consecutive moderation actions to avoid hitting per-action limits
- Batch evaluate items before taking actions — read all modqueue items first, then apply actions in sequence
- Monitor X-Ratelimit-Remaining and pause when below 10 remaining requests
- Poll the modqueue every 30-60 seconds rather than continuously to preserve rate limit budget
- Prioritize removing spam (higher impact) over approving trusted posts when rate-limited
Security checklist
- Use a dedicated bot account with only the minimum required moderator permissions — not a full-admin account
- Store all credentials (client_id, client_secret, bot password) in environment variables
- Log every moderation action with timestamp, item fullname, author, action, and reason for accountability
- Review your rules against historical modqueue data before enabling auto-removal in production
- Implement a dry-run mode that logs intended actions without executing them for validation
- Set up a manual override to pause the bot instantly if it starts making wrong decisions
- Regularly audit the actions log for false positives (approved spam) and false negatives (incorrectly removed posts)
Automation use cases
Spam Keyword Bot
beginnerAuto-remove posts and comments containing known spam phrases and domains, marking them as spam to train Reddit's filters.
New Account Gatekeeping
intermediateHold posts from accounts younger than 30 days or with fewer than 50 karma in the modqueue for manual review instead of auto-publishing.
Trusted User Fast-Track
intermediateAuto-approve posts from established community members (high karma, long account age) to reduce moderator workload.
Full AutoModerator Replacement
advancedBuild a custom moderation bot with complex rules (link karma, account age, domain whitelist/blacklist, keyword scoring) that exceeds AutoModerator's native capabilities.
No-code alternatives
Don't want to write code? These platforms can automate the same workflows visually.
Zapier
Starter from $19.99/monthZapier has no native Reddit moderation module. You'd need custom webhooks and HTTP calls to interact with the moderation API, providing minimal advantage over direct API access.
- + Can route moderation alerts to email/Slack
- + No server infrastructure
- - No native mod API support
- - Cannot poll modqueue
- - No RATELIMIT body parsing
Make
Core from $9/monthMake's HTTP Request module can interact with the Reddit moderation API, but building a polling-based bot requires complex scenario scheduling and multiple modules.
- + HTTP flexibility
- + Scheduling support
- - No native Reddit mod module
- - Complex to set up polling
- - Per-operation pricing for high-volume mod bots
n8n
Free self-hosted; Cloud from €20/monthn8n can run scheduled workflows that call the Reddit moderation API via HTTP nodes, combined with code nodes for rule evaluation logic. Better than Zapier/Make for this use case.
- + Self-hostable (free)
- + Code node handles rule complexity
- + Cron-based polling
- - Still no native Reddit mod node
- - Workflow complexity for multi-step mod flows
Best practices
- Grant the bot only the minimum required moderator permissions — it doesn't need traffic analytics or config access
- Start with remove_spam=false and only add spam=true once you're confident in your keyword rules
- Use temporary bans (30 days) rather than permanent bans for automated actions — reserve permanent bans for human moderators
- Keep a shadow queue of borderline items (score 40-60%) for human review rather than auto-removing everything below threshold
- Test rules on your modqueue history before enabling auto-removal — compare bot decisions to actual human moderator decisions
- Include a ban_message that explains the reason and tells users how to appeal — this reduces modmail volume
- Log the specific rule that triggered each action to help tune thresholds and identify false positives
- Reddit's 2023 mod bot guarantee means these operations are free forever — you don't need a commercial API plan
Ask AI to help
Copy one of these prompts to get a personalized, working implementation.
I'm building a Reddit moderation bot in Python using the OAuth2 API (oauth.reddit.com). The bot polls /r/{subreddit}/about/modqueue every 30 seconds, evaluates each item against spam rules, and calls POST /api/approve or POST /api/remove. I need help building an evaluate_item() function that scores items 0-100 based on: spam keyword presence (add 40 points each), account karma below 50 (add 30 points), account age below 30 days (add 20 points), and blocked domain in URL (add 50 points). Items scoring 80+ should be auto-removed as spam, 0-20 should be auto-approved, and 21-79 should be skipped for human review.
Build a Reddit moderation dashboard for subreddit moderators. Features: a live modqueue table showing pending items with author karma, account age, spam score, and quick action buttons (approve/remove/ban), an action history log with filtering by action type and date, a rules configuration panel for spam keywords, minimum karma, and domain blocklist, and a user lookup that shows a Reddit user's violation history. Use Reddit's orange and white color scheme with a clean table layout.
Frequently asked questions
Is Reddit's moderation API really free?
Yes. Reddit guaranteed in 2023 that mod bots will remain free forever as part of their developer policy. Moderation tools (approve, remove, ban, modmail) operate within the standard 60 RPM OAuth free tier. Even if Reddit introduces commercial pricing for other use cases, mod bots are explicitly exempted.
What happens when I hit the rate limit during heavy moderation?
You'll encounter HTTP 429 with X-Ratelimit-Reset indicating when the window resets (60 RPM total). Additionally, some moderation actions (especially banning) may return RATELIMIT errors inside 200 OK responses when applied too rapidly. Check X-Ratelimit-Remaining after each call and pause when it reaches zero. Add 0.5s delays between individual ban actions.
Can the bot moderate all subreddits or only ones where the account is a moderator?
Only subreddits where the authenticated account has moderator status. The API returns 403 SUBREDDIT_NOTALLOWED for any subreddit where the bot isn't a mod. There's no way to moderate a subreddit you don't have mod access to — this is intentional to prevent abuse.
How does this compare to AutoModerator?
Reddit's native AutoModerator handles basic keyword rules and account age/karma thresholds without API access. A custom API bot is better when you need: complex multi-condition logic, integration with external databases (known spam accounts), machine learning classification, bulk historical cleanup, cross-subreddit coordination, or detailed action logging beyond AutoModerator's built-in reporting.
Can I use PRAW to build the moderation bot instead of raw HTTP?
Yes — PRAW is the recommended Python approach. Use subreddit.mod.queue() to iterate the modqueue, submission.mod.approve()/submission.mod.remove() for actions, and subreddit.banned.add(username, ban_message=...) for banning. PRAW handles rate limiting and token refresh automatically. Install with pip install praw.
Will Reddit ban my bot account for automated moderation?
No — automated moderation is explicitly allowed and encouraged by Reddit for subreddit mods. The key requirements: use proper OAuth2 authentication (not scraping), include a valid User-Agent identifying your bot, act only in subreddits where you have mod permissions, and stay within the 60 RPM rate limit. Reddit's policy specifically protects mod automation tools.
Can RapidDev build a custom Reddit moderation bot for my community?
Yes. RapidDev has built 600+ apps including community management tools and moderation bots. We can build a production moderation bot with custom rule engines, violation tracking, automated reporting, and a moderator dashboard. Contact us for a free consultation.
Need this automated?
Our team has built 600+ apps with API automations. We can build this for you.
Book a free consultation