use case · user comments

Filter comment spam without burning your community.

Updated May 12, 2026

Comment sections are unique: a wrongly-blocked legitimate comment is public and visible. False positives drive engaged users away. The right tool is a calibrated probability you can route through three buckets — publish, hold, reject — not a binary classifier that forces you to pick one side of an awkward tradeoff.

The three-bucket pattern

Run every comment through /v1/predict on submission. Compare against two thresholds:

  • Below 0.60 → publish immediately.
  • 0.60 – 0.90 → save with status needs_review; show to the author but not to the public, surface in your moderation queue.
  • Above 0.90 → reject silently. Storing the comment with a rejected status (rather than deleting) lets you audit the model's behaviour later.

The exact thresholds depend on your appetite for false positives. Communities like dev forums (where every comment matters) skew conservative; review sections on a marketplace can be more aggressive. Tune on your own queue.

Drop-in handler (Python/Flask)

The pattern uses the official siftfy Python client — see SDKs. Two opinionated choices: trusted users skip the API entirely (cheap and noticeable on volume), and any Siftfy-side failure routes the comment to review rather than publishing it raw.

python
# Flask handler for POST /api/comments
import os
from flask import Flask, request, jsonify
from siftfy import Siftfy, RateLimitError, APIError

app = Flask(__name__)
client = Siftfy(api_key=os.environ["SIFTFY_KEY"], timeout=2.0)

SPAM_THRESHOLD = 0.90    # high bar — false-blocks burn your community
QUEUE_THRESHOLD = 0.60   # send to mod queue between QUEUE and SPAM

@app.post("/api/comments")
def post_comment():
    body = request.json
    text = body.get("body", "").strip()
    user_id = body["user_id"]

    if not text:
        return {"error": "body required"}, 400

    # Established trusted users skip classification entirely. Cheap to
    # implement (a single query against your "verified" table) and saves
    # a Siftfy call on the bulk of legitimate traffic.
    if user_is_trusted(user_id):
        return save_comment(body, status="published")

    probability = 0.0
    try:
        result = client.predict(text)
        probability = result.spam_probability
    except RateLimitError:
        # Hit our minute cap. Fall through to "queue" so a human eyeballs it.
        return save_comment(body, status="needs_review", reason="ratelimit")
    except APIError as e:
        # Server-side issue at Siftfy. Log and queue for review rather than
        # letting raw user content go straight to publish.
        app.logger.warning("siftfy %s rid=%s", e.status_code, e.request_id)
        return save_comment(body, status="needs_review", reason="siftfy-error")

    if probability >= SPAM_THRESHOLD:
        return save_comment(body, status="rejected", probability=probability)
    if probability >= QUEUE_THRESHOLD:
        return save_comment(body, status="needs_review", probability=probability)

    return save_comment(body, status="published", probability=probability)

Layering with your own signals

Siftfy classifies the comment text. Stack your own metadata signals on top:

  • Account age and karma. Brand-new accounts posting at threshold-adjacent scores warrant more scrutiny than ten-year-old accounts saying the same thing.
  • Posting velocity. Five 0.55-probability comments in 30 seconds is much more suspicious than one.
  • External link density. Comments at the borderline that contain three or more external links are disproportionately spam in our internal data.

Combine these with the spam probability into a single moderation score; the calibrated 0–1 output makes that math straightforward.

Edge cases

  • Edits. Re-classify on edit. Spammers commonly post a clean comment, then edit in spam links once the comment is approved. The cost is one extra API call per edit.
  • Multilingual content. Non-English comments can score artificially high. Either pre-detect language and skip classification, or raise your thresholds for non-English users.
  • Quoted spam. A genuine reply that quotes a spam parent comment will inherit the spam signal. Strip quotes before classifying, or classify only the new lines.
  • Logging. Always store the request_id from the Siftfy response with the comment row — it's the only handle for support tickets.

What do moderators usually ask first?

Should spam comments be deleted or queued?

Queue borderline comments and only reject very high probabilities. Keeping a rejected or reviewed record gives moderators an audit trail and lets you tune thresholds against your own community.

Should trusted users skip classification?

Yes, if you have a reliable trust signal. Established authors, verified customers, or staff can skip the API call so moderation effort stays focused on unknown or risky traffic.

What should happen if Siftfy is unavailable?

For public comments, fail closed into moderation review rather than publishing unclassified text. The user can still see a normal submission result while moderators inspect the queue.

Get an API key

See the EchoThread integration for the same pattern in production, or read /v1/predict for the full request/response shape. Background reading: moderation playbook, comment spam & SEO.