Fetch pending comments with GET /wp/v2/comments?status=hold, run each through a keyword blocklist or AI spam check, then update the status to 'approved', 'spam', or 'trash' via PUT /wp/v2/comments/{id}. Requires Application Password belonging to an Editor or Administrator — the moderate_comments capability is mandatory. WordPress has no comment webhooks, so schedule this as a cron job every 15 minutes.
| Fact | Value |
|---|---|
| Platform | WordPress |
| Rate limits | No core limit — managed hosts may throttle rapid sequential requests |
| Difficulty | Beginner |
| Time required | 45 minutes |
| Last updated | May 2026 |
API Quick Reference
No core limit — managed hosts may throttle rapid sequential requests
REST only
WordPress REST API Comment Moderation Overview
WordPress exposes comment management through the /wp/v2/comments endpoint. You can list pending comments filtered by status=hold, then approve, mark as spam, or trash each comment individually. Authentication uses Application Passwords over HTTPS — a core WordPress feature since 5.6. There are no native comment webhooks, so you must poll on a schedule.
https://yoursite.com/wp-json/wp/v2Setting Up Application Password Authentication
- 1Log in to your WordPress admin dashboard
- 2Navigate to Users → Your Profile
- 3Scroll to the Application Passwords section
- 4Enter a name like 'Comment Moderator Bot' and click Add New Application Password
- 5Copy the 24-character password immediately — it's only shown once
- 6Use an Editor or Administrator account — Subscriber and Author roles lack the moderate_comments capability
- 7Base64-encode 'username:application-password' (strip spaces from the password first) for the Authorization header
Key endpoints
/wp/v2/commentsFetch comments filtered by status. Use status=hold to get all pending comments awaiting moderation.
/wp/v2/comments/{id}Update a comment's status. Valid status values are 'approved', 'hold', 'spam', and 'trash' — these are strings, not booleans.
/wp/v2/posts/{id}Fetch post metadata when you need to correlate a comment with its parent post context — useful for logging and reporting.
Step-by-step automation
Configure Authentication and Base URL
Why: Application Passwords require HTTPS and must belong to a user with moderate_comments capability. Using the wrong role returns 403 rest_forbidden on every status update.
Set up your credentials and base URL. The Application Password is displayed with spaces when generated — strip them before Base64 encoding. Never hardcode credentials in source files; use environment variables.
1# Set environment variables2export WP_BASE_URL="https://yoursite.com/wp-json/wp/v2"3export WP_USER="editor_username"4export WP_APP_PASSWORD="abcd1234efgh5678ijkl9012"56# Test authentication — should return your user object7curl -s \8 -H "Authorization: Basic $(echo -n "$WP_USER:$WP_APP_PASSWORD" | base64)" \9 "$WP_BASE_URL/users/me" | python3 -m json.tool | grep -E '"id"|"name"|"capabilities"'Fetch All Pending Comments with Pagination
Why: WordPress returns a maximum of 100 comments per page. X-WP-TotalPages tells you how many pages exist. Without pagination, you'll miss comments on large sites.
Fetch all comments with status=hold, handling pagination via the X-WP-TotalPages header. A site with 250 pending comments requires 3 requests at 100 per page.
1# Fetch first page of pending comments2curl -s \3 -H "Authorization: Basic $(echo -n "$WP_USER:$WP_APP_PASSWORD" | base64)" \4 -I "$WP_BASE_URL/comments?status=hold&per_page=100&page=1" | grep -i 'x-wp-total'56# Fetch with full response7curl -s \8 -H "Authorization: Basic $(echo -n "$WP_USER:$WP_APP_PASSWORD" | base64)" \9 "$WP_BASE_URL/comments?status=hold&per_page=100&page=1" | python3 -m json.toolBuild the Spam Filter Logic
Why: Running every comment through the same logic ensures consistent moderation. The filter should be conservative — only automatically approve clearly clean comments; route borderline cases to manual review by leaving them in hold status.
Create a spam classifier that checks comment content against a keyword blocklist, evaluates link count, and optionally calls an AI API for advanced spam detection. Return 'approved', 'spam', or 'hold' for each comment.
1# Example: Check a comment body for spam signals manually2# In production, this logic runs in your script, not curl3# But you can test the classification with a simple keyword check:4COMMENT_TEXT="Buy cheap pills at example.com! Click here for discount"5if echo "$COMMENT_TEXT" | grep -iE 'buy cheap|click here|discount|casino|pills' > /dev/null; then6 echo "SPAM: keyword match"7else8 echo "CLEAN: no blocklist keywords"9fiUpdate Comment Status via API
Why: Each comment requires an individual PUT request. Batching is not supported — one request per comment. Add 100ms delays between calls to avoid triggering managed host rate limiting.
Send PUT requests to update each comment's status. Track results (approved, spam, trashed, errors) for logging. Skip comments already in the desired state to save API calls.
1# Approve a comment2curl -s -X PUT \3 -H "Authorization: Basic $(echo -n "$WP_USER:$WP_APP_PASSWORD" | base64)" \4 -H "Content-Type: application/json" \5 -d '{"status": "approved"}' \6 "$WP_BASE_URL/comments/123"78# Mark a comment as spam9curl -s -X PUT \10 -H "Authorization: Basic $(echo -n "$WP_USER:$WP_APP_PASSWORD" | base64)" \11 -H "Content-Type: application/json" \12 -d '{"status": "spam"}' \13 "$WP_BASE_URL/comments/456"Complete working code
Complete WordPress comment moderation automation. Fetches all pending comments, classifies them with a keyword filter, updates statuses via the REST API, and logs results.
Error handling
The Application Password belongs to a user without the moderate_comments capability. Subscriber and Author roles cannot moderate comments.
Regenerate the Application Password using an Editor or Administrator account. Verify with GET /wp/v2/users/me and check the capabilities object.
The Authorization header is missing, malformed, or the Application Password was revoked.
Verify your Base64 encoding — strip spaces from the Application Password before encoding. Check Users → Profile → Application Passwords to confirm it still exists.
The comment was deleted or trashed between fetching and updating — common in high-traffic sites.
Wrap PUT requests in try/catch and skip 404 errors gracefully. Refresh the comment list before processing if the run takes more than a few minutes.
Passing a boolean (true/false) or integer (0/1) instead of the string status values.
Status must be a string: 'approved', 'hold', 'spam', or 'trash'. These are string literals, not booleans.
Managed hosts (WP Engine, Kinsta, SiteGround) enforce WAF-level throttles on rapid sequential REST requests.
Add 100ms sleep between individual PUT requests. For large queues, process in batches of 50 with a 2-second pause between batches.
Rate Limiting for WordPress Comment Moderation
| Scope | Limit | Window |
|---|---|---|
Security checklist
- Store WP_APP_PASSWORD as an environment variable — never hardcode in source files
- Use HTTPS for all API requests — Application Passwords do not work over HTTP (except localhost)
- Generate a dedicated Application Password named 'Comment Moderator' for this script
- Use a dedicated Editor account for the automation rather than your admin credentials
- Log spam decisions with comment ID, author email, and content snippet for audit purposes
- Review spam queue weekly to confirm the filter isn't over-aggressively flagging legitimate comments
- Rotate Application Passwords every 90 days — revoke old ones from Users → Profile
Automation use cases
15-Minute Moderation Cron
Run the script every 15 minutes via cron (or a serverless scheduler like AWS EventBridge). Keeps the comment queue empty without requiring manual attention.
High-Traffic Post Protection
Trigger moderation immediately when a post gets unusual comment volume. Use the post={id} parameter to process only comments on viral or high-priority posts.
Tiered Moderation Pipeline
Combine keyword filtering with AI spam detection (OpenAI Moderation API or Perspective API) for a two-stage pipeline: quick keyword filter first, then AI for borderline cases.
Author-Based Trust Scoring
Maintain a list of trusted commenter emails. Skip moderation for known good authors and apply stricter rules for first-time commenters or guests.
Best practices
- Never auto-trash comments — move to spam status first so Akismet and other plugins can learn from the signal, then let the 30-day cleanup process handle deletion
- Keep borderline comments (short text with link, new commenter) in 'hold' status for manual review rather than auto-approving or auto-trashing
- Add 100ms delay between individual comment update requests to avoid triggering managed host WAF throttles
- Log all moderation decisions with timestamp, comment ID, author email, and the decision reason for compliance and audit trails
- Test your keyword blocklist monthly — spam patterns evolve, and overly broad patterns (e.g., 'free') will catch legitimate comments
- Use a dedicated Editor account for the automation with its own Application Password — not your main admin credentials
- Schedule moderation runs during low-traffic hours (2–5 AM server time) to minimize impact on host throttle budgets
Ask AI to help
Copy one of these prompts to get a personalized, working implementation.
I'm building a WordPress comment moderation script using the REST API. I want to fetch all pending comments with GET /wp/v2/comments?status=hold, classify them as approved, spam, or hold using keyword matching and link count, then update each with PUT /wp/v2/comments/{id}. Using Application Password auth (Editor role). Can you help me add an AI-powered spam scoring layer using the OpenAI Moderation API as a second stage for borderline comments?
Frequently asked questions
Why am I getting 403 when I try to update comment status?
Your Application Password belongs to a user without the moderate_comments capability. Only Editor and Administrator roles can moderate comments. Subscriber and Author roles return 403 rest_forbidden. Generate a new Application Password from an Editor or Admin account.
What's the difference between 'spam' and 'trash' status for comments?
Spam is the correct status for junk comments — it feeds spam-learning plugins (like Akismet) and keeps the comment visible in the Spam queue for plugin analysis. Trash is for legitimate comments you want to delete — they're recoverable for 30 days then purged. Always use spam for automated moderation results, not trash.
Does WordPress have comment webhooks so I don't need to poll?
WordPress core has no webhook system for comments. You must poll with GET /wp/v2/comments?status=hold on a schedule (every 15 minutes works well for most sites). WooCommerce adds webhook support for orders, but that doesn't extend to core WordPress comments.
How do I get comments from a specific post only?
Add the post parameter: GET /wp/v2/comments?status=hold&post=123. Replace 123 with the post ID. This is efficient when you only need to moderate comments on a specific high-traffic post after publishing.
What is the maximum number of comments I can fetch per request?
WordPress allows up to per_page=100 per request. Check the X-WP-TotalPages response header to determine if you need additional pages. A site with 350 pending comments requires 4 requests (100+100+100+50). The default is per_page=10 if you don't specify.
Can I moderate comments on a WordPress.com hosted site using Application Passwords?
WordPress.com uses its own OAuth2 API (developer.wordpress.com) rather than Application Passwords. The endpoints are the same (/wp/v2/comments) but authentication differs. For self-hosted WordPress.org sites on managed hosts (WP Engine, Kinsta, etc.), Application Passwords work as documented.
RapidDev built a comment moderation pipeline — what's a realistic time to implement this?
A basic keyword-filter moderation script takes about 45 minutes to build and deploy. The RapidDev team can help with more advanced implementations that combine keyword filtering, AI spam scoring, and Slack notifications for manual review — typically a 2-3 hour project depending on complexity.
Need this automated?
Our team has built 600+ apps with API automations. We can build this for you.
Book a free consultation