Resolver un CAPTCHA con CaptchaAI son dos llamadas cURL: in.php envía la tarea, res.php recoge el token. Los SDK son azúcar sobre esas dos peticiones, y en un runner de CI sin Python ni Node.js la terminal es todo lo que tienes.
Qué necesitas antes de empezar
| Requisito | Detalles |
|---|---|
| cURL | Cualquier versión moderna |
| jq (opcional) | Para analizar respuestas |
| Clave API CaptchaAI | Consigue uno aquí |
Guarda la clave en CAPTCHAAI_API_KEY: en el historial del shell es una fuga silenciosa.
Los tres comandos que resumen la API
Consultar el saldo
curl -s "https://ocr.captchaai.com/res.php?key=YOUR_API_KEY&action=getbalance"
Salida: 1.234. Un número significa clave válida y red hacia el endpoint.
Enviar un reCAPTCHA v2
curl -s "https://ocr.captchaai.com/in.php?key=YOUR_API_KEY&method=userrecaptcha&googlekey=6Le-wvkS...&pageurl=https://example.com"
Salida: OK|73548291. Ese número es el ID de la tarea, no el token: la API es asíncrona.
Sondear el resultado
curl -s "https://ocr.captchaai.com/res.php?key=YOUR_API_KEY&action=get&id=73548291"
Salida: OK|03AGdBq24PBCbw... o CAPCHA_NOT_READY. Esto último no es un error —se escribe así, sin la "T", por compatibilidad histórica— sino la respuesta normal mientras la tarea sigue en cola.
Un script de Bash que puedes reutilizar
Para automatizar, envuelve envío y sondeo en una función con tiempo de espera en solve_captcha.sh:
#!/bin/bash
set -euo pipefail
API_KEY="${CAPTCHAAI_API_KEY:?Set CAPTCHAAI_API_KEY environment variable}"
BASE_URL="https://ocr.captchaai.com"
solve_recaptcha() {
local site_key="$1"
local page_url="$2"
local timeout="${3:-300}"
# Submit
local response
response=$(curl -s "${BASE_URL}/in.php?key=${API_KEY}&method=userrecaptcha&googlekey=${site_key}&pageurl=${page_url}")
if [[ ! "$response" == OK|* ]]; then
echo "ERROR: Submit failed: $response" >&2
return 1
fi
local task_id="${response#OK|}"
echo "Submitted task: $task_id" >&2
# Poll
local deadline=$((SECONDS + timeout))
while (( SECONDS < deadline )); do
sleep 5
local result
result=$(curl -s "${BASE_URL}/res.php?key=${API_KEY}&action=get&id=${task_id}")
if [[ "$result" == "CAPCHA_NOT_READY" ]]; then
echo "Waiting..." >&2
continue
fi
if [[ "$result" == OK|* ]]; then
echo "${result#OK|}"
return 0
fi
echo "ERROR: Solve failed: $result" >&2
return 1
done
echo "ERROR: Timeout after ${timeout}s" >&2
return 1
}
# Usage: ./solve_captcha.sh SITE_KEY PAGE_URL
if [[ $# -ge 2 ]]; then
solve_recaptcha "$1" "$2"
fi
Hazlo ejecutable:
chmod +x solve_captcha.sh
Y ejecútalo:
export CAPTCHAAI_API_KEY="your_key_here"
./solve_captcha.sh "6Le-wvkS..." "https://example.com"
Dos detalles importan: el token sale por stdout y el progreso por stderr, así lo capturas limpio; y el bucle tiene fecha límite, así un fallo de red no cuelga el job.
Otros tipos de CAPTCHA desde la misma terminal
Cloudflare Turnstile
Cambia method y el nombre de la clave pública (sitekey, no googlekey):
curl -s "https://ocr.captchaai.com/in.php?key=${CAPTCHAAI_API_KEY}&method=turnstile&sitekey=0x4AAAAA...&pageurl=https://example.com"
CAPTCHA de imagen y OCR
Codifica el archivo en base64 y envíalo:
# Encode image to base64
IMAGE_B64=$(base64 -w 0 captcha.png)
# Submit
curl -s "https://ocr.captchaai.com/in.php?key=${CAPTCHAAI_API_KEY}&method=base64&body=${IMAGE_B64}"
Con imágenes grandes, usa POST multipart:
curl -s -X POST "https://ocr.captchaai.com/in.php" \
-F "key=${CAPTCHAAI_API_KEY}" \
-F "method=post" \
-F "[email protected]"
Por esta vía CaptchaAI cubre reCAPTCHA v2 y v3, Turnstile y Cloudflare Challenge, GeeTest v3, imagen/OCR, grid y BLS, más CaptchaFox (beta), Friendly Captcha (beta) y Lemin (beta). hCaptcha y FunCaptcha (Arkose Labs) no son compatibles; GeeTest v4, próximamente.
Del token al formulario en una tubería
El token solo sirve si acaba en el campo correcto. Este script encadena los dos pasos contra staging:
#!/bin/bash
# Solve CAPTCHA and submit form in one pipeline
API_KEY="${CAPTCHAAI_API_KEY}"
SITE_KEY="6Le-wvkS..."
TARGET_URL="https://staging.example.com/qa-login"
# Solve
TOKEN=$(./solve_captcha.sh "$SITE_KEY" "$TARGET_URL")
if [[ -z "$TOKEN" ]]; then
echo "Failed to solve CAPTCHA"
exit 1
fi
# Submit form with token
curl -s -X POST "$TARGET_URL" \
-d "username=user" \
-d "password=pass" \
-d "g-recaptcha-response=${TOKEN}"
El campo es g-recaptcha-response porque el ejemplo es reCAPTCHA; con Turnstile, cf-turnstile-response. Equivocarse aquí da el fallo más frustrante: la resolución funciona y el backend rechaza el envío.
Lotes desde un archivo
Caso habitual: validar el alta de un portal de cita previa en varios entornos, sin navegador. Metes las URL en urls.txt:
#!/bin/bash
# Input file: urls.txt (one URL per line)
while IFS= read -r url; do
echo "Processing: $url"
TOKEN=$(./solve_captcha.sh "6Le-wvkS..." "$url")
if [[ -n "$TOKEN" ]]; then
echo "$url,$TOKEN" >> results.csv
echo " Solved ✓"
else
echo " Failed ✗"
fi
done < urls.txt
El plan BASIC ($15/mes, 5 threads) resuelve cinco en paralelo; STANDARD ($30/mes, 15 threads) y ADVANCE ($90/mes, 50 threads) amplían la concurrencia sin tocar el script: se factura por thread, no por resolución. Un costo fijo en USD, fácil de presupuestar para agencias y freelancers que facturan en moneda local.
PowerShell para runners Windows
La misma secuencia con Invoke-RestMethod:
$ApiKey = $env:CAPTCHAAI_API_KEY
$BaseUrl = "https://ocr.captchaai.com"
# Submit
$response = Invoke-RestMethod "${BaseUrl}/in.php?key=${ApiKey}&method=userrecaptcha&googlekey=6Le-wvkS...&pageurl=https://example.com"
if ($response -match '^OK\|(.+)$') {
$taskId = $Matches[1]
Write-Host "Task: $taskId"
} else {
Write-Error "Submit failed: $response"
exit 1
}
# Poll
do {
Start-Sleep -Seconds 5
$result = Invoke-RestMethod "${BaseUrl}/res.php?key=${ApiKey}&action=get&id=${taskId}"
} while ($result -eq 'CAPCHA_NOT_READY')
if ($result -match '^OK\|(.+)$') {
$token = $Matches[1]
Write-Host "Token: $token"
} else {
Write-Error "Solve failed: $result"
}
Errores frecuentes y cómo resolverlos
| Error | Causa | Solución |
|---|---|---|
curl: (6) Could not resolve host |
Problema de DNS | Comprueba la red |
ERROR_WRONG_USER_KEY |
Clave API incorrecta | Comprueba si hay espacios o saltos de línea en la clave |
| La respuesta está vacía. | Timeout de red | Añade --connect-timeout 30 |
base64: invalid input |
Problema con archivos binarios | Usa base64 -w 0 (sin ajuste de línea) |
ERROR_WRONG_USER_KEY rara vez es una clave inválida: casi siempre es un salto de línea al copiarla.
Configuración recomendada para tu pipeline
Si el CAPTCHA aparece en un test de navegador, usa el mismo viewport, idioma y user-agent en local, staging y CI:
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)
Cómo encaja CaptchaAI en tu integración
El patrón no cambia según el lenguaje ni el framework:
- Tu test detecta el widget en tu propia aplicación.
- Envía a CaptchaAI los datos públicos (
sitekey, URL, tipo). - CaptchaAI devuelve un token válido para esa página.
- Tu test lo inyecta en el campo y envía el formulario.
- Tu backend lo verifica, igual que con una persona.
Aplica a integraciones que tú controlas: respeta los términos de servicio y la normativa de datos aplicable.
Qué medir cuando ya corre solo
- Tiempo de resolución — de la solicitud a la entrega del token.
- Tasa de éxito por endpoint propio — verificaciones que pasan sobre el total.
- Distribución de errores — por código (
ERROR_*, tiempos de espera, red). - Latencia extremo a extremo — render, resolución y respuesta del backend.
Usa además una API key separada para QA y backoff exponencial en los reintentos.
Preguntas frecuentes
Las dudas que se repiten al llevar estos comandos a un script real.
¿Necesito jq para leer las respuestas?
No. La API devuelve texto plano OK|valor; la expansión de parámetros de Bash basta.
¿Qué hago si CAPCHA_NOT_READY no termina nunca?
Revisa el script antes que la clave. Tres causas cubren casi todo:
- El
pageurlno es la URL exacta del widget. - El
sitekeyes de otro entorno (staging frente a producción). - El bucle no tiene fecha límite y sondea una tarea ya descartada.
¿Cuántas resoluciones puedo lanzar a la vez?
Hasta el número de threads de tu plan: con BASIC ($15/mes), 5 simultáneas. Por encima, las solicitudes esperan turno.
¿Es seguro poner la clave en un secreto de CI?
Sí. Expórtala como variable de entorno y nunca la escribas literal en el comando: la línea queda visible en los logs de muchos runners.
Guías relacionadas
- Resolver CAPTCHA con Axios, sin navegador
- Configurar y autenticar tu clave API
- Formatos de respuesta de la API
Valida tus integraciones CAPTCHA en entornos propios con CaptchaAI.