Profanity filtering users can't bypass.
It reads verlan, accents dropped or swapped and texting shorthand, and lets habite and salopette through.
te pu
« pute » whore
**** block · strong
Name your users' country too: gosses and tabernacle are read as Canada reads them (every word read by country).Each answer is the rules alone, the fast mode, checking a comment with language: "fr". In balanced mode, the default, the model reads what the rules leave open.
Five spellings of one insult, each read back to the same word.
Each one reads as
encule
fucker
blockall 5
Each spelling as typed, what the rules read in it, the tricks they named and their answer.
Digits for letters
3ncul3
reads asencule
leetspeakblock · strong
Dotted
e.n.c.u.l.e
reads asencule
separatorblock · strong
A Cyrillic с
сonnard
reads asconnard
homoglyphmixed_scriptblock · strong
Letters repeated
saloooope
reads assalope
repeated_charsblock · strong
A zero and a four
c0nn4rd
reads asconnard
leetspeakblock · strong
A Cyrillic е
putе
reads aspute
homoglyphmixed_scriptblock · strong
The letters French users type in place of the words.
A list matching inside words finds a rude string in each; the rules read the whole word.
A list matching inside words8 of 8blocked
Unprofane, rules alone8 of 8allow
The same French texts given to every system, and the open benchmark’s French items.
95.2%
Unprofane
Flagged 4.2% of clean French texts
65.3%
Perspective API
Flagged 2.1% of clean French texts
58.1%
OpenAI moderation
Flagged 6.3% of clean French texts
1.000F1 on the Scunthorpe Test’s 35 French items, rules aloneThe public split, run of , engine 2026-10-18. Every language’s score
Other APIs and French
Send language "fr" and a bare word reads as French writes it. Every language is still read.
Get your API key100 checks a day, no card
curl -X POST https://api.unprofane.com/v1/check \
-H "Authorization: Bearer up_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Va te faire foutre, connard","context":"comment","language":"fr"}'const res = await fetch('https://api.unprofane.com/v1/check', {
method: 'POST',
headers: { Authorization: 'Bearer up_test_YOUR_KEY', 'Content-Type': 'application/json' },
body: JSON.stringify({
"text": "Va te faire foutre, connard",
"context": "comment",
"language": "fr"
}),
});
const result = await res.json();import httpx
r = httpx.request(
"POST",
"https://api.unprofane.com/v1/check",
headers={"Authorization": "Bearer up_test_YOUR_KEY"},
json={
"text": "Va te faire foutre, connard",
"context": "comment",
"language": "fr",
},
timeout=5.0,
)
result = r.json()req, _ := http.NewRequest("POST", "https://api.unprofane.com/v1/check",
strings.NewReader(`{"text":"Va te faire foutre, connard","context":"comment","language":"fr"}`))
req.Header.Set("Authorization", "Bearer up_test_YOUR_KEY")
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)$res = file_get_contents('https://api.unprofane.com/v1/check', false, stream_context_create(['http' => [
'method' => 'POST',
'header' => "Authorization: Bearer up_test_YOUR_KEY\r\nContent-Type: application/json\r\n",
'content' => '{"text":"Va te faire foutre, connard","context":"comment","language":"fr"}',
]]));
$result = json_decode($res, true);require 'net/http'; require 'json'
uri = URI('https://api.unprofane.com/v1/check')
req = Net::HTTP::Post.new(uri, 'Authorization' => 'Bearer up_test_YOUR_KEY', 'Content-Type' => 'application/json')
req.body = '{"text":"Va te faire foutre, connard","context":"comment","language":"fr"}'
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
result = JSON.parse(res.body)using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "up_test_YOUR_KEY");
var req = new HttpRequestMessage(HttpMethod.Post, "https://api.unprofane.com/v1/check");
req.Content = new StringContent("{\"text\":\"Va te faire foutre, connard\",\"context\":\"comment\",\"language\":\"fr\"}", Encoding.UTF8, "application/json");
var res = await http.SendAsync(req);
var result = await res.Content.ReadFromJsonAsync<JsonElement>();var http := HTTPRequest.new()
add_child(http)
http.request_completed.connect(func(_r, _code, _h, body):
var result = JSON.parse_string(body.get_string_from_utf8())
print(result))
http.request("https://api.unprofane.com/v1/check",
PackedStringArray(["Authorization: Bearer up_test_YOUR_KEY", "Content-Type: application/json"]),
HTTPClient.METHOD_POST, "{\"text\":\"Va te faire foutre, connard\",\"context\":\"comment\",\"language\":\"fr\"}"){
"allowed": false,
"action": "block",
"severity": "strong",
"matches": [
{
"text": "Va te faire foutre",
"normalized": "va te faire foutre",
"category": "insult",
"severity": "strong"
},
{
"text": "connard",
"normalized": "connard",
"category": "insult",
"severity": "strong"
}
],
"censored": "******************, *******",
"language": {
"detected": "fr",
"confidence": 0.75
}
}Every language Unprofane covers is read on every call, French with its disguises included.
Get your API key