Captcha
Add animated captcha to protect your website forms from bots. Unique direction-based challenges that are easy for humans, hard for bots.
How It Works
Quick Start
Step 1: Go to Console → Captcha and create a new key.
Step 2: Add the widget to your HTML form:
<form action="/submit" method="POST">
<input type="text" name="email" placeholder="Email" />
<input type="password" name="password" placeholder="Password" />
<!-- ToolPix Captcha -->
<div data-tpx-captcha="YOUR_SITE_KEY"></div>
<button type="submit">Sign Up</button>
</form>
<!-- Load widget script (place before </body>) -->
<script src="https://toolpix.dev/api/v1/captcha/widget" defer></script>Step 3: Verify the captcha on your backend (see examples below).
Widget Options
| Attribute | Type | Description |
|---|---|---|
| data-tpx-captcha | string | Your site key (required) |
| data-tpx-dark | flag | Enable dark mode theme |
You can also initialize programmatically:
const captcha = new ToolPixCaptcha('#my-captcha', 'YOUR_SITE_KEY', {
dark: true,
onSuccess: (data) => {
console.log('Captcha solved!', data);
// data contains: { challengeId, answer, token, expiresAt }
}
});
// Check if solved
captcha.isSolved(); // true/false
// Get answer data for backend verification
captcha.getData(); // { challengeId, answer, token, expiresAt }
// Reset captcha
captcha.reset();API Reference
POST /api/v1/captcha/challenge
Request a new captcha challenge. Called automatically by the widget.
POST /api/v1/captcha/verify
Verify a captcha answer. Call this from your backend only — never expose your secret key to the client.
| Parameter | Type | Description |
|---|---|---|
| secretKey | string | Your secret key from Console (required) |
| challengeId | string | Challenge ID from captcha data |
| answer | string | User's answer (direction) |
| token | string | Server verification hash |
| expiresAt | number | Challenge expiry timestamp |
Response:
// Success
{ "success": true, "verificationToken": "abc123...", "timestamp": 1711800000000 }
// Failure
{ "success": false, "error": "Incorrect answer" }Backend Verification Examples
When the user submits your form, the captcha data is in a hidden input called tpx-captcha-data. Send it to our verify endpoint with your secret key.
Node.js / Express
app.post('/submit', async (req, res) => {
const captchaData = JSON.parse(req.body['tpx-captcha-data']);
const verify = await fetch('https://toolpix.dev/api/v1/captcha/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
secretKey: 'YOUR_SECRET_KEY',
...captchaData
})
});
const result = await verify.json();
if (!result.success) {
return res.status(400).json({ error: 'Captcha verification failed' });
}
// Captcha passed — process the form
// ...
});PHP
<?php
$captchaData = json_decode($_POST['tpx-captcha-data'], true);
$response = file_get_contents('https://toolpix.dev/api/v1/captcha/verify', false,
stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/json',
'content' => json_encode([
'secretKey' => 'YOUR_SECRET_KEY',
'challengeId' => $captchaData['challengeId'],
'answer' => $captchaData['answer'],
'token' => $captchaData['token'],
'expiresAt' => $captchaData['expiresAt'],
])
]
])
);
$result = json_decode($response, true);
if (!$result['success']) {
die('Captcha verification failed');
}
// Captcha passed — process the form
// ...
?>Python / Flask
import requests, json
from flask import Flask, request
app = Flask(__name__)
@app.route('/submit', methods=['POST'])
def submit():
captcha_data = json.loads(request.form.get('tpx-captcha-data', '{}'))
result = requests.post('https://toolpix.dev/api/v1/captcha/verify', json={
'secretKey': 'YOUR_SECRET_KEY',
**captcha_data
}).json()
if not result.get('success'):
return 'Captcha failed', 400
# Captcha passed — process the form
return 'Success!'Go
func submitHandler(w http.ResponseWriter, r *http.Request) {
captchaData := r.FormValue("tpx-captcha-data")
var data map[string]interface{}
json.Unmarshal([]byte(captchaData), &data)
data["secretKey"] = "YOUR_SECRET_KEY"
body, _ := json.Marshal(data)
resp, _ := http.Post(
"https://toolpix.dev/api/v1/captcha/verify",
"application/json",
bytes.NewBuffer(body),
)
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
if result["success"] != true {
http.Error(w, "Captcha failed", 400)
return
}
// Captcha passed — process the form
fmt.Fprint(w, "Success!")
}Ruby / Rails
require 'net/http'
require 'json'
def verify_captcha(captcha_data_json)
data = JSON.parse(captcha_data_json)
data['secretKey'] = 'YOUR_SECRET_KEY'
uri = URI('https://toolpix.dev/api/v1/captcha/verify')
res = Net::HTTP.post(uri, data.to_json,
'Content-Type' => 'application/json')
result = JSON.parse(res.body)
result['success'] == true
end
# In your controller:
unless verify_captcha(params['tpx-captcha-data'])
render plain: 'Captcha failed', status: 400
return
endC# / .NET
using System.Text.Json;
[HttpPost("submit")]
public async Task<IActionResult> Submit([FromForm] string tpxCaptchaData)
{
var captchaData = JsonSerializer.Deserialize<Dictionary<string, object>>(tpxCaptchaData);
captchaData["secretKey"] = "YOUR_SECRET_KEY";
var client = new HttpClient();
var response = await client.PostAsJsonAsync(
"https://toolpix.dev/api/v1/captcha/verify", captchaData);
var result = await response.Content.ReadFromJsonAsync<Dictionary<string, object>>();
if (result?["success"]?.ToString() != "True")
return BadRequest("Captcha failed");
// Captcha passed — process the form
return Ok("Success!");
}Java / Spring
@PostMapping("/submit")
public ResponseEntity<String> submit(@RequestParam("tpx-captcha-data") String captchaJson) {
Map<String, Object> data = new ObjectMapper().readValue(captchaJson, Map.class);
data.put("secretKey", "YOUR_SECRET_KEY");
RestTemplate rest = new RestTemplate();
Map result = rest.postForObject(
"https://toolpix.dev/api/v1/captcha/verify", data, Map.class);
if (!Boolean.TRUE.equals(result.get("success"))) {
return ResponseEntity.badRequest().body("Captcha failed");
}
// Captcha passed
return ResponseEntity.ok("Success!");
}Dark Mode
Add data-tpx-dark attribute for dark theme:
<div data-tpx-captcha="YOUR_SITE_KEY" data-tpx-dark></div>Domain Restrictions
In Console → Captcha, you can restrict which domains can use your captcha key. Only requests from listed domains will be accepted. Leave empty to allow all domains (useful for development).
Security Notes
- Never expose your secret key — only use it on your backend server.
- Challenges expire after 2 minutes. If expired, a new challenge is generated automatically.
- Each challenge can only be verified once.
- Wrong answers trigger a new challenge with a different animation.
- Set allowed domains to prevent unauthorized use of your captcha key.
Ready to get started?
Create your captcha key and protect your forms in minutes.
Open Console → Captcha