Skip to main content
Documentation
Learning Center

Master content moderation with comprehensive guides, tutorials, and API documentation

Quick Links

Spam Detection

Discuse spam detection classifies text as spam or not, with a confidence score. Send text to POST https://api.discuse.com/api/v2/check with check_spam enabled, and read the verdict back from results.spamfinder. It catches the promotional spam, scams, and bot-generated noise that simple keyword filters miss.

What does spam detection catch?

The model is trained on the high-volume patterns that evade blocklists:

  • Promotional spam and unsolicited advertising
  • Scam and phishing messages
  • Bot-generated and copy-paste content

It returns a single label (such as spam or ham) plus a confidence score, so you can decide how strict to be.

How do I run a spam check?

curl -X POST https://api.discuse.com/api/v2/check \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "content": {
      "text": "CONGRATULATIONS! You won $10,000! Click here to claim: bit.ly/fake"
    },
    "settings": {
      "check_spam": true
    }
  }'

Response format

{
  "has_violations": true,
  "cached": false,
  "message": "Spam content detected",
  "results": {
    "hits": true,
    "spamfinder": {
      "text": "CONGRATULATIONS! You won $10,000! Click here to claim: bit.ly/fake",
      "label": "spam",
      "confidence": 0.97,
      "is_spam": true,
      "hit": true
    }
  },
  "usage": {
    "api_requests_used": 8,
    "api_requests_limit": 5000,
    "api_requests_remaining": 4992
  }
}

What fields does the spam result return?

Field Type Meaning
text string The text that was classified
label string Model classification (e.g. spam, ham)
confidence number Model confidence in the label (0.0–1.0)
is_spam bool Raw model verdict — label == spam, threshold-unaware
hit bool Threshold-aware decision — is_spam AND confidence ≥ your project's spam threshold

is_spam vs hit

is_spam is the raw verdict: the model labelled the text as spam regardless of how confident it was. hit additionally requires the confidence to clear your project's configured spam threshold. Gate moderation actions on hit, not on is_spam, so a low-confidence spam label does not punish a borderline message.

How do I interpret the confidence score?

confidence reflects how certain the model is of its label:

  • 0.0 – 0.3: very low — likely legitimate.
  • 0.3 – 0.5: low — borderline.
  • 0.5 – 0.7: moderate — suspicious.
  • 0.7 – 0.9: high — very likely spam.
  • 0.9 – 1.0: very high — almost certainly spam.

Recommended thresholds

Set your project's spam threshold to match your platform's tolerance:

const SPAM_THRESHOLDS = {
  strict: 0.5,      // professional platforms, financial services
  standard: 0.7,    // social media, forums
  permissive: 0.85  // creative platforms, open communities
};

Use cases

Comment sections

async function moderateComment(comment) {
  const result = await checkSpam(comment.text);
  const spam = result.results.spamfinder;

  if (spam.hit) {
    if (spam.confidence > 0.9) {
      return { action: 'reject', reason: 'spam_detected' };
    }
    return { action: 'review', reason: 'possible_spam' };
  }
  return { action: 'approve' };
}

User registration

def validate_registration(user_data):
    bio = user_data.get('bio')
    if bio:
        result = check_spam(bio)
        if result['results']['spamfinder']['hit']:
            return {'approved': False, 'reason': 'Spam content detected in profile'}
    return {'approved': True}

Messaging platforms

async function filterMessage(message, sender) {
  const result = await checkSpam(message.text);
  const spam = result.results.spamfinder;

  if (spam.hit) {
    await incrementSpamCount(sender.id);
    const spamCount = await getSpamCount(sender.id);
    if (spamCount > 3) {
      await banUser(sender.id, 'repeated_spam');
    }
    return { delivered: false, reason: 'Message filtered as spam' };
  }
  return { delivered: true };
}

Combining with other checks

Run spam alongside sentiment and language in one request:

{
  "content": {
    "text": "Check out this amazing deal! Click here: example.com/offer"
  },
  "settings": {
    "check_spam": true,
    "check_sentiment": true,
    "check_language": true
  }
}

The response then carries results.spamfinder, results.sentiment, and results.language together.

Best practices

Use graded responses

Instead of binary block/allow, branch on confidence:

function handleSpamResult(spam) {
  if (!spam.hit) return 'allow';
  if (spam.confidence > 0.95) return 'silent_delete';
  if (spam.confidence > 0.8)  return 'block_notify';
  if (spam.confidence > 0.6)  return 'flag_for_review';
  return 'apply_friction';
}

Track repeat offenders

async function assessUser(userId, spam) {
  if (spam.hit) {
    await incrementUserSpamScore(userId, spam.confidence);
  }
  const userScore = await getUserSpamScore(userId);
  if (userScore > 10.0) await autoSuspendUser(userId);
  else if (userScore > 5.0) await flagForManualReview(userId);
}

Whitelist trusted users

Skip the spam check for verified or high-trust accounts to cut false positives and save quota:

function shouldCheckSpam(user) {
  if (user.isVerified) return false;
  if (user.trustScore > 0.9) return false;
  return true;
}

Usage limits

Spam detection draws from your sentiment-analysis quota:

Plan Monthly Analyses Notes
Basic 1,000 Includes spam + sentiment
Gold 5,000 Includes spam + sentiment
Platinum 15,000 Includes spam + sentiment
Ultimate 30,000 Includes spam + sentiment

Cached responses do not count against your quota.

Integration examples

Express.js middleware

const spamFilter = async (req, res, next) => {
  if (req.body.text) {
    const result = await checkSpam(req.body.text);
    if (result.results.spamfinder.hit) {
      return res.status(400).json({
        error: 'spam_detected',
        message: 'Your message was flagged as spam'
      });
    }
  }
  next();
};

app.post('/api/comments', spamFilter, createComment);

Python Flask

from functools import wraps
from flask import request, jsonify

def spam_filter(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        text = request.json.get('text')
        if text:
            result = check_spam(text)
            if result['results']['spamfinder']['hit']:
                return jsonify({
                    'error': 'spam_detected',
                    'message': 'Your message was flagged as spam'
                }), 400
        return f(*args, **kwargs)
    return decorated

@app.route('/api/comments', methods=['POST'])
@spam_filter
def create_comment():
    pass

Next steps

Written by the Discuse Team · Last updated June 2026

Related Articles

Text Analysis and Sentiment Detection

Detect spam, toxicity, profanity, and analyze sentiment in text content

Image NSFW Detection

Automatically detect and filter inappropriate images and adult content

File Antivirus Scanning

Protect your platform from malware, viruses, and malicious files