One API call with the evidence, nothing to deploy, and the accuracy measured in public.
CleanSpeak, from the makers of FusionAuth, is a moderation platform: filtering, moderator queues, user reputation, reporting, and the option to run it on your own servers.
CleanSpeak is not on the benchmark, so it has no bar and this page makes no claim about its accuracy. When you compare, ask for toxic text caught at a stated false-alarm rate, on text nobody tuned on.
The private test set, 9,029 texts labelled blind, read once on 2026-09-28, engine 2026-09-29. How it was measured
The real engine, live. Type a disguised word and watch it read, named and judged; type an innocent one and watch it pass.
Every answer carries the same evidence through the API: the words, where they were, how they were disguised and a censored version.
01
A free key and one request. The rules answer inside the Worker, at the edge.
5ms
at p99 inside the Worker, for a check the rules answer
Our own measurement in workerd, 300 requests, 2026-09-28
02
Named evasion tags and a censored version come back with every check.
username
xXsh1tXxshit
leetspeakreview ยท strong
Shown as xX****Xx
03
Engine versions are dated and immutable, and the changelog says what each one changed.
The decision and its evidence from one API call: nothing to deploy, no text stored.
A test key is free, with a plan or without one. One request, in the language you use, and the answer carries the evidence.
curl -X POST https://api.unprofane.com/v1/check \
-H "Authorization: Bearer up_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"xXsh1tXx","context":"username","policy":"teen"}'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": "xXsh1tXx",
"context": "username",
"policy": "teen"
}),
});
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": "xXsh1tXx",
"context": "username",
"policy": "teen",
},
timeout=5.0,
)
result = r.json()req, _ := http.NewRequest("POST", "https://api.unprofane.com/v1/check",
strings.NewReader(`{"text":"xXsh1tXx","context":"username","policy":"teen"}`))
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":"xXsh1tXx","context":"username","policy":"teen"}',
]]));
$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":"xXsh1tXx","context":"username","policy":"teen"}'
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\":\"xXsh1tXx\",\"context\":\"username\",\"policy\":\"teen\"}", 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\":\"xXsh1tXx\",\"context\":\"username\",\"policy\":\"teen\"}")Free test keys, 100 checks a day, no card. Live from $9 a month. Every answer shows its evidence.