La forma más barata de acelerar tus llamadas a la API de CaptchaAI no es cambiar de plan ni añadir workers: es dejar de abrir una conexión nueva en cada solicitud. Reutilizar una sola conexión TCP/TLS con keep-alive —y multiplexarla con HTTP/2 cuando resuelves en paralelo— recorta cientos de milisegundos por resolución sin tocar tu lógica de negocio.
Esta guía lo muestra en Python y Node.js con el código exacto, y desglosa las cifras que hay detrás del ahorro para que sepas cuándo el esfuerzo vale la pena y cuándo HTTP/1.1 keep-alive ya te basta.
Qué gana tu pipeline al reutilizar la conexión
Cada resolución no es una sola llamada, sino una tanda: un envío a in.php y varios sondeos a res.php hasta que el token está listo. Una resolución típica de reCAPTCHA v2 son entre 5 y 7 solicitudes HTTP: el envío inicial más 4 a 6 sondeos.
Sin keep-alive, cada una de esas solicitudes vuelve a pagar el handshake TCP y la negociación TLS —entre 100 y 300 ms por conexión—. Con keep-alive, solo la primera paga ese precio completo y el resto reutiliza el canal ya abierto:
| Escenario | Cálculo de la sobrecarga | Total |
|---|---|---|
| Sin keep-alive | 5 × (TCP ~50 ms + TLS ~100 ms) | 750 ms |
| Con keep-alive | 1 × (TCP + TLS) + 4 × (~5 ms) | 170 ms |
El resultado es un ahorro de ~580 ms por resolución. Con 10.000 resoluciones al día son unas 1,6 horas de latencia que dejas de acumular, y esa cifra crece cuanto más lejos esté tu servidor de los endpoints. Para una agencia en Latinoamérica o España que corre QA de scraping desde una máquina regional, la distancia de red suma latencia en cada salto: reutilizar la conexión hace que ese costo se pague una sola vez. Y como los planes de CaptchaAI se facturan por thread —BASIC arranca en $15/mes con 5 threads—, cada milisegundo que recortas libera antes ese thread para la siguiente tarea.
Python con requests.Session: keep-alive sin configuración extra
La biblioteca requests mantiene la conexión abierta de forma predeterminada en cuanto usas un objeto Session. Reutiliza esa misma sesión para el envío y todos los sondeos:
# keepalive_solver.py
import os
import time
import requests
API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
# Create a session — reuses TCP connections across requests
session = requests.Session()
session.headers.update({"Connection": "keep-alive"})
def solve_captcha(sitekey, pageurl):
"""Solve reCAPTCHA v2 using a persistent connection."""
# Submit — uses existing connection if available
resp = session.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": "1",
})
result = resp.json()
if result.get("status") != 1:
raise Exception(f"Submit failed: {result.get('request')}")
task_id = result["request"]
# Poll — reuses the same connection
time.sleep(15)
for _ in range(25):
poll = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": "1",
})
poll_result = poll.json()
if poll_result.get("status") == 1:
return poll_result["request"]
if poll_result.get("request") != "CAPCHA_NOT_READY":
raise Exception(f"Error: {poll_result.get('request')}")
time.sleep(5)
raise Exception("Timeout")
# Solve multiple CAPTCHAs reusing the same connection
for i in range(5):
token = solve_captcha(
"6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
"https://www.google.com/recaptcha/api2/demo"
)
print(f"Solve {i+1}: {token[:30]}...")
HTTP/2 en Python con httpx
requests solo habla HTTP/1.1. Para multiplexación real —varios sondeos por una misma conexión sin esperar en fila— cambia a httpx con http2=True:
# http2_solver.py
import os
import time
import httpx
API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
BASE_URL = "https://ocr.captchaai.com"
# HTTP/2 client with connection pooling
client = httpx.Client(http2=True, timeout=30.0)
def solve_captcha(sitekey, pageurl):
"""Solve using HTTP/2 multiplexed connections."""
resp = client.get(f"{BASE_URL}/in.php", params={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": "1",
})
result = resp.json()
if result.get("status") != 1:
raise Exception(f"Submit failed: {result.get('request')}")
task_id = result["request"]
time.sleep(15)
for _ in range(25):
poll = client.get(f"{BASE_URL}/res.php", params={
"key": API_KEY, "action": "get",
"id": task_id, "json": "1",
})
poll_result = poll.json()
if poll_result.get("status") == 1:
return poll_result["request"]
if poll_result.get("request") != "CAPCHA_NOT_READY":
raise Exception(f"Error: {poll_result.get('request')}")
time.sleep(5)
raise Exception("Timeout")
# Multiple solves over a single HTTP/2 connection
for i in range(5):
token = solve_captcha(
"6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
"https://www.google.com/recaptcha/api2/demo"
)
print(f"Solve {i+1}: {token[:30]}...")
client.close()
Node.js: una instancia de Axios con conexiones persistentes
En Node.js el keep-alive no viene activado por defecto: crea agentes http y https con keepAlive: true y pásalos a una instancia de Axios que reutilizas en todas las llamadas. El maxSockets fija cuántas conexiones simultáneas mantiene el pool:
// keepalive_solver.js
const axios = require('axios');
const http = require('http');
const https = require('https');
const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';
// Create agents with keep-alive enabled
const httpAgent = new http.Agent({ keepAlive: true, maxSockets: 10 });
const httpsAgent = new https.Agent({ keepAlive: true, maxSockets: 10 });
// Axios instance with persistent connections
const api = axios.create({
baseURL: 'https://ocr.captchaai.com',
httpAgent,
httpsAgent,
timeout: 30000,
});
async function solveCaptcha(sitekey, pageurl) {
// Submit — reuses connection
const submit = await api.get('/in.php', {
params: {
key: API_KEY, method: 'userrecaptcha',
googlekey: sitekey, pageurl, json: '1',
},
});
if (submit.data.status !== 1) throw new Error(submit.data.request);
const taskId = submit.data.request;
// Poll — reuses same connection
await new Promise(r => setTimeout(r, 15000));
for (let i = 0; i < 25; i++) {
const poll = await api.get('/res.php', {
params: { key: API_KEY, action: 'get', id: taskId, json: '1' },
});
if (poll.data.status === 1) return poll.data.request;
if (poll.data.request !== 'CAPCHA_NOT_READY') throw new Error(poll.data.request);
await new Promise(r => setTimeout(r, 5000));
}
throw new Error('Timeout');
}
(async () => {
for (let i = 0; i < 5; i++) {
const token = await solveCaptcha(
'6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-',
'https://www.google.com/recaptcha/api2/demo'
);
console.log(`Solve ${i + 1}: ${token.slice(0, 30)}...`);
}
// Clean up agents
httpAgent.destroy();
httpsAgent.destroy();
})();
HTTP/2 o HTTP/1.1 keep-alive: cuál te conviene
Ambos reutilizan la conexión; la diferencia está en cómo la comparten cuando tienes varias resoluciones en vuelo a la vez.
| Característica | HTTP/1.1 Keep-Alive | HTTP/2 |
|---|---|---|
| Reutilización de la conexión | Sí (secuencial) | Sí (multiplexado) |
| Flujos simultáneos | 1 por conexión | Hasta 100+ por conexión |
| Compresión de cabeceras | No | HPACK |
| Reducción de latencia | ~60% | ~70% |
| Requiere soporte del navegador | No | No (llamadas API) |
| Ideal para | Resoluciones secuenciales | Resoluciones paralelas |
La regla práctica es sencilla:
- Resoluciones secuenciales (un CAPTCHA a la vez): HTTP/1.1 keep-alive ya te da casi todo el ahorro.
- Resoluciones concurrentes (muchas a la vez): HTTP/2 gana, porque todas comparten una única conexión en lugar de disputarse el pool de sockets.
Cómo dimensionar el pool de conexiones
El tamaño del pool debe seguir a tu nivel de concurrencia real, no a un número al azar:
| Resoluciones concurrentes | Tamaño de pool recomendado |
|---|---|
| 1–5 | 5 conexiones |
| 5–20 | 10 conexiones |
| 20–50 | 25 conexiones |
| 50–100 | 50 conexiones |
| 100+ | Usa HTTP/2 (1 conexión) |
Un pool sobredimensionado desperdicia memoria con sockets ociosos; uno demasiado pequeño obliga a abrir conexiones nuevas y anula el beneficio del keep-alive. Ajústalo a los threads que tienes contratados: no ganas nada con 50 conexiones si tu plan resuelve 15 tareas en paralelo.
Problemas frecuentes y cómo resolverlos
Si activaste el keep-alive y no ves mejora, casi siempre es uno de estos cuatro casos:
| Problema | Causa | Solución |
|---|---|---|
| Se cierran conexiones entre sondeos | Timeout del servidor o del proxy | Sube el timeout de keep-alive por encima de 30 s en la configuración del cliente |
| Sin mejora de rendimiento | Ya usabas keep-alive (predeterminado en algunas librerías) | Verifícalo con herramientas de monitoreo de red |
| Errores de conexión rechazada | Pool agotado | Aumenta maxSockets o reduce la concurrencia |
| HTTP/2 no se negocia | El servidor no soporta h2 | Recurre a HTTP/1.1 keep-alive |
Checklist para exprimir el keep-alive
Antes de dar por optimizado tu cliente, repasa estos cuatro puntos:
- Reutiliza una única
Sessiono cliente para el envío y todos los sondeos, nunca uno por solicitud. - Sube el timeout de keep-alive por encima de tu intervalo de sondeo para que la conexión no muera entre llamadas.
- Dimensiona el pool según los threads que resuelves en paralelo, ni más ni menos.
- Reserva HTTP/2 para cargas concurrentes; en un flujo secuencial, HTTP/1.1 keep-alive ya basta.
Preguntas frecuentes
¿Cuánta latencia ahorro de verdad con keep-alive?
Depende de tu distancia a los endpoints, pero en una resolución de 5 a 7 solicitudes es habitual recortar ~580 ms frente a abrir una conexión nueva cada vez. Cuanto más lejos esté tu servidor, mayor es el ahorro por sondeo evitado.
¿HTTP/2 me sirve si resuelvo un CAPTCHA a la vez?
Poco. Su multiplexación brilla con resoluciones en paralelo; para un flujo secuencial, HTTP/1.1 keep-alive ya reutiliza la conexión y da prácticamente el mismo tiempo de respuesta.
¿Reutilizar la conexión afecta a la tasa de resolución?
No. El keep-alive solo cambia cómo viajan tus solicitudes por la red: el envío, los sondeos y el token que devuelve res.php son idénticos. Lo que ganas es latencia, no una resolución distinta.
¿Debo cerrar la sesión después de cada lote?
No. Mantén la sesión o el cliente abiertos entre lotes si ejecutas resoluciones periódicas; ciérralos solo al terminar la aplicación. Reabrir en cada lote vuelve a pagar el handshake que intentas evitar.
¿El keep-alive funciona detrás de un proxy?
Sí, siempre que el proxy también mantenga la conexión abierta. Conviene revisar dos cosas antes de darlo por hecho:
- Que el proxy no cierre el canal entre solicitudes: algunos proxies SOCKS5 lo hacen y anulan la ventaja.
- Que soporte HTTP/2 si tu cliente lo negocia; de lo contrario, quédate en HTTP/1.1 keep-alive.
Artículos relacionados
- Patrón circuit breaker para llamadas a la API CAPTCHA
- Cómo dimensionar el pool de conexiones de tu cliente API
- Benchmarks de tiempos de resolución de CAPTCHA