Profanity filtering users can't bypass.
Find swear words, slurs and disguised profanity in any text, and copy it censored.
Insults, slurs and threats blocked; swearing held for a person to look at.
Each trick is undone before the words are read, and named in the answer. Pick one to try it.
Caught, by trick: the Scunthorpe Test, rules alone, 2026-10-20
A list misses the disguised word, and refuses the innocent one that happens to contain it.
Moving to Scunthorpe next month
The list finds a slur inside a town.
Blocks it
allow
f.u.c.k off, nobody wants you here
Four dots and the list sees no word.
Misses it
block
ƒυ¢к off
A hooked f, a Greek u, a cent sign and a Cyrillic k.
Misses it
review
I know where you live
A threat in ordinary words.
Misses it
block
1 innocent blocked3 missed
1 innocent let through3 caught
More abuse caught than Perspective or OpenAI, each allowed the same share of false alarms.
89.0%
of toxic text caught, with 7.0% of clean text flagged
One request returns the verdict, each word as read, the trick that hid it and a censored copy.
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":"what a b i t c h move","context":"comment"}'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": "what a b i t c h move",
"context": "comment"
}),
});
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": "what a b i t c h move",
"context": "comment",
},
timeout=5.0,
)
result = r.json()req, _ := http.NewRequest("POST", "https://api.unprofane.com/v1/check",
strings.NewReader(`{"text":"what a b i t c h move","context":"comment"}`))
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":"what a b i t c h move","context":"comment"}',
]]));
$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":"what a b i t c h move","context":"comment"}'
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\":\"what a b i t c h move\",\"context\":\"comment\"}", 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\":\"what a b i t c h move\",\"context\":\"comment\"}"){
"allowed": false,
"action": "block",
"severity": "strong",
"evasion": [
"separator",
"concatenation"
],
"matches": [
{
"text": "b i t c h",
"normalized": "bitch",
"category": "insult"
}
],
"censored": "what a ********* move"
}