Integration Guide
Add CAPTCHA protection to your website in three steps.
1
Create an account & register your site
Register, create a workspace, add your domain, and verify ownership. After verification you receive a Client ID, API Key, and Client Secret.
⚠ The Client Secret is shown once at creation time. Store it securely — it is used only from your backend.
2
Add the widget to your page
The widget script requests a challenge from our API and renders it inside a container element. No external CDN — serve the snippet from your own domain or copy-paste it.
<!-- 1. Add a container where the CAPTCHA renders -->
<div id="captcha-widget"></div>
<!-- 2. Load the widget script (self-hosted on your site) -->
<script>
window.CAPTCHA_CONFIG = {
apiBase: "https://captcha.sabacore.ir",
apiKey: "YOUR_API_KEY", // from dashboard
container: "#captcha-widget",
onSuccess: function(challengeId) {
// store challengeId; send it with your form submission
document.getElementById("captcha-id").value = challengeId;
}
};
</script>
<script src="/js/captcha-widget.js" defer></script>
<!-- 3. Hidden field in your form -->
<input type="hidden" id="captcha-id" name="captcha_challenge_id">
<!-- Minimal HTML form example -->
<form method="POST" action="/your-form-handler">
<div id="captcha-widget"></div>
<input type="hidden" id="captcha-id" name="captcha_challenge_id">
<button type="submit">Submit</button>
</form>
3
Verify the solution on your server
When the user submits your form, your backend calls the verify endpoint. Never call verify from the browser — it requires your Client Secret.
resp, err := http.Post(
"https://captcha.sabacore.ir/v1/captcha/verify",
"application/json",
strings.NewReader(`{"challenge_id":"`+challengeID+`","solution":"`+solution+`"}`),
)
// add header: X-Client-Secret: YOUR_CLIENT_SECRET
$ch = curl_init("https://captcha.sabacore.ir/v1/captcha/verify");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-Client-Secret: YOUR_CLIENT_SECRET",
"Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"challenge_id" => $challengeId,
"solution" => $solution,
]),
]);
$result = json_decode(curl_exec($ch), true);
if ($result["valid"]) { /* proceed */ }
import requests
r = requests.post(
"https://captcha.sabacore.ir/v1/captcha/verify",
headers={"X-Client-Secret": "YOUR_CLIENT_SECRET"},
json={"challenge_id": challenge_id, "solution": solution},
)
if r.json()["valid"]:
pass # proceed
curl -X POST https://captcha.sabacore.ir/v1/captcha/verify \
-H "X-Client-Secret: YOUR_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{"challenge_id":"<id>","solution":"<answer>"}'
Verify response
// 200 OK
{ "valid": true }
// Error examples
{ "error": "challenge expired" } // 410
{ "error": "challenge consumed" } // 409
API reference
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /v1/health |
— | Liveness check |
| POST | /v1/captcha/issue |
X-API-Key |
Issue a challenge (from browser) |
| POST | /v1/captcha/verify |
X-Client-Secret |
Verify solution (backend only) |
Rate limits
| Endpoint | Limit |
|---|---|
/v1/captcha/issue |
60 req/min per IP · 6,000 req/hour per site |
/v1/captcha/verify |
120 req/min per site |
Questions? Open an issue or contact us via your dashboard.