¿Has configurado un parser, conectado proxies, pero la API sigue devolviendo errores 429 "Too Many Requests" o bloqueando el acceso? El problema no está en los proxies en sí, sino en la estrategia incorrecta de su uso. El rate limiting es un mecanismo de protección de la API que limita la cantidad de solicitudes desde una dirección IP en un período determinado. En este artículo, analizaremos por qué ocurren bloqueos al trabajar a través de proxies y cómo configurar correctamente el sistema para eludir los límites.
Qué es el API rate limiting y cómo funciona
El rate limiting (limitación de la frecuencia de solicitudes) es un mecanismo de protección de la API contra sobrecargas y abusos. El servicio establece un límite en la cantidad de solicitudes que se pueden realizar desde una fuente en un intervalo de tiempo determinado. Por ejemplo, las APIs populares utilizan tales restricciones:
- Twitter API: 300 solicitudes cada 15 minutos para acceso estándar
- Instagram Graph API: 200 solicitudes por hora por aplicación
- Google Maps API: depende del plan, generalmente 100-1000 solicitudes por día
- Wildberries API: límites no oficiales de alrededor de 60 solicitudes por minuto desde una IP
- Avito API: 10 solicitudes por segundo para scraping de anuncios
Existen varios métodos para determinar la fuente de las solicitudes, sobre los cuales se aplica el rate limiting:
Dirección IP: el método más común. La API cuenta la cantidad de solicitudes desde una IP específica en una ventana de tiempo.
Clave API: si utilizas autenticación a través de una clave, el límite se vincula a ella independientemente de la IP.
User-Agent y fingerprint: algunas APIs analizan los encabezados del navegador y crean una huella digital del cliente.
Sesión (cookies): el límite puede estar vinculado a la sesión del usuario a través de cookies.
Cuando se supera el límite, la API devuelve el estado HTTP 429 "Too Many Requests" y el encabezado Retry-After, que indica el tiempo hasta el restablecimiento del límite. Algunos servicios utilizan una "ventana deslizante" (rolling window), donde el límite se actualiza gradualmente, y otros utilizan una ventana fija que se restablece en un momento determinado.
Por qué los proxies no salvan automáticamente del rate limiting
Muchos desarrolladores piensan erróneamente que basta con conectar un proxy para poder enviar un número ilimitado de solicitudes. En la práctica, surgen los siguientes problemas:
Uso de un solo proxy para todas las solicitudes
Si tu script utiliza la misma dirección IP de proxy para todas las solicitudes, la API lo ve como un usuario normal y aplica los límites estándar. Por ejemplo, has configurado un parser de precios con Wildberries a través de un solo proxy residencial. El parser hace 100 solicitudes por minuto, pero el límite es de 60 solicitudes. Resultado: bloqueo de la IP durante 10-30 minutos.
Rotación lenta de IP
Algunos utilizan un grupo de 5-10 proxies y cambian entre ellos de manera secuencial. El problema es que cada IP aún alcanza el límite más rápido de lo que ocurre la rotación completa. Supongamos que tienes 10 proxies y un límite de 100 solicitudes por hora por IP. Si haces 1000 solicitudes por hora, cada proxy recibirá 100 solicitudes, justo en el límite. Cualquier irregularidad en la distribución llevará a bloqueos.
Ignorar otros factores de identificación
Incluso con una rotación de IP ideal, puedes ser bloqueado si:
- Todas las solicitudes provienen del mismo User-Agent (por ejemplo,
python-requests/2.28.0) - Se utiliza una sola clave API para todas las solicitudes
- Las solicitudes llegan con una periodicidad perfecta (cada 0.5 segundos) — esto parece un bot
- Las direcciones IP de los proxies están en la misma subred (por ejemplo, todas en el rango 192.168.1.x)
Reputación de las direcciones IP
Los proxies de centros de datos a menudo son incluidos en listas negras, ya que sus IP son utilizadas por cientos de otros usuarios para scraping. La API puede aplicar límites más estrictos a tales direcciones o bloquearlas de inmediato. Por ejemplo, Instagram y Facebook prohíben agresivamente los centros de datos, incluso si no superas los límites oficiales.
Estrategias de rotación de IP para eludir los límites
La rotación adecuada de proxies es clave para eludir el rate limiting. Consideremos estrategias efectivas según la tarea.
Rotación después de cada solicitud
La estrategia más agresiva: cada solicitud pasa a través de una nueva IP. Es adecuada para tareas con límites muy estrictos (1-5 solicitudes por IP) o cuando se necesita distribuir la carga al máximo. Para esto se utilizan proxies residenciales con rotación automática — proporcionan un grupo de millones de IP, y cada solicitud recibe automáticamente una nueva dirección.
Ejemplo de uso: scraping de Instagram a través de una API no oficial, donde el límite es de 5 solicitudes por minuto desde una IP. Con rotación después de cada solicitud, puedes hacer 300 solicitudes por minuto a través de 300 IP diferentes.
Ventajas: máxima protección contra el rate limiting, cada IP se utiliza al mínimo.
Desventajas: alto costo (los proxies residenciales son más caros), posibles retrasos al cambiar de IP, más difícil mantener sesiones.
Rotación por tiempo (sticky sessions)
La dirección IP se utiliza durante un tiempo determinado (5-30 minutos), luego se cambia por una nueva. Esta estrategia es adecuada para APIs que requieren mantener la sesión, o cuando se necesitan hacer varias solicitudes relacionadas desde un mismo "usuario".
Cálculo del tiempo óptimo de rotación: si el límite de la API es de 100 solicitudes por hora, y planeas hacer 50 solicitudes a través de una IP, utiliza una sesión sticky de 30 minutos. Durante este tiempo harás 25 solicitudes (con carga uniforme), que es la mitad del límite.
Rotación por grupo con seguimiento de límites
Estrategia avanzada: tu script lleva un registro de la cantidad de solicitudes desde cada IP y cambia automáticamente a una nueva cuando se acerca al límite. Por ejemplo, tienes un grupo de 20 proxies, el límite de la API es de 100 solicitudes por hora. El script rastrea el contador para cada IP y cambia al siguiente al alcanzar 90 solicitudes.
Esta estrategia requiere programar la lógica, pero ofrece la máxima eficiencia: utilizas cada proxy a plena capacidad sin exceder los límites.
Rotación geográfica
Algunas APIs aplican diferentes límites según la región. Por ejemplo, el servicio puede restringir las solicitudes desde EE. UU. más estrictamente que desde Europa. En tales casos, utiliza proxies de diferentes países y distribuye la carga entre ellos.
| Estrategia de rotación | Cuándo usar | Tipo de proxy |
|---|---|---|
| Después de cada solicitud | Límites estrictos (1-10 solicitudes/IP), scraping de redes sociales | Residenciales con auto-rotación |
| Por tiempo (5-30 min) | Se necesita sesión, límites medios (50-200 solicitudes/hora) | Residenciales sticky o móviles |
| Por grupo con seguimiento de límites | Gran volumen de scraping, límites conocidos de API | Cualquier tipo con grupo de 10+ IP |
| Geográfica | Restricciones regionales, scraping de contenido local | Residenciales de diferentes países |
Configuración de retrasos entre solicitudes
Incluso con una rotación de IP ideal, es importante configurar correctamente los retrasos entre solicitudes. Las solicitudes demasiado rápidas parecen un ataque, incluso si provienen de diferentes IP.
Cálculo del retraso mínimo
Fórmula: retraso = (ventana de tiempo en segundos / límite de solicitudes) × coeficiente de seguridad
Ejemplo: la API permite 100 solicitudes por hora (3600 segundos). Retraso mínimo = 3600 / 100 = 36 segundos. Añadimos un coeficiente de seguridad de 1.2: 36 × 1.2 = 43 segundos entre solicitudes desde una IP.
Si utilizas 10 proxies con rotación, puedes hacer solicitudes cada 4.3 segundos (43 / 10), sin exceder el límite en ninguna IP.
Retrasos aleatorios (jitter)
En lugar de un retraso fijo de 5 segundos, utiliza intervalos aleatorios, por ejemplo, de 3 a 7 segundos. Esto hace que tu tráfico se asemeje a las acciones de un usuario real. Muchos sistemas de protección contra bots analizan patrones: si las solicitudes llegan exactamente cada 5.0 segundos, es sospechoso.
Retraso exponencial en caso de errores
Cuando recibas un error 429, no continúes enviando solicitudes de inmediato. Utiliza un retraso exponencial: el primer intento después de 1 segundo, el segundo después de 2, el tercero después de 4, el cuarto después de 8 y así sucesivamente. Esta es una práctica estándar que las APIs esperan de los clientes.
Consejo: Verifica el encabezado Retry-After en la respuesta de la API. Indica el tiempo exacto en que puedes repetir la solicitud. Utiliza este valor en lugar de retrasos arbitrarios.
Qué tipo de proxy elegir para trabajar con la API
La elección del tipo de proxy influye críticamente en el éxito de eludir el rate limiting. Analicemos los pros y contras de cada opción para trabajar con la API.
Proxies residenciales
Los proxies residenciales utilizan direcciones IP de usuarios reales, asignadas por proveedores de internet. Para la API, esto se ve como internet doméstico normal.
Ventajas para la API:
- Alta confianza: las APIs rara vez bloquean IP domésticas
- Grandes grupos: millones de IP para rotación
- Diversidad geográfica: IP de diferentes ciudades y países
- Adecuados para redes sociales y APIs estrictas (Instagram, Facebook, TikTok)
Desventajas:
- Costo elevado: el pago suele ser por tráfico (de $5-15 por 1 GB)
- Velocidad variable: depende de la conexión del usuario final
- Inestabilidad: la IP puede desconectarse en cualquier momento
Cuándo usar: scraping de Instagram, Facebook, TikTok, trabajo con APIs de marketplaces (Wildberries, Ozon), cualquier tarea donde la reputación de la IP sea crítica.
Proxies móviles
Los proxies móviles utilizan IP de operadores móviles (4G/5G). Una IP a menudo es utilizada por miles de usuarios reales simultáneamente, por lo que las APIs rara vez los bloquean.
Ventajas para la API:
- Máxima confianza: las APIs no pueden bloquear IP de operadores móviles
- Ideales para aplicaciones móviles y APIs (Instagram, TikTok, Snapchat)
- Cambio automático de IP al reconectar (modo avión)
- Una IP puede hacer más solicitudes sin ser bloqueada
Desventajas:
- Costo muy alto: de $50-150 por una IP al mes
- Grupos pequeños: difícil obtener cientos de IP móviles
- Velocidad variable: depende de la calidad de la señal móvil
Cuándo usar: trabajo con APIs móviles, scraping de Instagram/TikTok en grandes volúmenes, cuando se necesita la máxima protección contra bloqueos.
Proxies de centros de datos
Los proxies de centros de datos son direcciones IP de servidores ubicados en centros de datos. No están vinculados a usuarios reales.
Ventajas para la API:
- Bajo costo: de $1-5 por IP al mes
- Alta velocidad: canales de 1-10 Gbps
- Estabilidad: las IP no se desconectan aleatoriamente
- Grandes grupos: fácil obtener cientos de IP
Desventajas:
- Baja confianza: muchas APIs bloquean centros de datos
- Las IP a menudo están en listas negras debido a otros usuarios
- No son adecuadas para redes sociales y servicios estrictos
Cuándo usar: scraping de APIs públicas sin protección estricta (clima, tasas de cambio, noticias), trabajo con APIs propias, pruebas y desarrollo.
| Criterio | Residenciales | Móviles | Centros de datos |
|---|---|---|---|
| Confianza de la API | Alta | Máxima | Baja |
| Tamaño del grupo | Millones de IP | Cientos de IP | Miles de IP |
| Velocidad | Media (10-50 Mbps) | Media (5-100 Mbps) | Alta (100+ Mbps) |
| Costo | $5-15/GB | $50-150/IP/mes | $1-5/IP/mes |
| Para redes sociales | ✅ Excelente | ✅ Ideal | ❌ No adecuados |
| Para APIs públicas | ✅ Bien | ✅ Bien (caro) | ✅ Excelente |
Implementación práctica: ejemplos de código en Python
Consideremos ejemplos concretos de implementación para eludir el rate limiting utilizando proxies. Todos los ejemplos están en Python con la biblioteca requests.
Rotación simple desde un grupo de proxies
Implementación básica con rotación cíclica de IP desde una lista:
import requests
import time
from itertools import cycle
# Lista de proxies (formato: protocol://user:pass@host:port)
PROXY_LIST = [
'http://user1:pass1@proxy1.example.com:8080',
'http://user2:pass2@proxy2.example.com:8080',
'http://user3:pass3@proxy3.example.com:8080',
]
# Creamos un iterador cíclico
proxy_pool = cycle(PROXY_LIST)
def make_request(url):
proxy = next(proxy_pool) # Tomamos el siguiente proxy del grupo
proxies = {
'http': proxy,
'https': proxy
}
try:
response = requests.get(url, proxies=proxies, timeout=10)
return response
except requests.exceptions.RequestException as e:
print(f"Error con el proxy {proxy}: {e}")
return None
# Ejemplo de uso
for i in range(10):
response = make_request('https://api.example.com/data')
if response and response.status_code == 200:
print(f"Solicitud {i+1}: éxito")
time.sleep(2) # Retraso entre solicitudes
Rotación con seguimiento de límites
Una versión más avanzada, que cuenta las solicitudes para cada proxy y cambia al acercarse al límite:
import requests
import time
from collections import defaultdict
class ProxyRotator:
def __init__(self, proxy_list, max_requests_per_ip=90, time_window=3600):
self.proxy_list = proxy_list
self.max_requests = max_requests_per_ip # Límite de solicitudes por IP
self.time_window = time_window # Ventana de tiempo en segundos
self.request_counts = defaultdict(list) # Historial de solicitudes para cada IP
self.current_index = 0
def get_proxy(self):
"""Devuelve el proxy con la menor cantidad de solicitudes"""
current_time = time.time()
# Limpiamos registros antiguos fuera de la ventana de tiempo
for proxy in self.request_counts:
self.request_counts[proxy] = [
t for t in self.request_counts[proxy]
if current_time - t < self.time_window
]
# Buscamos el proxy con la menor cantidad de solicitudes
available_proxies = []
for proxy in self.proxy_list:
count = len(self.request_counts[proxy])
if count < self.max_requests:
available_proxies.append((proxy, count))
if not available_proxies:
# Si todos los proxies han alcanzado el límite, esperamos
oldest_request = min(
min(times) for times in self.request_counts.values() if times
)
wait_time = self.time_window - (current_time - oldest_request) + 1
print(f"Todos los proxies han alcanzado el límite. Esperando {wait_time:.0f} seg...")
time.sleep(wait_time)
return self.get_proxy()
# Elegimos el proxy con la menor cantidad de solicitudes
proxy = min(available_proxies, key=lambda x: x[1])[0]
self.request_counts[proxy].append(current_time)
return proxy
def make_request(self, url, **kwargs):
proxy = self.get_proxy()
proxies = {'http': proxy, 'https': proxy}
try:
response = requests.get(url, proxies=proxies, timeout=10, **kwargs)
return response
except requests.exceptions.RequestException as e:
print(f"Error con el proxy {proxy}: {e}")
return None
# Ejemplo de uso
PROXY_LIST = [
'http://user:pass@proxy1.example.com:8080',
'http://user:pass@proxy2.example.com:8080',
]
rotator = ProxyRotator(PROXY_LIST, max_requests_per_ip=100, time_window=3600)
for i in range(500): # Hacemos 500 solicitudes
response = rotator.make_request('https://api.example.com/data')
if response and response.status_code == 200:
print(f"Solicitud {i+1}: éxito")
time.sleep(1) # Retraso mínimo
Manejo de errores 429 con retraso exponencial
Manejo correcto de la respuesta "Too Many Requests" considerando el encabezado Retry-After:
import requests
import time
def make_request_with_retry(url, proxies, max_retries=5):
"""Realiza una solicitud con repeticiones automáticas en caso de error 429"""
for attempt in range(max_retries):
try:
response = requests.get(url, proxies=proxies, timeout=10)
if response.status_code == 200:
return response
elif response.status_code == 429:
# Verificamos el encabezado Retry-After
retry_after = response.headers.get('Retry-After')
if retry_after:
wait_time = int(retry_after)
print(f"Límite de tasa. Esperando {wait_time} seg (desde Retry-After)")
else:
# Retraso exponencial: 2^attempt segundos
wait_time = 2 ** attempt
print(f"Límite de tasa. Intento {attempt+1}, esperando {wait_time} seg")
time.sleep(wait_time)
continue
else:
print(f"Error HTTP {response.status_code}")
return None
except requests.exceptions.RequestException as e:
print(f"Error de conexión: {e}")
if attempt < max_retries - 1:
time.sleep(2 ** attempt)
continue
return None
print(f"Se ha superado el número de intentos ({max_retries})")
return None
# Ejemplo de uso
proxies = {
'http': 'http://user:pass@proxy.example.com:8080',
'https': 'http://user:pass@proxy.example.com:8080'
}
response = make_request_with_retry('https://api.example.com/data', proxies)
if response:
print("Datos recibidos:", response.json())
Uso de proxies residenciales con rotación automática
Muchos proveedores de proxies residenciales ofrecen un solo endpoint que cambia automáticamente la IP en cada solicitud. Ejemplo de configuración:
import requests
import random
import time
# Proxies residenciales con rotación automática
# Formato: protocol://username:password@gateway:port
ROTATING_PROXY = 'http://customer-USER:PASS@proxy.provider.com:12321'
def make_request_rotating(url):
"""Solicitud a través de un proxy rotativo (nueva IP cada vez)"""
proxies = {
'http': ROTATING_PROXY,
'https': ROTATING_PROXY
}
# Añadimos un User-Agent aleatorio para mayor anonimato
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36',
]
headers = {
'User-Agent': random.choice(user_agents)
}
try:
response = requests.get(url, proxies=proxies, headers=headers, timeout=15)
return response
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
return None
# Hacemos 100 solicitudes a través de diferentes IP
for i in range(100):
response = make_request_rotating('https://api.example.com/data')
if response and response.status_code == 200:
print(f"Solicitud {i+1}: éxito, IP cambiada")
# Retraso aleatorio de 1-3 segundos
time.sleep(random.uniform(1, 3))
Monitoreo de límites y manejo de errores
Trabajar de manera efectiva con APIs requiere monitoreo constante de límites y manejo adecuado de errores. Aquí están las prácticas clave:
Análisis de encabezados de respuesta
Muchas APIs devuelven información sobre límites en los encabezados de respuesta. Encabezados estándar:
X-RateLimit-Limit— número máximo de solicitudes en la ventanaX-RateLimit-Remaining— cuántas solicitudes quedanX-RateLimit-Reset— tiempo de restablecimiento del límite (timestamp de Unix)Retry-After— en cuántos segundos se puede repetir la solicitud
Ejemplo de lectura de estos encabezados:
response = requests.get(url, proxies=proxies)
# Verificamos los encabezados de límites
limit = response.headers.get('X-RateLimit-Limit')
remaining = response.headers.get('X-RateLimit-Remaining')
reset_time = response.headers.get('X-RateLimit-Reset')
if remaining:
remaining = int(remaining)
if remaining < 10:
print(f"¡Atención! Solo quedan {remaining} solicitudes")
if reset_time:
import datetime
reset_dt = datetime.datetime.fromtimestamp(int(reset_time))
print(f"El límite se restablecerá a las {reset_dt}")
Registro y estadísticas
Lleva un registro detallado de las solicitudes para cada proxy. Esto ayudará a identificar IP problemáticas y optimizar la rotación:
import json
from datetime import datetime
class RequestLogger:
def __init__(self):
self.stats = {}
def log_request(self, proxy, status_code, response_time):
if proxy not in self.stats:
self.stats[proxy] = {
'total': 0,
'success': 0,
'rate_limited': 0,
'errors': 0,
'avg_response_time': 0
}
self.stats[proxy]['total'] += 1
if status_code == 200:
self.stats[proxy]['success'] += 1
elif status_code == 429:
self.stats[proxy]['rate_limited'] += 1
else:
self.stats[proxy]['errors'] += 1
# Actualizamos el tiempo promedio de respuesta
current_avg = self.stats[proxy]['avg_response_time']
total = self.stats[proxy]['total']
self.stats[proxy]['avg_response_time'] = (
(current_avg * (total - 1) + response_time) / total
)
def print_stats(self):
print("\n=== Estadísticas de proxies ===")
for proxy, data in self.stats.items():
success_rate = (data['success'] / data['total'] * 100) if data['total'] > 0 else 0
print(f"\nProxy: {proxy}")
print(f" Total de solicitudes: {data['total']}")
print(f" Exitosas: {data['success']} ({success_rate:.1f}%)")
print(f" Limitadas: {data['rate_limited']}")
print(f" Errores: {data['errors']}")
print(f" Tiempo promedio de respuesta: {data['avg_response_time']:.2f}s")
# Uso
logger = RequestLogger()
start_time = time.time()
response = requests.get(url, proxies=proxies)
response_time = time.time() - start_time
logger.log_request(proxy, response.status_code, response_time)
logger.print_stats()
Cambio automático de estrategia
Si recibes constantemente errores 429, ralentiza automáticamente las solicitudes o aumenta el grupo de proxies:
class AdaptiveRateLimiter:
def __init__(self, initial_delay=1.0):
self.delay = initial_delay
self.consecutive_429 = 0
def on_success(self):
"""Solicitud exitosa - se puede acelerar un poco"""
self.consecutive_429 = 0
self.delay = max(0.5, self.delay * 0.95) # Reducimos el retraso en un 5%
def on_rate_limit(self):
"""Recibimos 429 - necesitamos ralentizarnos"""
self.consecutive_429 += 1
self.delay *= 1.5 # Aumentamos el retraso en 1.5 veces
if self.consecutive_429 > 5:
print("ATENCIÓN: Demasiados errores 429. ¡Verifica la configuración!")
def wait(self):
"""Espera antes de la siguiente solicitud"""
time.sleep(self.delay)
return self.delay
# Uso
limiter = AdaptiveRateLimiter(initial_delay=2.0)
for i in range(1000):
response = make_request(url)
if response.status_code == 200:
limiter.on_success()
elif response.status_code == 429:
limiter.on_rate_limit()
delay = limiter.wait()
print(f"Solicitud {i+1}, retraso: {delay:.2f}s")
Manejo de captcha y otros bloqueos
Algunas APIs muestran un captcha en lugar de un bloqueo directo al exceder los límites. Señales:
- Código de estado 403 con cuerpo de respuesta que contiene "captcha" o "recaptcha"
- Redirección a una página con captcha (código de estado 302)
- Encabezados especiales como
X-Captcha-Required: true
En tales casos, se debe:
- Detener inmediatamente el uso de esta IP
- Cambiar a otro proxy del grupo
- Aumentar los retrasos entre solicitudes
- Agregar más diversidad en User-Agent y otros encabezados
Importante: Si te enfrentas regularmente a captchas al usar proxies residenciales, el problema probablemente esté en los patrones de comportamiento (encabezados idénticos, solicitudes demasiado rápidas), y no en las direcciones IP.
Conclusión
Eludir el rate limiting de la API al usar proxies no es solo una configuración técnica, sino una estrategia integral que incluye la elección correcta del tipo de proxy, la configuración de rotación de IP, la gestión de retrasos y el monitoreo de límites. Conclusiones clave del artículo:
- Los proxies por sí solos no resuelven el problema del rate limiting — se necesita una estrategia de rotación adecuada
- Para redes sociales y APIs estrictas, utiliza proxies residenciales o móviles; para APIs públicas, los centros de datos son adecuados
- Calcula el tamaño del grupo de proxies según los límites de la API y la velocidad de scraping deseada
- Siempre verifica los encabezados de respuesta para ajustar tu estrategia de manera dinámica