API Tutorials

CaptchaAI Callback URL Setup: Complete Webhook Guide

Instead of polling /res.php repeatedly, you can provide a callback URL. CaptchaAI sends the solved token directly to your server when ready. This eliminates polling overhead and reduces latency.


How Callbacks Work

Standard polling approach:
  Submit ──▶ Wait ──▶ Poll ──▶ Poll ──▶ Poll ──▶ Result
  (many requests, wasted time between polls)

Callback approach:
  Submit (with callback URL) ──▶ ... CaptchaAI solves ...
                                            │
  Your server receives POST ◀───────────────┘
  (one request, instant delivery)

Submit with Callback URL

Add the pingback parameter to your submit request:

import requests

API_KEY = "YOUR_API_KEY"

resp = requests.post("https://ocr.captchaai.com/in.php", data={
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": "SITE_KEY",
    "pageurl": "https://example.com",
    "pingback": "https://your-server.com/captcha-callback",
    "json": 1,
})

result = resp.json()
task_id = result["request"]
print(f"Task submitted: {task_id}")
# No polling needed — result comes via webhook

Supported for All CAPTCHA Types

# reCAPTCHA v2
data = {
    "key": API_KEY,
    "method": "userrecaptcha",
    "googlekey": "SITE_KEY",
    "pageurl": "https://example.com",
    "pingback": "https://your-server.com/callback",
    "json": 1,
}

# Turnstile
data = {
    "key": API_KEY,
    "method": "turnstile",
    "sitekey": "SITE_KEY",
    "pageurl": "https://example.com",
    "pingback": "https://your-server.com/callback",
    "json": 1,
}

# Image CAPTCHA
data = {
    "key": API_KEY,
    "method": "base64",
    "body": base64_image,
    "pingback": "https://your-server.com/callback",
    "json": 1,
}

Build a Callback Receiver (Flask)

from flask import Flask, request
import threading
import logging

app = Flask(__name__)
logger = logging.getLogger(__name__)

# Store results by task ID
results = {}
events = {}


@app.route("/captcha-callback", methods=["POST", "GET"])
def captcha_callback():
    """Receive solved CAPTCHA tokens from CaptchaAI."""
    # CaptchaAI sends parameters as query string or form data
    task_id = request.args.get("id") or request.form.get("id")
    code = request.args.get("code") or request.form.get("code")

    if not task_id or not code:
        logger.warning("Callback missing id or code")
        return "ERROR", 400

    logger.info("Received result for task %s", task_id)
    results[task_id] = code

    # Notify waiting threads
    event = events.get(task_id)
    if event:
        event.set()

    return "OK"


def wait_for_result(task_id, timeout=120):
    """Wait for a callback result."""
    event = threading.Event()
    events[task_id] = event

    if task_id in results:
        return results.pop(task_id)

    event.wait(timeout=timeout)

    if task_id in results:
        return results.pop(task_id)

    raise TimeoutError(f"No callback received for task {task_id}")


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Complete Workflow

import requests
import threading
import time

API_KEY = "YOUR_API_KEY"
CALLBACK_URL = "https://your-server.com/captcha-callback"


def solve_with_callback(sitekey, pageurl):
    """Submit CAPTCHA and wait for callback."""
    # Submit with callback URL
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "pingback": CALLBACK_URL,
        "json": 1,
    })
    result = resp.json()

    if result.get("status") != 1:
        raise RuntimeError(f"Submit failed: {result.get('request')}")

    task_id = result["request"]
    print(f"Task {task_id} submitted, waiting for callback...")

    # Wait for callback on the receiver
    token = wait_for_result(task_id, timeout=120)
    print(f"Token received via callback: {token[:50]}...")
    return token

Callback vs Polling Comparison

Factor Polling Callback
API requests Many (submit + N polls) 2 (submit + callback)
Latency Poll interval delay Instant delivery
Server load Client polls continuously CaptchaAI pushes result
Complexity Simple (client-only) Requires public endpoint
Firewall requirements Outbound only Must accept inbound POST
Best for Simple scripts Production pipelines

When to Use Callbacks

Scenario Recommended Approach
Quick script, few solves Polling
Production pipeline, high volume Callback
Serverless (Lambda, Cloud Functions) Polling (no persistent server)
Behind firewall, no public IP Polling
Microservice architecture Callback

Securing Your Callback Endpoint

Verify that callbacks actually come from CaptchaAI:

from flask import Flask, request, abort

app = Flask(__name__)

# Store submitted task IDs to validate callbacks
pending_tasks = set()


def submit_captcha(sitekey, pageurl):
    """Submit and track task ID."""
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "pingback": CALLBACK_URL,
        "json": 1,
    })
    task_id = resp.json()["request"]
    pending_tasks.add(task_id)
    return task_id


@app.route("/captcha-callback", methods=["POST", "GET"])
def secure_callback():
    """Validate callback before processing."""
    task_id = request.args.get("id") or request.form.get("id")

    # Reject unknown task IDs
    if task_id not in pending_tasks:
        abort(403)

    code = request.args.get("code") or request.form.get("code")
    pending_tasks.discard(task_id)
    results[task_id] = code

    return "OK"

Troubleshooting

Issue Cause Fix
No callback received Server not publicly accessible Use ngrok for testing, or deploy to cloud
Callback URL rejected URL not reachable from CaptchaAI Verify URL is publicly accessible
Partial results Some callbacks fail Log all callbacks, add retry/polling fallback
Duplicate callbacks Network retry Use task ID deduplication

FAQ

Does the callback URL need HTTPS?

HTTPS is recommended for production. HTTP works for testing but exposes tokens in transit.

What if my callback server is down?

CaptchaAI may retry the callback, but you should implement a polling fallback for reliability. Check results via /res.php if no callback is received within the expected time.

Can I use different callback URLs per request?

Yes. Each pingback parameter is per-request. Different CAPTCHA types or projects can use different callback endpoints.



Eliminate polling overhead — try CaptchaAI callbacks for instant token delivery.

Full Working Code

Complete runnable examples for this article in Python, Node.js, PHP, Go, Java, C#, Ruby, Rust, Kotlin & Bash.

View on GitHub →

Discussions (0)

No comments yet.