One on the page, one on your server. Examples for the seven most common backend languages. The token shape is reCAPTCHA-compatible — if you already validate g-recaptcha-response, this works.
Sign up, then Sites → Add a site. You'll get a sitekey (public, embedded on your page) and a secret (private, used by your server). Sitekeys look like 0x9a7f4b2c…; secrets are 66 characters.
Drop the loader script into your HTML and add a .trustedcaptcha element wherever you want the widget. It auto-renders when the DOM is ready.
<script src="https://cdn.trustedcaptcha.com/widget/v1/api.js" async defer></script>
<form action="/signup" method="POST">
<input name="email" type="email" required>
<div class="trustedcaptcha"
data-sitekey="0x9a7f4b2c..."
data-theme="auto"></div>
<button type="submit">Sign up</button>
</form>
When the user passes, the widget fills a hidden input named trustedcaptcha-response (plus g-recaptcha-response, h-captcha-response, cf-turnstile-response for compatibility) with a 40-byte token. Submit the form normally.
POST to the verify endpoint with your secret and the token. Returns reCAPTCHA-compatible JSON.
$response = file_get_contents(
'https://challenges.trustedcaptcha.com/api/v1/siteverify'
. '?secret=' . urlencode($SECRET)
. '&response=' . urlencode($_POST['trustedcaptcha-response'])
. '&remoteip=' . urlencode($_SERVER['REMOTE_ADDR'])
);
$result = json_decode($response, true);
if (!$result['success']) {
http_response_code(400);
exit('Captcha verification failed');
}
// $result['score'] is the risk score (0.0 = bot, 1.0 = human)
const r = await fetch('https://challenges.trustedcaptcha.com/api/v1/siteverify', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
secret: process.env.TC_SECRET,
response: req.body['trustedcaptcha-response'],
remoteip: req.ip,
}),
});
const result = await r.json();
if (!result.success) return res.status(400).send('Captcha verification failed');
// result.score
import requests
r = requests.post('https://challenges.trustedcaptcha.com/api/v1/siteverify', data={
'secret': os.environ['TC_SECRET'],
'response': request.form['trustedcaptcha-response'],
'remoteip': request.remote_addr,
})
result = r.json()
if not result['success']:
abort(400, 'Captcha verification failed')
# result['score']
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://challenges.trustedcaptcha.com/api/v1/siteverify')
res = Net::HTTP.post_form(uri, {
'secret' => ENV['TC_SECRET'],
'response' => params['trustedcaptcha-response'],
'remoteip' => request.remote_ip,
})
result = JSON.parse(res.body)
halt 400, 'Captcha verification failed' unless result['success']
resp, _ := http.PostForm("https://challenges.trustedcaptcha.com/api/v1/siteverify", url.Values{
"secret": {os.Getenv("TC_SECRET")},
"response": {r.FormValue("trustedcaptcha-response")},
"remoteip": {r.RemoteAddr},
})
defer resp.Body.Close()
var result struct {
Success bool `json:"success"`
Score float64 `json:"score"`
}
json.NewDecoder(resp.Body).Decode(&result)
if !result.Success {
http.Error(w, "Captcha verification failed", 400)
return
}
curl -X POST https://challenges.trustedcaptcha.com/api/v1/siteverify \
-d "secret=YOUR_SECRET" \
-d "response=USER_TOKEN" \
-d "remoteip=USER_IP"
# Response:
# {"success":true, "score":0.92, "mode":"smart", "hostname":"example.com",
# "challenge_ts":"2026-05-11T09:00:00+00:00", "action":null, "error_codes":[]}