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 /siteverify call with the same token returns timeout-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.