Getting Started

Server-Side Validation

Validate CAPTCHA tokens on your server

After a user completes the CAPTCHA challenge, you must validate the token on your server before processing the form submission.

Validation Endpoint#

Send a POST request to validate the token:

POST https://challenge.captchacat.com/validate_token
Content-Type: application/json

Request Body

{
  "api_key": "YOUR_API_KEY",
  "token": "TOKEN_FROM_FORM"
}
FieldTypeDescription
api_keystringYour secret API key from the dashboard
tokenstringThe captchacat-token value from the form submission

Response

Status CodeDescription
200 OKToken is valid
400 Bad RequestToken is invalid, expired, or already used

Code Examples#

Node.js / Express

const express = require('express');
const app = express();
 
app.use(express.urlencoded({ extended: true }));
 
app.post('/submit', async (req, res) => {
  const token = req.body['captchacat-token'];
 
  if (!token) {
    return res.status(400).json({ error: 'CAPTCHA token missing' });
  }
 
  const response = await fetch('https://challenge.captchacat.com/validate_token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      api_key: process.env.CAPTCHA_CAT_API_KEY,
      token: token
    })
  });
 
  if (!response.ok) {
    return res.status(400).json({ error: 'CAPTCHA validation failed' });
  }
 
  // Token is valid - process the form
  // ...
 
  res.json({ success: true });
});

Python / Flask

import requests
from flask import Flask, request, jsonify
 
app = Flask(__name__)
 
@app.route('/submit', methods=['POST'])
def submit():
    token = request.form.get('captchacat-token')
 
    if not token:
        return jsonify({'error': 'CAPTCHA token missing'}), 400
 
    response = requests.post(
        'https://challenge.captchacat.com/validate_token',
        json={
            'api_key': CAPTCHA_CAT_API_KEY,
            'token': token
        }
    )
 
    if response.status_code != 200:
        return jsonify({'error': 'CAPTCHA validation failed'}), 400
 
    # Token is valid - process the form
    # ...
 
    return jsonify({'success': True})

Python / Django

import requests
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.conf import settings
 
@require_POST
def submit(request):
    token = request.POST.get('captchacat-token')
 
    if not token:
        return JsonResponse({'error': 'CAPTCHA token missing'}, status=400)
 
    response = requests.post(
        'https://challenge.captchacat.com/validate_token',
        json={
            'api_key': settings.CAPTCHA_CAT_API_KEY,
            'token': token
        }
    )
 
    if response.status_code != 200:
        return JsonResponse({'error': 'CAPTCHA validation failed'}, status=400)
 
    # Token is valid - process the form
    # ...
 
    return JsonResponse({'success': True})

PHP

<?php
$token = $_POST['captchacat-token'] ?? null;
 
if (!$token) {
    http_response_code(400);
    echo json_encode(['error' => 'CAPTCHA token missing']);
    exit;
}
 
$response = file_get_contents('https://challenge.captchacat.com/validate_token', false, stream_context_create([
    'http' => [
        'method' => 'POST',
        'header' => 'Content-Type: application/json',
        'content' => json_encode([
            'api_key' => getenv('CAPTCHA_CAT_API_KEY'),
            'token' => $token
        ])
    ]
]));
 
$httpCode = $http_response_header[0];
if (strpos($httpCode, '200') === false) {
    http_response_code(400);
    echo json_encode(['error' => 'CAPTCHA validation failed']);
    exit;
}
 
// Token is valid - process the form
// ...
 
echo json_encode(['success' => true]);

Go

package main
 
import (
    "bytes"
    "encoding/json"
    "net/http"
    "os"
)
 
func submitHandler(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    token := r.FormValue("captchacat-token")
 
    if token == "" {
        http.Error(w, "CAPTCHA token missing", http.StatusBadRequest)
        return
    }
 
    payload, _ := json.Marshal(map[string]string{
        "api_key": os.Getenv("CAPTCHA_CAT_API_KEY"),
        "token":   token,
    })
 
    resp, err := http.Post(
        "https://challenge.captchacat.com/validate_token",
        "application/json",
        bytes.NewBuffer(payload),
    )
 
    if err != nil || resp.StatusCode != http.StatusOK {
        http.Error(w, "CAPTCHA validation failed", http.StatusBadRequest)
        return
    }
 
    // Token is valid - process the form
    // ...
 
    w.Header().Set("Content-Type", "application/json")
    w.Write([]byte(`{"success": true}`))
}

Ruby / Rails

class FormsController < ApplicationController
  def submit
    token = params['captchacat-token']
 
    if token.blank?
      return render json: { error: 'CAPTCHA token missing' }, status: :bad_request
    end
 
    response = HTTParty.post(
      'https://challenge.captchacat.com/validate_token',
      headers: { 'Content-Type' => 'application/json' },
      body: {
        api_key: ENV['CAPTCHA_CAT_API_KEY'],
        token: token
      }.to_json
    )
 
    unless response.success?
      return render json: { error: 'CAPTCHA validation failed' }, status: :bad_request
    end
 
    # Token is valid - process the form
    # ...
 
    render json: { success: true }
  end
end

Best Practices#

Always Validate Server-Side

Never trust client-side validation alone. Always validate the token on your server before processing any form submission.

Keep Your API Key Secret

  • Never expose your API key in client-side code
  • Use environment variables to store the key
  • Rotate keys if they are accidentally exposed

Handle Validation Failures Gracefully

  • Display a user-friendly error message
  • Allow users to retry the CAPTCHA
  • Log failures for monitoring

Validate Before Processing

Always validate the CAPTCHA token before performing any database operations or sending emails. This prevents bots from triggering expensive operations.

// Good: Validate first
const isValid = await validateCaptcha(token);
if (!isValid) {
  return res.status(400).json({ error: 'Invalid CAPTCHA' });
}
await saveToDatabase(formData);
 
// Bad: Don't do this
await saveToDatabase(formData);
const isValid = await validateCaptcha(token);

Token Properties

  • Tokens are single-use and expire after validation
  • Tokens have a limited lifetime (typically a few minutes)
  • Each token is bound to a specific site key

Troubleshooting#

Token Always Invalid

  • Verify your API key is correct
  • Check that you're using the production API key, not a test key
  • Ensure the token hasn't expired (validate promptly after form submission)

Missing Token

  • Verify the widget is properly initialized
  • Check that the form contains a captchacat-token hidden field after verification
  • Ensure the widget's sitekey matches your site

Network Errors

  • Ensure your server can reach the CAPTCHA Cat validation endpoint
  • Check for firewall rules blocking outbound requests
  • Implement retry logic for transient failures

Built with precision. Designed for developers.