Scarnox · Blog

Solver de captcha em PHP com cURL: exemplo funcional (2026)

O PHP precisa apenas da extensão cURL, que vem com a maioria das instalações, para resolver um captcha. Você envia um POST JSON para a API da Scarnox, lê solution.token da resposta e envia esse token ao formulário do site. Este guia percorre o fluxo completo em PHP para o PopularCaptcha, que a Scarnox resolve hoje de forma proxyless.

Comece com crédito de teste grátis, sem cartão e teste o script em uma página que você controla antes de usá-lo em qualquer outro lugar.

Requisitos

A função solver

Esta função envia um POST para a API, verifica errorId e devolve o token. Ela lança uma exceção em qualquer falha, para que quem chama decida se deve tentar de novo:

<?php
declare(strict_types=1);

const SCARNOX_API_KEY = 'scarnox_YOURAPIKEY';
const SCARNOX_API_URL = 'https://api.scarnox.com/api/tasks/create';

function solveCaptcha(string $sitekey, string $pageUrl, bool $invisible = false): string
{
    $body = [
        'captcha_type' => 'PopularCaptchaTaskProxyless',
        'site_key'     => $sitekey,
        'site_url'     => $pageUrl,
    ];
    if ($invisible) {
        $body['is_invisible'] = true;
    }

    $ch = curl_init(SCARNOX_API_URL);
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => json_encode($body, JSON_THROW_ON_ERROR),
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . SCARNOX_API_KEY,
            'Content-Type: application/json',
        ],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 185, // a chamada retorna quando o token está pronto
    ]);

    $raw = curl_exec($ch);
    if ($raw === false) {
        $error = curl_error($ch);
        throw new RuntimeException('cURL error: ' . $error);
    }

    $data = json_decode((string) $raw, true);
    if (!is_array($data)) {
        throw new RuntimeException('Response was not JSON');
    }
    if (($data['errorId'] ?? 1) !== 0) {
        throw new RuntimeException(
            ($data['errorCode'] ?? 'ERROR') . ': ' . ($data['errorDescription'] ?? 'unknown')
        );
    }

    return (string) $data['solution']['token'];
}

O cabeçalho Authorization usa o prefixo Bearer. O timeout é de 185 segundos, porque a API fica bloqueada até o token estar pronto, sem etapa de polling.

Refazendo falhas transitórias

Uma resolução com falha não é cobrada, então algumas retentativas saem baratas. Coloque um loop em volta da chamada e aguarde um pouco mais após cada falha:

function solveWithRetry(string $sitekey, string $pageUrl, int $attempts = 3): string
{
    $lastError = null;
    for ($i = 1; $i <= $attempts; $i++) {
        try {
            return solveCaptcha($sitekey, $pageUrl);
        } catch (RuntimeException $e) {
            $lastError = $e;
            fwrite(STDERR, "Attempt $i failed: " . $e->getMessage() . PHP_EOL);
            sleep($i);
        }
    }
    throw $lastError;
}

Para uma política completa de erros, incluindo quais falhas fazem parar, veja tratamento de erros do solver de captcha.

Muitos sites vinculam o captcha à sessão que carregou o formulário. Um arquivo de cookies permite que o cURL envie os mesmos cookies no GET e no POST. A função abaixo envia o token ao formulário da página:

function submitToken(string $pageUrl, string $token): string
{
    $cookieFile = sys_get_temp_dir() . '/scarnox-cookies.txt';

    // GET primeiro, para que o site defina o cookie de sessão.
    $ch = curl_init($pageUrl);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_COOKIEFILE     => $cookieFile,
        CURLOPT_COOKIEJAR      => $cookieFile,
        CURLOPT_TIMEOUT        => 60,
    ]);
    curl_exec($ch);

    // Depois, envie o formulário por POST com o token nos dois campos do captcha.
    $fields = http_build_query([
        'email'                 => 'you@example.com',
        'h-captcha-response'    => $token,
        'g-recaptcha-response'  => $token,
    ]);

    curl_setopt_array($ch, [
        CURLOPT_POST       => true,
        CURLOPT_POSTFIELDS => $fields,
        CURLOPT_TIMEOUT    => 60,
    ]);
    $body = curl_exec($ch);
    if ($body === false) {
        throw new RuntimeException('Form submit failed: ' . curl_error($ch));
    }

    return (string) $body;
}

Dois pontos são importantes. O GET roda antes da resolução para que o cookie esteja no lugar. E o token é enviado logo depois da resolução, porque expira em cerca de dois minutos. Coloque a chamada de resolução logo antes desta função, e não no início de um lote longo.

O arquivo de cookies é compartilhado por todas as chamadas no mesmo processo, então dê a cada conta ou sessão o seu próprio arquivo se você rodar vários cadastros em paralelo. Caso contrário, uma sessão pode sobrescrever os cookies de outra, e o segundo POST vai falhar por motivos que parecem um problema de captcha, mas não são.

Juntando tudo

$pageUrl = 'https://example.com/signup';
$token   = solveWithRetry('YOUR-SITEKEY-UUID', $pageUrl);
$html    = submitToken($pageUrl, $token);

echo strlen($html) . " bytes returned\n";

Execute pela linha de comando:

php signup.php

Widgets invisíveis

Se o widget for invisível, chame solveCaptcha($sitekey, $pageUrl, true). O campo is_invisible é então enviado junto com a requisição. Algumas páginas também precisam de um callback em JavaScript chamado com o token, o que um script do lado do servidor não consegue disparar. Nesse caso, use uma ferramenta de navegador. O guia captcha invisível cobre os casos.

Tempo e expiração

O token é válido por cerca de dois minutos. Mantenha a resolução e o envio próximos. Expiração de token de captcha explica como distinguir um token expirado de um token rejeitado.

Faça scraping com responsabilidade

Se o seu script PHP faz scraping de páginas públicas, verifique o robots.txt e os termos do site antes de começar. Adicione um intervalo entre as requisições e não guarde dados pessoais de que você não precisa. Resolver um captcha não muda o que você tem permissão para coletar. Para o lado legal, veja web scraping é legal.

Preços

Você paga por resolução bem-sucedida. Resoluções com falha não são cobradas. Veja os preços atuais e comece com crédito de teste grátis, sem cartão.

Próximos passos