Badword Filtering
Badword filtering catches your own list of disallowed words in text. Unlike spam or sentiment — which use trained models — this check matches against a custom word list you configure for your project, so you decide exactly which terms are off-limits. Enable it with check_badwords on POST /api/v2/check.
When should I use a custom word list?
Models are great at fuzzy categories like toxicity, but some terms are policy decisions only you can make: a competitor's name, a banned product, community-specific slurs, leaked-credential patterns, or words your legal team requires you to block. A custom list gives you an exact, predictable match for those.
How does it work?
Badword filtering runs only when two things are true: check_badwords is enabled, and your project has a configured custom word list. It then does a case-insensitive substring match of each listed word against the message text, and returns every word it found.
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": "Check out CompetitorBrand for a better deal"
},
"settings": {
"check_badwords": true
}
}'
Response
{
"has_violations": true,
"cached": false,
"results": {
"hits": true,
"badwords": {
"hit": true,
"matched_words": ["CompetitorBrand"],
"apply_penalty": true
}
}
}
The results.badwords object has three fields:
| Field | Type | Description |
|---|---|---|
hit |
boolean | True if any listed word was found |
matched_words |
string[] | The configured words that matched the text |
apply_penalty |
boolean | Whether the match should trigger your enforcement (always true for a match) |
How do I configure the word list?
The list is part of your project's settings, not the request — you set it once and every check_badwords request uses it. Manage it through your project settings (the same place you set default checks). Because the request only carries the check_badwords toggle, you can keep your word list private and change it without touching client code.
If check_badwords is on but your project has no words configured, the check is skipped and no badwords result is returned.
What matching does it use?
The match is a case-insensitive substring search: a listed word spam matches spam, SPAM, and also spammer. Choose list entries with that in mind — add whole phrases when you want to avoid matching inside larger words, and remember short entries can match unintended substrings.
Usage
Badword filtering does not have its own quota — like the text checks, it runs as part of a /api/v2/check call, and each call counts once against your monthly API-request allowance. See Authentication and API Keys for how quota and rate limits work.
Best practices
Combine with model-based checks
Use the custom list for exact, policy-driven terms and let check_spam, check_sentiment, and check_images handle the fuzzy categories. One /check call can run all of them together — see Text Analysis.
Act on the matched words
matched_words tells you exactly what tripped the filter, which is useful for moderator context and for tuning your list:
const result = await check({ text }, { check_badwords: true });
const bad = result.results?.badwords;
if (bad?.hit) {
await blockAndLog(text, bad.matched_words);
}
Review the list regularly
A static list goes stale as communities and language shift. Periodically review which entries actually fire (from matched_words in your logs) and prune ones that only cause false positives.
Integration example
import os
import requests
def check_badwords(text):
response = requests.post(
'https://api.discuse.com/api/v2/check',
headers={
'Content-Type': 'application/json',
'X-API-Key': os.environ['DISCUSE_API_KEY']
},
json={'content': {'text': text}, 'settings': {'check_badwords': True}}
)
return response.json()
Ready to enforce your own word policy? Get started with Discuse.