Si la API te devuelve ERROR_PROXY_NOT_AUTHORIZED, el CAPTCHA no tiene nada que ver: CaptchaAI intentó abrir la página de destino a través del proxy que le pasaste y esa conexión no se estableció. Casi siempre es una de cinco cosas: string mal formado, proxytype que no coincide con el protocolo real, credenciales caducadas, contraseña con símbolos sin codificar, o lista blanca de IP en la que la IP que llega no es la tuya.
Qué comprueba la API antes de fallar
CaptchaAI recibe proxy y proxytype, abre la página de pageurl saliendo por ese proxy y resuelve el desafío desde ahí. Si el túnel no se abre, la tarea ni empieza y in.php responde el error. El fallo es anterior a la resolución: no hay res.php que consultar ni sirve cambiar de tipo de CAPTCHA. Todo está en dos campos.
| Causa | Cómo comprobarla |
|---|---|
| Formato del string incorrecto | Contrasta contra host:port:usuario:password |
proxytype equivocado |
Confirma si es HTTP, HTTPS, SOCKS4 o SOCKS5 |
| Credenciales caducadas | Prueba el proxy fuera de CaptchaAI |
| Contraseña con caracteres especiales | Codifícala con URL-encoding |
| El proveedor exige lista blanca de IP | Revisa su panel de control |
| El host del proxy no responde | Comprueba la conectividad TCP al puerto |
El formato exacto que espera CaptchaAI
Dos campos, sin variantes. proxytype va en mayúsculas y proxy usa dos puntos como separador, sin http:// delante ni @ entre usuario y host:
proxytype: HTTP | HTTPS | SOCKS4 | SOCKS5
proxy: host:port:username:password
Así queda dentro de un envío completo de reCAPTCHA v2:
import requests
# Correct format
data = {
"key": "YOUR_API_KEY",
"method": "userrecaptcha",
"googlekey": "SITE_KEY",
"pageurl": "https://example.com",
"proxytype": "HTTP",
"proxy": "192.168.1.1:8080:myuser:mypass",
"json": 1,
}
resp = requests.post("https://ocr.captchaai.com/in.php", data=data)
El fallo más habitual es pegar el proxy en formato URL (http://usuario:pass@host:puerto), como lo entrega el proveedor. Ese string sirve para requests, pero no aquí: reordénalo a host:puerto:usuario:password.
Variantes admitidas del string
Los proxies sin autenticación y los SOCKS también valen, siempre que el proxytype acompañe:
# With authentication
proxy = "192.168.1.1:8080:username:password"
# Without authentication (rare — most captcha proxies need auth)
proxy = "192.168.1.1:8080"
# SOCKS5
proxytype = "SOCKS5"
proxy = "192.168.1.1:1080:user:pass"
Si tu proveedor da acceso solo por lista blanca de IP, el string sin credenciales es correcto de forma, pero fallará igual por el motivo de más abajo.
Valida el proxy antes de enviar la tarea
Enviar una tarea para descubrir que el proxy no funcionaba es la forma lenta de depurar. Estas funciones separan sintaxis de red:
import re
import requests
def validate_proxy(proxy_str, proxy_type="HTTP"):
"""Validate proxy format and connectivity."""
# Check format
parts = proxy_str.split(":")
if len(parts) < 2:
raise ValueError(f"Invalid proxy format: {proxy_str}")
host = parts[0]
port = parts[1]
if not port.isdigit():
raise ValueError(f"Invalid port: {port}")
# Check proxy type
valid_types = {"HTTP", "HTTPS", "SOCKS4", "SOCKS5"}
if proxy_type not in valid_types:
raise ValueError(f"Invalid proxy type: {proxy_type}")
return True
def test_proxy(proxy_str, proxy_type="HTTP"):
"""Test if proxy is working."""
parts = proxy_str.split(":")
host = parts[0]
port = parts[1]
if len(parts) == 4:
user, password = parts[2], parts[3]
proxy_url = f"{proxy_type.lower()}://{user}:{password}@{host}:{port}"
else:
proxy_url = f"{proxy_type.lower()}://{host}:{port}"
proxies = {"http": proxy_url, "https": proxy_url}
try:
resp = requests.get(
"https://httpbin.org/ip",
proxies=proxies,
timeout=10,
)
print(f"Proxy working. IP: {resp.json()['origin']}")
return True
except Exception as e:
print(f"Proxy failed: {e}")
return False
# Test before using con CaptchaAI
proxy = "192.168.1.1:8080:user:pass"
if test_proxy(proxy, "HTTP"):
print("Proxy is ready to use")
Si test_proxy imprime una IP y la tarea sigue fallando, el problema ya no es el string sino el origen de la conexión.
Contraseñas con símbolos: el caso de los dos puntos
El separador son los dos puntos, así que cualquier : dentro de la contraseña rompe el parseo y añade un campo de más. Igual con @, # o %. Codifícala antes de armar el string:
from urllib.parse import quote
def format_proxy(host, port, username, password):
"""Format proxy string, handling special characters."""
# URL-encode password if it contains special characters
safe_password = quote(password, safe="")
return f"{host}:{port}:{username}:{safe_password}"
# Password with special characters
proxy = format_proxy("192.168.1.1", "8080", "user", "p@ss:word!")
Es frecuente cuando el gestor de secretos genera la contraseña: sale sólida y el pipeline la rompe al concatenarla.
Cuando no sabes qué protocolo habla el proxy
Algunos proveedores dan el mismo host con un puerto por protocolo. Antes de probar proxytype a ciegas, mira qué puerto acepta conexión:
def detect_proxy_type(host, port):
"""Try to detect which proxy protocol works."""
import socket
for proxy_type in ["HTTP", "SOCKS5", "SOCKS4"]:
try:
# Basic TCP connection test
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
result = sock.connect_ex((host, int(port)))
sock.close()
if result == 0:
return proxy_type # Port is open, try this type
except Exception:
continue
return None
Es una comprobación de puerto abierto, no un handshake: úsala como primer filtro y confirma con un envío real.
Funciona en local y falla vía CaptchaAI: el caso de la lista blanca
Este es el escenario que más tickets genera y el que menos se parece a un bug. Piensa en un equipo que monitoriza precios de marketplaces regionales desde un servidor en Madrid o en Ciudad de México: dieron de alta la IP de ese servidor en la lista blanca del proveedor y desde ahí todo funciona. Pero cuando CaptchaAI abre la página de destino, quien se conecta al proxy es la infraestructura de CaptchaAI, no tu servidor, y esa IP no está autorizada. La solución no está en el código: pide credenciales en lugar de acceso por IP.
| Síntoma | Causa probable | Corrección |
|---|---|---|
| Falla con credenciales que sabes correctas | El proveedor exige lista blanca de IP | Cambia a autenticación por usuario y contraseña |
| Funciona en local, falla vía CaptchaAI | La IP que conecta al proxy no es la tuya | Mismo cambio: credenciales en lugar de IP |
| El proxy SOCKS no conecta nunca | proxytype equivocado |
Prueba SOCKS5, luego SOCKS4 |
| Fallos intermitentes de autenticación | Límite de solicitudes del proveedor | Usa proxies dedicados en lugar de compartidos |
| El string se parte en campos de más | Contraseña con : sin codificar |
Codifica la contraseña con URL-encoding |
Preguntas frecuentes
¿Necesito un proxy para resolver un CAPTCHA con CaptchaAI?
No. Es opcional y muchos flujos funcionan sin él. Tiene sentido cuando el destino comprueba que la IP donde se generó el token coincide con la que después lo envía.
¿Este error consume saldo o threads de mi plan?
No. La tarea se rechaza antes de resolverse, así que no ocupa un thread. Los planes van de BASIC ($15/mes, 5 threads) a VIP-3 ($7,500/mes, 5.000 threads) y se facturan por threads concurrentes, con resoluciones ilimitadas.
¿HTTP o SOCKS5 para la mayoría de los casos?
HTTP o HTTPS cubre casi todo. Elige SOCKS5 solo si tu proveedor lo entrega así o el destino lo exige; SOCKS4 rara vez hace falta.
¿Puedo reutilizar el mismo proxy en varias tareas en paralelo?
Sí, pero mide antes: los proxies compartidos aplican límite de solicitudes por credencial, y eso aparece como fallos intermitentes al subir la concurrencia.
¿Sirve el mismo formato para reCAPTCHA v2, v3 y Turnstile?
Sí. Los campos proxy y proxytype son idénticos en todos los tipos que CaptchaAI resuelve, incluidos reCAPTCHA v2/v3, Cloudflare Turnstile y GeeTest v3. Solo cambia el method.
Guías relacionadas
Deja el entorno igual en local, staging y CI
Buena parte de los "falla solo en CI" no viene del proxy sino del navegador. Fija la misma configuración en tus runners antes de culpar a la red:
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)
Con viewport e idioma idénticos, un fallo que solo aparece en CI apunta al proxy. Registra los errores por código (ERROR_*, tiempos de espera, red): son intermitentes y sin traza no se reproducen.
¿Sigues bloqueado con el proxy? Resuélvelo con CaptchaAI.