logo
captchaAPI

5-minute integration

Four steps: create a project, drop in the script, mark your form, and verify on the server. If you already have a project, skip to step 2.

On WordPress? Do step 1 to get your keys, then install the captchaapi.eu plugin and skip the rest. It protects the login, registration, lost-password, and comment forms plus Contact Form 7 - activate it, paste your site key and secret key, and choose which forms to protect. No script tag, no markup, no backend code. Get it from the WordPress.org plugin directory.

1. Create a project and grab your keys

  1. Sign in and go to Projects.
  2. Click New Project, give it a name, and optionally add the domain(s) you'll embed the widget on. Leaving Allowed Domains empty means any origin can use the site key.
  3. On the project detail page you'll now see two keys:
    • Site Key - public. Goes into your HTML.
    • Secret Key - private. Shown once here after creation; goes into your backend config (not your HTML). You can re-reveal it later with password confirmation.

Copy the secret key somewhere durable (password manager, environment variable) before you navigate away. To reveal it again you'll need to enter your account password.

2. Include the script

Add one tag to your page, ideally just before </body>:

<script>window.CAPTCHA_SITE_KEY = 'YOUR_SITE_KEY';</script>
<script src="https://captchaapi.eu/captcha.js" defer></script>

The script auto-initialises every form that carries the data-captcha attribute. Nothing runs until the user interacts with that form.

3. Mark your form

Add data-captcha to the form, and optionally a [data-captcha-status] element to show the protection state to the user.

<form method="POST" action="/contact" data-captcha>
    <input name="email" type="email" required>
    <textarea name="message" required></textarea>

    <span data-captcha-status></span>

    <button type="submit">Send</button>
</form>

When the form submits, the widget inserts a hidden input named captchaapi_response. Its value is an opaque string your backend sends to the verify endpoint. You don't need to add it yourself.

4. Verify on your server

On Laravel? Skip step 4 and install captchaapi/laravel - composer require captchaapi/laravel gives you the validation rule, Blade component, and Livewire trait pre-wired. See the Laravel section in backend examples.

Put your secret key in an environment variable - never in code or client-side HTML. It's a single key, format sk_live_...:

CAPTCHA_SECRET_KEY=sk_live_your_secret_here

On the endpoint that receives the form, read captchaapi_response and make one server-to-server call to the verify endpoint. Send your secret key as a Bearer token and let the form through only when success is true:

<?php

$response = $_POST['captchaapi_response'] ?? '';

$ch = curl_init('https://captchaapi.eu/api/v1/captcha/verify');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . getenv('CAPTCHA_SECRET_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode(['response' => $response]),
]);

$body = curl_exec($ch);
curl_close($ch);

$result = json_decode($body, true);

if (! is_array($result) || ($result['success'] ?? false) !== true) {
    http_response_code(422);
    exit('Captcha failed.');
}

// proceed with the submission

HTTP 200 does not mean pass - always read the success field. Single-use and replay protection are enforced on our side: a given captchaapi_response verifies exactly once, so there's no replay cache for you to run.

That's it. Submissions with a missing or invalid response fail the check - reject them. See Backend examples for ready-made snippets in Laravel, plain PHP, Node.js, Python, and Ruby.

Tip: the verify response's over_limit field is true when the project is past its monthly challenge quota. success still comes back true (visitors continue to be served on the same baseline difficulty curve as in-quota traffic) so your users aren't punished - it's a signal to upgrade.

Next: full API reference · widget configuration · backend examples.