Tutorials

Solve CAPTCHAs with PHP Using CaptchaAI

PHP powers the majority of the web. This guide shows you how to integrate CaptchaAI into any PHP project using cURL — no SDKs or frameworks required.

Requirements

Requirement Details
PHP 7.4+
cURL extension Enabled (default in most installs)
CaptchaAI API key Get one here

Helper Functions

Create a reusable solver class:

<?php

class CaptchaAI
{
    private string $apiKey;
    private string $baseUrl = 'https://ocr.captchaai.com';

    public function __construct(string $apiKey)
    {
        $this->apiKey = $apiKey;
    }

    /**

     * Submit a CAPTCHA task and return the task ID.
     */
    public function submit(array $params): string
    {
        $params['key'] = $this->apiKey;
        $url = $this->baseUrl . '/in.php?' . http_build_query($params);

        $response = $this->httpGet($url);

        if (!str_starts_with($response, 'OK|')) {
            throw new RuntimeException("Submit failed: {$response}");
        }

        return explode('|', $response)[1];
    }

    /**

     * Poll for the result with a timeout.
     */
    public function poll(string $taskId, int $timeout = 300): string
    {
        $deadline = time() + $timeout;
        $url = $this->baseUrl . '/res.php?' . http_build_query([
            'key'    => $this->apiKey,
            'action' => 'get',
            'id'     => $taskId,
        ]);

        while (time() < $deadline) {
            sleep(5);
            $response = $this->httpGet($url);

            if ($response === 'CAPCHA_NOT_READY') {
                continue;
            }

            if (str_starts_with($response, 'OK|')) {
                return explode('|', $response, 2)[1];
            }

            throw new RuntimeException("Solve failed: {$response}");
        }

        throw new RuntimeException("Timeout after {$timeout}s for task {$taskId}");
    }

    /**

     * Submit and poll in one call.
     */
    public function solve(array $params, int $timeout = 300): string
    {
        $taskId = $this->submit($params);
        return $this->poll($taskId, $timeout);
    }

    /**

     * Check account balance.
     */
    public function getBalance(): float
    {
        $url = $this->baseUrl . '/res.php?' . http_build_query([
            'key'    => $this->apiKey,
            'action' => 'getbalance',
        ]);

        return (float) $this->httpGet($url);
    }

    private function httpGet(string $url): string
    {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 30,
            CURLOPT_SSL_VERIFYPEER => true,
        ]);

        $response = curl_exec($ch);

        if ($response === false) {
            $error = curl_error($ch);
            curl_close($ch);
            throw new RuntimeException("HTTP request failed: {$error}");
        }

        curl_close($ch);
        return $response;
    }
}

Solve reCAPTCHA v2

$solver = new CaptchaAI('YOUR_API_KEY');

$token = $solver->solve([
    'method'    => 'userrecaptcha',
    'googlekey' => '6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
    'pageurl'   => 'https://example.com/login',
]);

echo "Token: {$token}\n";

Solve reCAPTCHA v3

$token = $solver->solve([
    'method'    => 'userrecaptcha',
    'googlekey' => '6Le-wvkS...',
    'pageurl'   => 'https://example.com',
    'version'   => 'v3',
    'action'    => 'submit',
]);

Solve Cloudflare Turnstile

$token = $solver->solve([
    'method'  => 'turnstile',
    'sitekey' => '0x4AAAAA...',
    'pageurl' => 'https://example.com',
]);

Solve Image CAPTCHAs

$imageData = base64_encode(file_get_contents('captcha.png'));

$text = $solver->solve([
    'method' => 'base64',
    'body'   => $imageData,
]);

echo "Text: {$text}\n";

Solve GeeTest v3

$result = $solver->solve([
    'method'     => 'geetest',
    'gt'         => '022c...',
    'challenge'  => 'abc...',
    'api_server' => 'api.geetest.com',
    'pageurl'    => 'https://example.com',
]);

// Parse GeeTest result
$parts = [];
foreach (explode(',', $result) as $item) {
    [$key, $value] = explode(':', $item, 2);
    $parts[$key] = $value;
}

echo "Challenge: {$parts['challenge']}\n";
echo "Validate: {$parts['validate']}\n";
echo "Seccode: {$parts['seccode']}\n";

Laravel Integration

Use the solver in a Laravel controller:

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class FormController extends Controller
{
    public function submit(Request $request)
    {
        $solver = new \CaptchaAI(config('services.captchaai.key'));

        $token = $solver->solve([
            'method'    => 'userrecaptcha',
            'googlekey' => config('services.captchaai.sitekey'),
            'pageurl'   => $request->url(),
        ]);

        // Use token in form submission
        $response = \Http::asForm()->post('https://target.com/submit', [
            'g-recaptcha-response' => $token,
            'other_field'          => $request->input('data'),
        ]);

        return response()->json(['success' => true]);
    }
}

Add your key to config/services.php:

'captchaai' => [
    'key' => env('CAPTCHAAI_API_KEY'),
],

Troubleshooting

Error Cause Fix
cURL error: SSL certificate problem Outdated CA bundle Update php.ini curl.cainfo path
Submit failed: ERROR_WRONG_USER_KEY Invalid API key Verify key from dashboard
Timeout after 300s Network or capacity issue Increase timeout; retry
cURL error: Could not resolve host DNS issue Check network connectivity

FAQ

Does this work with PHP 8+?

Yes. The code uses str_starts_with() which requires PHP 8.0+. For PHP 7.4, replace with substr($str, 0, 3) === 'OK|'.

Can I use Guzzle instead of cURL?

Yes. Replace the httpGet method with a Guzzle client call. The API parameters stay the same.

How do I handle concurrent solves in PHP?

Use curl_multi_exec for parallel HTTP requests, or process CAPTCHAs in a queue (Laravel Queue, Symfony Messenger).

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.