Quickstart
simplecaptcha protects your forms invisibly: no image puzzles, no clicks, no cookies, no consent banner. Integration is two steps — embed the widget and verify the token server-side. Both are required; without server-side verification there is no protection at all.
1. Embed the widget
<form method="post" action="/contact" data-simplecaptcha>…</form>
<script
src="https://api.simplecaptcha.de/v1/widget.js"
data-sitekey="sck_yoursitekey"
defer
></script>
The widget starts solving invisibly on the visitor's first interaction with
the form and stores the result in a hidden simplecaptcha-response field.
If the form is submitted before solving finishes, the widget briefly holds
the submit and re-submits automatically once the token is ready. Forms added
to the page after load (React/SPA checkouts) are picked up automatically.
Tokens are single-use: after each submit the widget solves a fresh one in the background, so a retried submission (failed validation, declined payment) verifies again.
JavaScript events (optional, for SPAs)
form.addEventListener('simplecaptcha:solved', () => { … })
form.addEventListener('simplecaptcha:error', (e) => console.warn(e.detail))
form.addEventListener('simplecaptcha:expired', () => { … })
If your code submits the token programmatically (fetch/XHR instead of a form
POST), dispatch simplecaptcha:consume on the form after reading the token —
the widget then solves a replacement:
form.dispatchEvent(new CustomEvent('simplecaptcha:consume'))
2. Verify server-side
Check the simplecaptcha-response field on every submit against
/v1/siteverify — with your secret key (scs_…), never in the frontend:
curl -X POST https://api.simplecaptcha.de/v1/siteverify \
-d "secret=scs_yoursecret" \
-d "response=TOKEN_FROM_FORM"
// Node — npm install @simplecaptcha/node
import { SimpleCaptcha } from '@simplecaptcha/node'
const captcha = new SimpleCaptcha({ secret: process.env.SIMPLECAPTCHA_SECRET })
const result = await captcha.verify(req.body['simplecaptcha-response'])
if (!result.success) return res.status(400).send('Captcha failed')
// PHP — composer require simplecaptcha/simplecaptcha
$captcha = new \SimpleCaptcha\SimpleCaptcha($_ENV['SIMPLECAPTCHA_SECRET']);
$result = $captcha->verify($_POST['simplecaptcha-response'] ?? null);
if (!$result->success) { http_response_code(400); exit('Captcha failed'); }
3. Testing without real keys
Create a test key in the dashboard: it works on localhost, accepts any
token, and is never billed. Responses include "is_test": true.
Important notes
- Every token is single-use. A second
/siteverifycall with the same token returnstimeout-or-duplicate. Verify exactly once. - Handle error codes — see error codes, especially
quota-exceeded(recommended pattern). - No JavaScript means no token — see handling missing tokens.