API Tutorials

Resolva CAPTCHA de imagem com Node.js e CaptchaAI

Quando o formulário exibe uma imagem com letras tortas, não existe token de widget para pedir: alguém precisa ler os pixels e digitar o texto. A resposta prática em Node.js é enviar essa imagem ao endpoint de OCR da CaptchaAI e receber a string de volta em segundos, sem modelo local nem biblioteca de visão computacional no seu servidor. Abaixo estão os dois modos de envio, o polling do resultado, os parâmetros de precisão e um exemplo com Puppeteer.


Quando o OCR é a rota certa

CAPTCHAs de imagem — o tipo "normal", com texto distorcido — seguem vivos em portais de governo, sistemas legados e formulários internos que nunca migraram para reCAPTCHA. Não há sitekey nem pageurl: o desafio é um <img> na página.

Use a rota de OCR quando o desafio for uma imagem estática de texto ou uma expressão matemática. Widget da Google ou da Cloudflare na página pede outro fluxo — a CaptchaAI resolve reCAPTCHA v2/v3, Cloudflare Turnstile e Challenge, GeeTest v3, CAPTCHAs de imagem/OCR e desafios de grade de imagens, cada um com seu próprio method. Vale registrar o que não entra nessa lista: hCaptcha e FunCaptcha (Arkose Labs) não são suportados, e o GeeTest v4 está em breve, não disponível. CaptchaFox (beta), Friendly Captcha (beta) e Lemin (beta) existem apenas nesse estágio.

Mantenha o uso no escopo autorizado: ambiente próprio, staging, QA de formulários que a sua equipe controla. Havendo dados pessoais no fluxo, considere as obrigações da LGPD (ou do RGPD, em Portugal) antes de registrar imagens e respostas em log.


Antes de começar: o que você precisa

Item Valor
Chave de API CaptchaAI Obtida em captchaai.com
Node.js 14+
Bibliotecas axios, fs
Formato de imagem JPG, PNG ou GIF (100 bytes – 100 KB)

Guarde a chave em variável de ambiente, nunca no repositório. Na primeira integração, crie a conta e verifique o saldo na CaptchaAI antes de rodar o código.


Passo 1: envie a imagem em base64

O caminho mais direto é ler o arquivo, codificar em base64 e mandar tudo em uma requisição para in.php. A resposta traz o ID da tarefa — guarde-o, porque é com ele que você consulta o resultado.

const axios = require('axios');
const fs = require('fs');

const API_KEY = 'YOUR_API_KEY';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

// Read and encode the image
const imageB64 = fs.readFileSync('captcha.png').toString('base64');

// Submit to CaptchaAI
const { data: submitData } = await axios.post('https://ocr.captchaai.com/in.php', null, {
  params: {
    key: API_KEY,
    method: 'base64',
    body: imageB64,
    json: 1,
  },
});

if (submitData.status !== 1) throw new Error(submitData.request);
const taskId = submitData.request;
console.log(`Task submitted: ${taskId}`);

Se status não vier como 1, o campo request traz o código de erro. Note o json=1: sem ele a API responde em texto puro e sobra parsing de string na sua mão.


Passo 2: alternativa com upload do arquivo

Com a imagem já em disco — um screenshot recém-salvo pelo Puppeteer, por exemplo — o upload multipart evita codificar um buffer grande em memória. O resto do fluxo é idêntico.

const FormData = require('form-data');

const form = new FormData();
form.append('key', API_KEY);
form.append('method', 'post');
form.append('json', '1');
form.append('file', fs.createReadStream('captcha.png'));

const { data: submitData } = await axios.post('https://ocr.captchaai.com/in.php', form, {
  headers: form.getHeaders(),
});

const taskId = submitData.request;

Passo 3: consulte o resultado com polling

A resolução não é instantânea. O padrão é esperar cerca de 5 segundos, chamar res.php e repetir enquanto a resposta for CAPCHA_NOT_READY. Qualquer outro valor diferente de sucesso é um erro real e deve interromper o loop — insistir em cima de um erro só queima tempo e thread.

await sleep(5000);

let captchaText;
for (let i = 0; i < 30; i++) {
  const { data: pollData } = await axios.get('https://ocr.captchaai.com/res.php', {
    params: { key: API_KEY, action: 'get', id: taskId, json: 1 },
  });

  if (pollData.status === 1) {
    captchaText = pollData.request;
    console.log(`CAPTCHA text: ${captchaText}`);
    break;
  }
  if (pollData.request !== 'CAPCHA_NOT_READY') {
    throw new Error(pollData.request);
  }
  await sleep(5000);
}

Trinta tentativas com intervalo de 5 s dão uma janela de cerca de 150 segundos, folgada para o tipo imagem. Em produção, prefira um teto explícito de tentativas a um laço infinito.


Passo 4: restrinja o formato do texto para ganhar precisão

Se você já sabe que o desafio tem seis dígitos, diga isso à API. Restrições de comprimento e de conjunto de caracteres reduzem bastante o número de leituras ambíguas — o clássico 0 contra O.

// Digits only, 4-6 characters
const { data } = await axios.post('https://ocr.captchaai.com/in.php', null, {
  params: {
    key: API_KEY,
    method: 'base64',
    body: imageB64,
    numeric: 1,      // digits only
    min_len: 4,       // minimum length
    max_len: 6,       // maximum length
    json: 1,
  },
});
Parâmetro Valor Objetivo
numeric 1 = dígitos, 2 = letras Limita os caracteres aceitos
min_len / max_len Inteiro Restrições de comprimento
calc 1 Calcula a expressão matemática exibida
regsense 1 Diferencia maiúsculas de minúsculas

Fluxo completo: do screenshot ao formulário enviado

O exemplo abaixo junta tudo: Puppeteer abre a página, recorta o elemento do CAPTCHA em PNG, envia à CaptchaAI, consulta o resultado e digita o texto no campo.

const axios = require('axios');
const puppeteer = require('puppeteer');
const fs = require('fs');

const API_KEY = 'YOUR_API_KEY';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function solveImageCaptcha() {
  // 1. Load page and screenshot CAPTCHA
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com/register');

  const captchaEl = await page.$('#captcha-image');
  await captchaEl.screenshot({ path: 'captcha.png' });

  // 2. Encode and submit
  const imageB64 = fs.readFileSync('captcha.png').toString('base64');
  const { data: submit } = await axios.post('https://ocr.captchaai.com/in.php', null, {
    params: { key: API_KEY, method: 'base64', body: imageB64, json: 1 },
  });
  const taskId = submit.request;

  // 3. Poll for text
  await sleep(5000);
  let text;
  for (let i = 0; i < 30; i++) {
    const { data: poll } = await axios.get('https://ocr.captchaai.com/res.php', {
      params: { key: API_KEY, action: 'get', id: taskId, json: 1 },
    });
    if (poll.status === 1) { text = poll.request; break; }
    if (poll.request !== 'CAPCHA_NOT_READY') throw new Error(poll.request);
    await sleep(5000);
  }

  // 4. Type and submit
  await page.type('#captcha-input', text);
  await page.click('form [type="submit"]');
  console.log(`Solved: ${text}`);
  await browser.close();
}

solveImageCaptcha().catch(console.error);

Resultado esperado:

Solved: ABC123

Uma suíte de testes de integração reaproveita esse esqueleto trocando apenas o seletor #captcha-image pelo do seu ambiente de staging.


Erros comuns e como corrigir

Erro Causa Correção
ERROR_WRONG_FILE_EXTENSION Formato não suportado Use JPG, PNG ou GIF
ERROR_TOO_BIG_CAPTCHA_FILESIZE Imagem acima de 100 KB Comprima antes de enviar
ERROR_ZERO_CAPTCHA_FILESIZE Imagem abaixo de 100 bytes Verifique se o screenshot capturou o elemento certo
CAPCHA_NOT_READY Ainda em resolução Consulte novamente a cada 5 segundos

O erro de tamanho zero é o mais frequente em automação: acontece quando o seletor é resolvido antes de a imagem carregar. Espere o elemento ficar visível antes do screenshot. Quando o texto volta errado com frequência, revise o recorte: bordas do formulário dentro do PNG atrapalham mais do que a distorção do desafio.


Quantas threads o seu pipeline precisa

A CaptchaAI cobra por thread simultânea, não por resolução: cada plano inclui resoluções ilimitadas dentro das threads contratadas. Uma thread é um CAPTCHA em andamento; assim que ele termina, ela libera espaço para o próximo.

Pense em uma equipe de QA em São Paulo rodando testes noturnos contra um formulário legado em staging. Se a suíte dispara quatro execuções em paralelo, o plano BASIC (US$ 15/mês, 5 threads) já cobre a janela. Um monitoramento autorizado que mantém dezenas de páginas abertas ao mesmo tempo pede outro patamar — o ADVANCE (US$ 90/mês, 50 threads) é o degrau típico. Com workers em sa-east-1, a latência de rede pesa pouco diante do tempo de resolução — o gargalo real é quantas requisições ficam em voo.


Perguntas frequentes

Esse endpoint de OCR atende hCaptcha ou FunCaptcha?

Não. hCaptcha e FunCaptcha (Arkose Labs) não são suportados, e nenhum ajuste de OCR muda isso — esses widgets não entregam o desafio como imagem de texto. O endpoint deste guia atende CAPTCHAs de imagem, texto e grade.

Preciso de um navegador headless para usar a API de OCR?

Não. A API recebe bytes de imagem, então qualquer origem serve: arquivo salvo por um crawler em Node.js puro, buffer vindo de uma requisição HTTP ou recorte do Puppeteer. O navegador só entra para capturar a imagem da tela.

Posso enviar vários CAPTCHAs de imagem ao mesmo tempo?

Sim, até o número de threads do seu plano. Envie as tarefas em paralelo, guarde os IDs em uma fila e faça o polling de cada uma de forma independente — um Promise.all rende muito mais do que um loop sequencial.

Que tamanho e formato de imagem a API aceita?

JPG, PNG e GIF, entre 100 bytes e 100 KB. Screenshots do Puppeteer em PNG quase sempre cabem no limite; se um recorte passar de 100 KB, reduza a área capturada ou recomprima antes do envio.


Guias relacionados


Comece a resolver CAPTCHAs de imagem com CaptchaAI →

Os comentários estão desativados para este artigo.