Automate Instagram comment moderation using the Graph API: list comments with GET /{media-id}/comments, hide them with POST /{comment-id}?hidden=true, delete with DELETE /{comment-id}, and reply with POST /{comment-id}/replies?message=. There is no webhook for new comments — you must poll. The instagram_business_manage_comments scope requires separate App Review approval (2-4 weeks). The 200 calls/hour rate limit requires smart polling intervals across multiple posts.
API Quick Reference
OAuth 2.0
200 calls/hour per user
JSON
Available
Understanding the Instagram Comment Moderation API
The Instagram Graph API v25.0 provides full comment moderation capabilities: listing comments, hiding them, deleting them, and replying to them. All operations use the instagram_business_manage_comments OAuth scope, which requires separate App Review submission from the publishing scope. The base URL is https://graph.instagram.com/v25.0.
The critical design constraint for comment moderation automation: Instagram provides no webhook or real-time notification for new comments. You must poll the /comments endpoint on each post at regular intervals to detect new comments. With a 200 calls/hour limit per user and potentially dozens of posts to monitor, you need intelligent polling that prioritizes recent and high-engagement posts rather than polling every post at equal frequency.
The moderation actions themselves are straightforward: hiding a comment (preferred over deleting, as it preserves context while removing visibility) uses POST /{comment-id}?hidden=true, which is reversible. Deleting with DELETE /{comment-id} is permanent. Replies use POST /{comment-id}/replies with a message parameter. Official docs: https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-media/comments
https://graph.instagram.com/v25.0Setting Up Instagram Comment Moderation Authentication
Comment moderation requires the instagram_business_manage_comments scope, which must be separately approved through Meta App Review. This is distinct from the content publishing scope — even if you have publishing approved, you need a separate review for moderation. During development, add test accounts in App Dashboard > Roles > Test Users.
- 1Create or use an existing Business App at developers.facebook.com
- 2Add Instagram product if not already added
- 3In App Review > Permissions and Features, request instagram_business_basic and instagram_business_manage_comments
- 4For the review, prepare a screencast demonstrating your comment moderation use case (keyword filtering, spam detection, etc.)
- 5Implement OAuth redirect: https://www.facebook.com/v25.0/dialog/oauth?client_id={app-id}&redirect_uri={uri}&scope=instagram_business_basic,instagram_business_manage_comments
- 6Exchange authorization code for short-lived token at https://graph.facebook.com/v25.0/oauth/access_token
- 7Exchange for 60-day long-lived token: GET https://graph.instagram.com/access_token?grant_type=fb_exchange_token&client_id={app-id}&client_secret={secret}&fb_exchange_token={short-token}
- 8Store token and set up refresh job to run every 50 days
1import requests2import os34IG_USER_ID = os.environ['IG_USER_ID']5ACCESS_TOKEN = os.environ['IG_ACCESS_TOKEN']67def verify_moderation_access():8 """Verify comment moderation permissions are active."""9 # Try to list comments on a recent post to verify access10 # First get a recent post11 resp = requests.get(12 f'https://graph.instagram.com/v25.0/{IG_USER_ID}/media',13 params={'fields': 'id', 'limit': 1, 'access_token': ACCESS_TOKEN}14 )15 resp.raise_for_status()16 posts = resp.json().get('data', [])17 if not posts:18 print('No posts found to test with')19 return20 media_id = posts[0]['id']21 comments_resp = requests.get(22 f'https://graph.instagram.com/v25.0/{media_id}/comments',23 params={'fields': 'id,text', 'access_token': ACCESS_TOKEN}24 )25 if comments_resp.status_code == 403:26 raise PermissionError(27 'Missing instagram_business_manage_comments scope. Submit App Review.'28 )29 comments_resp.raise_for_status()30 print(f'Comment moderation access confirmed. Found {len(comments_resp.json().get("data", []))} comments on test post.')3132verify_moderation_access()Security notes
- •Store access tokens in environment variables — never in source code or logs
- •The manage_comments scope allows hiding and deleting user content — treat it as a privileged operation and audit all actions
- •Log every hide/delete/reply action with comment ID, action type, reason, and timestamp
- •Never automatically delete comments without a human review step for edge cases — prefer hiding as a reversible first action
- •Implement token refresh automation every 50 days
- •Keep keyword and spam filter rules in a separate configuration file that can be updated without code changes
Key endpoints
/{ig-media-id}/commentsReturns comments on a specific Instagram post. Includes comment ID, text, username, and can include replies. Supports pagination for posts with many comments.
| Parameter | Type | Required | Description |
|---|---|---|---|
fields | string | optional | Use 'id,text,username,timestamp,replies{id,text,username}' to get comment content, author, and nested replies. |
limit | number | optional | Number of comments to return per page. Default 10, max 50. |
Response
1{"data": [{"id": "17858893269000001", "text": "Great post!", "username": "user123", "timestamp": "2026-05-07T10:30:00+0000"}, {"id": "17858893269000002", "text": "Spam comment buy now!!!", "username": "spammer99"}], "paging": {"cursors": {"after": "abc123"}}}/{ig-comment-id}Hides or unhides a comment. Setting hidden=true makes the comment invisible to everyone except the commenter (they don't know it's hidden). This is reversible — prefer hiding over deletion for borderline content.
| Parameter | Type | Required | Description |
|---|---|---|---|
hidden | boolean | required | Set to true to hide the comment, false to unhide. |
Request
1{"hidden": true, "access_token": "EAAB..."}Response
1{"success": true}/{ig-comment-id}Permanently deletes a comment. This action cannot be undone — use with caution. Prefer hiding for borderline content.
| Parameter | Type | Required | Description |
|---|---|---|---|
access_token | string | required | Long-lived token with instagram_business_manage_comments scope. |
Response
1{"success": true}/{ig-comment-id}/repliesPosts a reply to a comment. The reply appears nested under the original comment. Use for automated responses to questions or welcome messages.
| Parameter | Type | Required | Description |
|---|---|---|---|
message | string | required | The reply text. Can include @mentions and hashtags. Max 2,200 characters. |
Request
1{"message": "Thanks for your comment!", "access_token": "EAAB..."}Response
1{"id": "17858893269000003"}Step-by-step automation
Get Recent Posts to Monitor
Why: You cannot monitor all posts equally — fetching your recent posts and prioritizing by recency/engagement lets you stay within the 200 calls/hour quota.
Fetch your most recent posts with basic engagement stats. For moderation purposes, focus on posts published in the last 7-14 days (older posts rarely get new comments) and sort by comment_count to prioritize active discussions. Store the list of posts and their last-polled timestamps to implement smart polling.
1curl -s "https://graph.instagram.com/v25.0/${IG_USER_ID}/media?fields=id,timestamp,comments_count&limit=20&access_token=${ACCESS_TOKEN}"Pro tip: Build a 'polling priority queue' where posts with high comment_count or recent publish time get polled more frequently. A post published 2 hours ago should be polled every 5 minutes; a post from 10 days ago can be polled hourly.
Expected result: Array of posts from the last 14 days sorted by comment count, each with id, timestamp, and comments_count.
Fetch and Filter New Comments
Why: Polling all comments every run is wasteful — tracking which comments have already been processed prevents re-processing and preserves quota.
For each post, fetch comments with fields including text and username. Compare against your database of already-processed comment IDs. For new comments, run them through your moderation rules: keyword blocklist for obvious spam/toxicity, and pattern matching for promotional content. Store processed comment IDs to avoid re-checking them.
1curl -s "https://graph.instagram.com/v25.0/${MEDIA_ID}/comments?fields=id,text,username,timestamp&limit=50&access_token=${ACCESS_TOKEN}"Pro tip: Prefer hiding over deletion for borderline content. Hidden comments are still stored in your database and can be reviewed/unhidden later. Permanent deletion is appropriate only for obvious spam or content that violates policy.
Expected result: Each comment classified as 'hide', 'delete', 'reply', or 'ok' based on your moderation rules.
Execute Moderation Actions
Why: Hiding or deleting comments requires separate API calls per comment — batch them efficiently to minimize quota usage.
For each classified comment: hide it (POST /{comment-id}?hidden=true), delete it (DELETE /{comment-id}), or reply to it (POST /{comment-id}/replies?message=...). Add a small delay between actions to avoid triggering rate limits. Log every action with comment ID, action type, and reason.
1# Hide a comment2curl -X POST \3 "https://graph.instagram.com/v25.0/${COMMENT_ID}?hidden=true&access_token=${ACCESS_TOKEN}"45# Delete a comment6curl -X DELETE \7 "https://graph.instagram.com/v25.0/${COMMENT_ID}?access_token=${ACCESS_TOKEN}"89# Reply to a comment10curl -X POST \11 "https://graph.instagram.com/v25.0/${COMMENT_ID}/replies" \12 -d "message=Thanks for your question!" \13 -d "access_token=${ACCESS_TOKEN}"Pro tip: Use a daily moderation summary report: count total comments reviewed, hidden, deleted, and replied to. This helps tune your keyword filters and identify trending spam patterns.
Expected result: Array of action results logging what was done to each comment. Spam is hidden/deleted, questions receive automated replies.
Complete working code
Complete comment moderation bot: fetches recent posts, polls for new comments since last run, classifies them using keyword and URL detection rules, executes moderation actions, and logs all decisions. Runs as a cron job every 15 minutes.
1#!/usr/bin/env python32"""Instagram Comment Moderation Bot - runs every 15 minutes via cron."""3import os4import re5import time6import json7import logging8import requests9from datetime import datetime, timedelta, timezone1011logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')12log = logging.getLogger(__name__)1314IG_USER_ID = os.environ['IG_USER_ID']15ACCESS_TOKEN = os.environ['IG_ACCESS_TOKEN']16STATE_FILE = '/tmp/ig_moderation_state.json'17BASE = 'https://graph.instagram.com/v25.0'1819BLOCKLIST = ['spam', 'buy now', 'dm me', 'follow for follow', 'click link', 'free money', 'earn from home']20URL_RE = re.compile(r'https?://|bit\.ly|t\.me|wa\.me', re.I)2122def ig_get(path, params):23 params['access_token'] = ACCESS_TOKEN24 r = requests.get(f'{BASE}{path}', params=params, timeout=30)25 r.raise_for_status()26 return r.json()2728def load_state():29 try:30 with open(STATE_FILE) as f:31 return json.load(f)32 except FileNotFoundError:33 return {'processed_ids': [], 'last_run': None}3435def save_state(state):36 with open(STATE_FILE, 'w') as f:37 json.dump(state, f)3839def get_recent_posts():40 data = ig_get(f'/{IG_USER_ID}/media', {'fields': 'id,timestamp,comments_count', 'limit': 20})41 cutoff = datetime.now(timezone.utc) - timedelta(days=14)42 posts = [p for p in data['data'] if datetime.fromisoformat(p['timestamp'].replace('Z', '+00:00')) > cutoff]43 return sorted(posts, key=lambda p: p.get('comments_count', 0), reverse=True)[:10]4445def classify(text):46 lower = text.lower()47 if URL_RE.search(text) and any(k in lower for k in BLOCKLIST):48 return 'delete'49 if any(k in lower for k in BLOCKLIST):50 return 'hide'51 return 'ok'5253def moderate():54 state = load_state()55 processed = set(state.get('processed_ids', []))56 posts = get_recent_posts()57 total_actions = 058 for post in posts:59 comments_data = ig_get(f'/{post["id"]}/comments', {'fields': 'id,text,username', 'limit': 50})60 for comment in comments_data.get('data', []):61 if comment['id'] in processed:62 continue63 processed.add(comment['id'])64 action = classify(comment.get('text', ''))65 if action == 'hide':66 try:67 requests.post(f'{BASE}/{comment["id"]}', params={'hidden': 'true', 'access_token': ACCESS_TOKEN}, timeout=10)68 log.info(f'Hidden comment {comment["id"]} from @{comment["username"]}')69 total_actions += 170 except Exception as e:71 log.error(f'Hide failed: {e}')72 elif action == 'delete':73 try:74 requests.delete(f'{BASE}/{comment["id"]}', params={'access_token': ACCESS_TOKEN}, timeout=10)75 log.info(f'Deleted comment {comment["id"]} from @{comment["username"]}')76 total_actions += 177 except Exception as e:78 log.error(f'Delete failed: {e}')79 time.sleep(0.3)80 time.sleep(0.5) # Between posts81 # Keep only last 10k IDs to prevent state file bloat82 state['processed_ids'] = list(processed)[-10000:]83 state['last_run'] = datetime.now(timezone.utc).isoformat()84 save_state(state)85 log.info(f'Run complete: {total_actions} actions taken')86 return total_actions8788if __name__ == '__main__':89 moderate()Error handling
{"error":{"code":10,"message":"Application does not have permission for this action","type":"OAuthException"}}The instagram_business_manage_comments permission is not approved for your app or the account is not a Test User in Development mode.
Submit instagram_business_manage_comments for App Review with a screencast showing comment moderation use case. In development, add the Instagram account as a Test User via App Dashboard > Roles > Test Users.
Do not retry — resolve App Review first.
{"error":{"code":17,"message":"User request limit reached","type":"OAuthException"}}Exceeded 200 API calls per hour. Comment moderation involving multiple posts and many comments per post is the primary cause — each comment fetch, hide, and delete is a separate API call.
Reduce polling frequency. Monitor only top 10 posts. Add 300-500ms delays between moderation actions. Check X-Business-Use-Case-Usage header and pause operations when call_count exceeds 80%.
Exponential backoff starting at 60 seconds.
{"error":{"code":100,"message":"Invalid parameter","type":"OAuthException"}}The comment ID does not exist (already deleted by user, or the post was deleted) or the hidden parameter was passed incorrectly.
Catch this error and mark the comment as already handled in your state file. Don't retry — the comment is gone.
Do not retry — mark as processed and continue.
{"error":{"code":190,"message":"Invalid OAuth access token","type":"OAuthException"}}The 60-day long-lived access token has expired or the user revoked app permissions.
Refresh via GET https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token={token}. If fully expired, re-authorize via OAuth.
Do not retry — refresh or re-authorize first.
Rate Limits for Instagram Comment Moderation API
| Scope | Limit | Window |
|---|---|---|
| Per user (app+user pair) | 200 API calls | per hour (rolling) |
| Private replies on comments | 750 private replies | per hour per business account |
1import time2import requests34def safe_moderation_call(method, url, max_retries=3, **kwargs):5 for attempt in range(max_retries):6 r = getattr(requests, method)(url, timeout=20, **kwargs)7 if r.status_code == 400:8 code = r.json().get('error', {}).get('code')9 if code in (17, 4, 32):10 wait = 60 * (2 ** attempt)11 print(f'Rate limited. Waiting {wait}s')12 time.sleep(wait)13 continue14 if code == 100: # Comment deleted by user15 return None16 return r17 raise Exception('Rate limit retries exceeded')- Budget your 200 calls/hour: 10 posts × 1 comment fetch = 10 calls; remaining 190 calls = budget for up to 190 moderation actions per hour
- Use a processed comment ID cache to avoid re-fetching and re-classifying the same comments on every run
- Poll recent posts (last 7 days) more frequently than older posts — most toxic comments arrive within hours of posting
- Add 300-500ms delays between each moderation action (hide/delete/reply) to avoid triggering secondary rate limits
- Log all moderation actions to a database to build a training dataset for improving your keyword filters over time
Security checklist
- Store access tokens in environment variables — never in source code or version control
- Log every moderation action (comment ID, action type, reason, timestamp) in an immutable audit log
- Never auto-delete comments without a confidence threshold — use hiding as the default action and review hidden comments periodically
- Keep your keyword blocklist in a configuration file that can be updated without a deployment
- Implement a review queue for borderline cases rather than making binary hide/delete decisions automatically
- Ensure the instagram_business_manage_comments scope is only used in your backend — never expose the token to frontend code
- Rotate access tokens if your server is compromised and notify the account owner to review recent moderation actions
Automation use cases
Spam Comment Filter
intermediateAutomatically hide or delete comments matching keyword patterns (URLs, promotional phrases, follow-for-follow), running every 15 minutes across all active posts.
Auto-Reply to FAQs
intermediateDetect common question patterns (shipping, pricing, availability) in comments and automatically post a helpful reply template, reducing manual response time.
Competitor Mention Monitor
beginnerAlert moderators when comments mention competitor brand names, allowing quick human response rather than automated action.
Multi-Account Moderation Dashboard
advancedAggregate comments across multiple Instagram Business accounts into a single moderation queue with bulk action capabilities.
No-code alternatives
Don't want to write code? These platforms can automate the same workflows visually.
Zapier
Free tier (limited); Starter from $19.99/monthZapier can monitor Instagram comments and trigger moderation workflows in other tools, though direct comment hiding/deletion requires API credentials.
- + No code required for alerting workflows
- + Can route flagged comments to Slack for human review
- + Easy to set up
- - Cannot directly hide/delete comments without custom code step
- - Higher cost for frequent polling
- - Limited moderation logic
Make (formerly Integromat)
Free tier (1,000 ops/month); Core from $9/monthMake's Instagram module supports comment operations with more complex filtering logic than Zapier and lower per-operation costs.
- + More affordable than Zapier
- + Complex conditional routing
- + Better for bulk operations
- - Same API limitations
- - Steeper learning curve
- - Meta App Review still required
n8n
Free (self-hosted); Cloud from $20/monthn8n self-hosted allows building complete moderation pipelines with HTTP request nodes, custom JavaScript for classification, and database logging.
- + Free self-hosted
- + Full control over moderation logic
- + Can integrate with ML spam classifiers
- - Requires server infrastructure
- - More setup than no-code tools
- - Instagram node has limited comment support
Best practices
- Prefer hiding over deleting for borderline content — hidden comments can be reviewed and unhidden, while deleted comments are permanent
- Build a state file or database tracking processed comment IDs to prevent re-processing the same comments on every polling run
- Never make fully automated permanent deletions on high-confidence-only content — even the best keyword filters produce false positives
- Poll recent posts (published in last 48 hours) every 5-10 minutes; poll older posts much less frequently (hourly or less) to stay within rate limits
- Log all moderation actions with the original comment text, applied rule, and timestamp — this creates a training dataset for improving rules
- Set up a human review queue for comments that score borderline on your classifier, rather than making automated decisions on uncertain cases
- Test your blocklist and rules against historical comment data before deploying to avoid accidentally hiding legitimate comments at launch
Ask AI to help
Copy one of these prompts to get a personalized, working implementation.
I'm building an Instagram comment moderation bot using the Instagram Graph API v25.0. The bot fetches comments via GET /{media-id}/comments?fields=id,text,username, classifies them using keyword matching, then calls POST /{comment-id}?hidden=true to hide spam. My current keyword list is too broad — I'm hiding legitimate comments. Help me: 1) build a better comment classifier that uses contextual rules (URL + keyword = spam, but URL alone = maybe legitimate), 2) implement a confidence score system with thresholds for auto-hide vs. human-review vs. auto-delete, 3) structure the polling scheduler to prioritize high-engagement recent posts within the 200 calls/hour limit.
Build a React comment moderation dashboard for Instagram. It needs: a queue view showing unreviewed flagged comments with their post thumbnail, commenter username, comment text, and AI-assigned spam score; bulk action buttons (Hide All Spam, Approve All) and per-comment actions (Hide, Delete, Approve, Reply); a filter panel to show comments by post, action status, and score threshold; a reply composer for selected comments; and a statistics card showing comments reviewed today, hidden, deleted, and auto-approved. Use Supabase to store the moderation queue and Supabase Edge Functions to call the Instagram API.
Frequently asked questions
Is there a webhook for new Instagram comments so I don't have to poll?
No — the Instagram Graph API does not provide webhooks for new comments. You must poll GET /{media-id}/comments at regular intervals to detect new comments. The Meta Webhooks product covers some Instagram events (mentions, story interactions) but comment webhooks are not available for most Business accounts. Design your polling with smart prioritization (recent posts more frequently) to stay within the 200 calls/hour limit.
What's the difference between hiding and deleting a comment?
Hiding (POST /{comment-id}?hidden=true) makes a comment invisible to all users except the original commenter, who does not know their comment is hidden. The comment still exists in the API and can be unhidden by setting hidden=false. Deleting (DELETE /{comment-id}) permanently removes the comment — it cannot be recovered. Best practice: hide borderline content first, review it periodically, and only delete clearly harmful content.
What happens when I hit the 200 calls/hour rate limit?
You'll receive HTTP 400 with error code 17 (User request limit reached). Each API operation — fetching comments, hiding, deleting, replying — consumes one call. For a bot monitoring 10 posts with 50 comments each per run, that's already 10 calls just for fetching. Implement a processed-ID cache so you don't re-fetch already-reviewed comments, and use exponential backoff (starting at 60 seconds) when rate limited.
Can I automate comment replies at scale?
Yes, but with care. The API allows replies via POST /{comment-id}/replies?message=. Meta monitors for automated or repetitive replies and can flag your account for spam-like behavior. Keep reply messages varied, reply only to genuine questions (not all comments), and limit reply volume to what would be human-realistic for your account size. Also note the 750 private replies per hour limit for business accounts.
Do I need a separate App Review for comment moderation vs. post publishing?
Yes. The instagram_business_manage_comments scope requires a separate App Review submission from instagram_business_content_publish. Even if you already have publishing approved, you must submit a new review specifically for the manage_comments permission, including a new screencast demonstrating your comment moderation use case. Each review typically takes 2-4 weeks.
Can RapidDev build an Instagram comment moderation bot?
Yes — RapidDev has built comment moderation systems combining keyword filtering, ML-based spam detection, and human review queues for brand accounts. We handle the Meta App setup and approval process, build the classification logic, and deliver a moderation dashboard. Book a free consultation at rapidevelopers.com.
Can I use AI (like OpenAI or Claude) to classify Instagram comments?
Yes, and it's much more effective than keyword matching alone. After fetching comments, send each comment text to an LLM with a classification prompt (rate this comment: spam, toxic, promotional, question, positive, neutral) and use the response to drive moderation decisions. LLM classification is especially good at catching sophisticated spam that bypasses simple keyword lists. Budget for API costs: at roughly 50 comments per post and $0.001/classification with a cheap model, the costs are minimal.
Need this automated?
Our team has built 600+ apps with API automations. We can build this for you.
Book a free consultation