Tutoriales de API

Resolver CAPTCHA de imagen con Node.js y CaptchaAI

Aquí no hay sitekey ni token: subes el PNG a la API OCR de CaptchaAI y recuperas en texto plano las letras que muestra. Con axios y fs, unas veinte líneas de Node.js.

Los formularios que aún piden letras deformadas son casi siempre los mismos: portales de trámites públicos, intranets antiguas y registros sin tocar en años. Ese recuadro gris detiene tu script; CaptchaAI lee la imagen y devuelve el texto.


Lo que necesitas antes de empezar

Elemento Valor
Clave API de CaptchaAI Desde captchaai.com
Node.js 14 o superior
Librerías axios, fs
Formato de imagen JPG, PNG o GIF (100 bytes – 100 KB)

Captura solo el elemento del CAPTCHA, nunca la página entera: el límite son 100 KB.


Enviar la imagen: base64 o archivo

Ninguna vía es mejor: elige según cómo llega la imagen.

En base64, cuando está en memoria

Codificas el buffer y lo mandas en el parámetro body.

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}`);

Con json: 1 la respuesta trae status y request. Si status no es 1, request trae el código de error.

Como archivo, cuando la imagen está en disco

Si tu pipeline ya los guarda en disco, envíalos con form-data y el método post.

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;

Consultar el resultado en res.php

Mientras la tarea siga en cola, res.php responde CAPCHA_NOT_READY.

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);
}

La asimetría del bucle importa: CAPCHA_NOT_READY es "sigue esperando"; cualquier otro valor, "para y registra el código".


Parámetros que mejoran la precisión

Si conoces el formato, decláralo: leído como texto libre, un 0 se confunde con una 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 Propósito
numeric 1 = dígitos, 2 = letras Limita los caracteres admitidos
min_len / max_len Entero Restricciones de longitud
calc 1 Calcula la expresión matemática
regsense 1 Distingue mayúsculas de minúsculas

Escenario típico en la región: un portal de cita previa o de trámites fiscales que siempre muestra cinco dígitos. Con numeric: 1, min_len: 5 y max_len: 5 bajan los reintentos por lecturas ambiguas. Revisa varias capturas antes: si el portal alterna letras y números, restringir de más genera fallos tuyos.


Ejemplo completo con Puppeteer

Todas las piezas juntas: abrir la página, capturar el CAPTCHA, enviarlo, sondear y rellenar.

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

La captura es la parte frágil: si #captcha-image apunta al contenedor y no a la imagen, recibirás ERROR_ZERO_CAPTCHA_FILESIZE.


Errores comunes y qué hacer con ellos

Error Causa Solución
ERROR_WRONG_FILE_EXTENSION Formato no compatible Usa JPG, PNG o GIF
ERROR_TOO_BIG_CAPTCHA_FILESIZE Imagen mayor de 100 KB Comprime antes de enviar
ERROR_ZERO_CAPTCHA_FILESIZE Imagen menor de 100 bytes Revisa el selector y la captura
CAPCHA_NOT_READY Todavía en proceso Sigue consultando cada 5 segundos

Los tres primeros se arreglan antes de la llamada. El cuarto no es un error: es una tarea en curso.


Coste y concurrencia

CaptchaAI factura por thread concurrente, no por resolución: cada plan incluye resoluciones ilimitadas por thread. Un thread es un CAPTCHA en vuelo y, al terminar, queda libre. Para un scraper de un proceso basta BASIC ($15/mes, 5 threads); con varios workers, STANDARD ($30/mes, 15 threads) o ADVANCE ($90/mes, 50 threads). Precios en USD, con coste mensual predecible.


Preguntas frecuentes

¿Necesito Puppeteer para resolver un CAPTCHA de imagen?

No. Solo captura la imagen de una página renderizada; si tu backend ya recibe el PNG, bastan axios y fs.

¿Qué hago si el texto devuelto es incorrecto?

Repórtalo con action=reportbad sobre res.php indicando el id de la tarea, y declara el formato con numeric y min_len/max_len.

¿Sirve este mismo código para reCAPTCHA o Turnstile?

No. Ahí se envía el sitekey y la URL, y la API devuelve un token. CaptchaAI cubre reCAPTCHA v2/v3, Cloudflare Turnstile y Challenge, GeeTest v3 e imagen/OCR; hCaptcha y FunCaptcha no son compatibles.

¿Puedo resolver CAPTCHA con operaciones matemáticas?

Sí. Añade calc: 1 y recibirás el resultado ya calculado, no la expresión.


Guías relacionadas


Empieza a resolver CAPTCHA de imagen con CaptchaAI

Configuración de navegador recomendada

Usa la misma configuración en QA, staging y CI: evita el test que pasa en local y falla en el runner.

from selenium import webdriver

def make_driver(headless: bool = True) -> webdriver.Chrome:
    options = webdriver.ChromeOptions()
    if headless:
        options.add_argument('--headless=new')
    options.add_argument('--window-size=1280,800')
    options.add_argument('--lang=es-ES')
    return webdriver.Chrome(options=options)

Igualar viewport, idioma y user-agent reduce la varianza entre ejecuciones.

Cómo encaja CaptchaAI en tu pipeline

El patrón no cambia con el framework de pruebas:

  1. Tu test detecta el CAPTCHA en tu aplicación (QA, staging o preproducción).
  2. Envía a CaptchaAI los datos públicos: la imagen en OCR, o el sitekey y la URL en los tipos basados en token.
  3. CaptchaAI devuelve el texto o el token.
  4. Tu test lo escribe en el campo y envía la petición.
  5. Tu backend lo valida contra el proveedor.

Aplica a integraciones que tú controlas. Respeta los términos de servicio y la normativa de protección de datos.

Métricas y buenas prácticas

Instrumenta el CAPTCHA como cualquier dependencia externa: tiempo de resolución, tasa de éxito por endpoint, errores por código y latencia extremo a extremo. Conserva logs y capturas: sin ellos un fallo intermitente no se reproduce.

  • Prueba siempre sobre tu aplicación o entornos autorizados.
  • Mantén una clave API de QA separada de la de producción.
  • Define tiempos de espera y reintentos con backoff exponencial.
  • Versiona la configuración de los tests (selectores, formato, longitudes).

Diagnóstico rápido

Síntoma Acción recomendada
El test no encuentra la imagen del CAPTCHA Revisa selectores y esperas en staging
CaptchaAI devuelve ERROR_NO_SLOT_AVAILABLE Reintenta con backoff
El backend rechaza el texto enviado Compara mayúsculas y longitud con el formato real
El test pasa en local y falla en CI Iguala viewport, idioma y user-agent
Tiempos de resolución muy variables Revisa los threads de tu plan

Valida tus integraciones en entornos propios con CaptchaAI.

Los comentarios están deshabilitados para este artículo.