Scarnox · Blog

Resolver captchas en Node.js: ejemplo funcional con fetch (2026)

Un solver de captchas en Node.js es una sola petición HTTP. Node 18 y posteriores incluyen fetch, así que no necesitas ningún paquete para llamar a la API. Envías la sitekey y la URL de la página, esperas la respuesta y pones el token en el formulario. Esta guía muestra el flujo completo para PopularCaptcha, que Scarnox resuelve hoy sin proxy.

Empieza con crédito de prueba gratis, sin tarjeta y ejecuta primero el código de abajo contra una página de prueba.

Requisitos

El módulo del solver

El módulo siguiente es una sola función. Hace un POST a la API con una cabecera Bearer, aborta tras 185 segundos y lanza una excepción cuando errorId no es 0:

// solver.mjs
const API_URL = "https://api.scarnox.com/api/tasks/create";

export async function solveCaptcha(sitekey, pageUrl, { invisible = false } = {}) {
  const body = {
    captcha_type: "PopularCaptchaTaskProxyless",
    site_key: sitekey,
    site_url: pageUrl,
  };
  if (invisible) body.is_invisible = true;

  const res = await fetch(API_URL, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SCARNOX_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
    signal: AbortSignal.timeout(185_000), // the call returns when the token is ready
  });

  const data = await res.json().catch(() => null);
  if (!data) throw new Error(`HTTP ${res.status}: response was not JSON`);

  if (data.errorId !== 0) {
    throw new Error(`${data.errorCode}: ${data.errorDescription}`);
  }
  return data.solution.token;
}

No hay sondeo. La petición permanece abierta hasta que el token está listo, y el token está en solution.token si tiene éxito.

Reintentos

Una resolución fallida devuelve un errorId distinto de 0, y no se te cobra. Un bucle de reintentos corto basta para los fallos temporales. Mantén el número de intentos bajo, para que una sitekey o una URL de página incorrectas no consuman tiempo:

export async function solveWithRetry(sitekey, pageUrl, attempts = 3) {
  let lastError;
  for (let i = 1; i <= attempts; i++) {
    try {
      return await solveCaptcha(sitekey, pageUrl);
    } catch (err) {
      lastError = err;
      console.warn(`Attempt ${i} failed: ${err.message}`);
      await new Promise((r) => setTimeout(r, 1000 * i));
    }
  }
  throw lastError;
}

Para una política de errores más completa, consulta gestión de errores del solver de captchas.

Enviar el token

El token va al campo del formulario que espera el sitio. La mayoría de las páginas de PopularCaptcha leen h-captcha-response, y muchas también leen g-recaptcha-response. Envía ambos para estar seguro:

// submit.mjs
import { solveWithRetry } from "./solver.mjs";

const pageUrl = "https://example.com/signup";
const token = await solveWithRetry("YOUR-SITEKEY-UUID", pageUrl);

const form = new URLSearchParams({
  email: "you@example.com",
  "h-captcha-response": token,
  "g-recaptcha-response": token,
});

const res = await fetch(pageUrl, {
  method: "POST",
  body: form,
  redirect: "manual",
});
console.log("Status:", res.status);

Ejecútalo con:

SCARNOX_API_KEY=scarnox_YOURAPIKEY node submit.mjs

Cookies y sesiones

El fetch simple no mantiene cookies entre peticiones. Muchas páginas de registro y búsqueda vinculan el captcha a una cookie de sesión, así que un token enviado sin la cookie correspondiente puede ser rechazado. Hay dos formas de gestionarlo:

Elige la primera cuando el formulario sea sencillo y el sitio no ejecute mucho JavaScript. Elige la segunda para cualquier caso más complejo.

Una comprobación práctica: después del POST, mira el código de estado y la URL final. Una redirección a una página de éxito suele indicar que el token fue aceptado. Una página que vuelve a mostrar el widget suele indicar que fue rechazado, o que la cookie del GET no llegó. Registra el cuerpo de la respuesta en las primeras ejecuciones para saber en cuál de los dos casos estás.

Tiempos

El token caduca en unos dos minutos. Resuelve cerca del momento en que envías. El error habitual es resolver al principio de un trabajo largo y enviar más tarde. Resuelve y envía de inmediato. Caducidad del token de captcha lo cubre con más detalle.

Captchas invisibles

Si el widget de la página tiene data-size="invisible", pasa { invisible: true }. La función entonces envía is_invisible: true con la petición:

const token = await solveCaptcha("YOUR-SITEKEY-UUID", pageUrl, { invisible: true });

Si la página además asocia un callback al widget, puede que el formulario necesite que se llame a esa función con el token. Lee el script de la página para encontrarla. La guía sobre captchas invisibles cubre los patrones.

Hacer scraping con el solver

A veces el captcha aparece en una página que haces scraping, no en un formulario que envías. El mismo patrón funciona: detecta el widget en el HTML, resuélvelo y envíalo a la URL a la que publica el widget. Mantén bajo el ritmo de peticiones y revisa antes los términos del sitio. Scrapea de forma responsable: respeta el robots.txt, evita los datos personales que no necesitas y no sobrecargues el servidor.

Precios

Scarnox cobra por cada resolución correcta. Las resoluciones fallidas no se cobran. Para las tarifas actuales, consulta la página de precios. Para empezar, empieza con crédito de prueba gratis, sin tarjeta.

Próximos pasos