Códigos de estado HTTP en web scraping: 403, 407, 429, 503

Publicado:

17 min de lectura

Acar Diveroli
Autor: Acar Diveroli
Un sobre de petición que llega a tres columnas de códigos de error coronadas por un candado, una llave y un reloj

Las líneas más frecuentes en el registro de un scraper no son las respuestas 200 correctas, sino los códigos 403, 429 y 503 que aparecen entre ellas. Cada uno de estos códigos describe un problema distinto y requiere una reacción distinta. Un script que los trata todos igual, «reintentar tres veces», o bien insiste contra un bloqueo permanente y empeora las cosas, o bien pierde datos por un problema temporal que se habría resuelto en unos segundos.

En este artículo explicamos cómo leer los códigos de estado HTTP, qué significan los códigos con los que más te encuentras en el scraping (403, 407, 429 y 503) y las causas probables que hay detrás. Después vemos 502, 504 y los errores de conexión, cómo usar la cabecera Retry-After, y reunimos en una sola tabla qué códigos reintentar y ante cuáles parar. Al final hay un ejemplo en Python probado que aplica estas decisiones.

¿Cómo se leen los códigos de estado HTTP?

Toda respuesta HTTP empieza con un código de estado de tres dígitos. El primer dígito fija la clase de la respuesta. Los códigos están definidos en la sección 15 de RFC 9110; la lista de códigos de estado de MDN también da explicaciones breves y ejemplos de cada uno.

ClaseSignificadoEn términos de scraping
1xxInformativo, petición en cursoEn la práctica no los verás
2xxÉxitoLa página llegó, pero revisa igualmente su contenido
3xxRedirecciónLa dirección cambió o te enviaron a una página de inicio de sesión
4xxProblema del lado del clienteAlgo falla en tu petición, tu identidad o tu velocidad
5xxProblema del lado del servidorEl servidor, la pasarela o el proxy no pudieron completar la petición

La distinción más importante para el scraping es esta: la mayoría de los códigos 4xx dan el mismo resultado cuando envías exactamente la misma petición otra vez. Tienes que cambiar algo de la petición. La mayoría de los códigos 5xx son temporales, y la misma petición puede funcionar tras esperar un rato. 429 es la excepción: es un código 4xx, pero se resuelve esperando.

Una advertencia más que vale para cualquier código: ver 200 no significa que hayas obtenido los datos que querías. Los sistemas de protección de bots a menudo devuelven una página de verificación con código 200. Antes de dar una respuesta por correcta, comprueba que la página contiene un elemento que esperas (un título de producto, un campo de precio).

¿Qué significa 403 Forbidden?

403 indica que el servidor entendió tu petición pero se niega a atenderla. Según RFC 9110, el servidor no está obligado a explicar por qué. La diferencia con 401 Unauthorized, que se usa cuando falta autenticación, es que con 403 añadir credenciales normalmente no cambia el resultado.

Causas probables de un 403 en el scraping:

  • Reputación o tipo de IP. Los sitios que restringen el tráfico de direcciones de centros de datos devuelven 403 directamente a esas direcciones.
  • Restricción geográfica. El sitio solo permite el acceso desde ciertos países.
  • Cabeceras ausentes o incoherentes. El valor User-Agent predeterminado de la librería o una combinación de cabeceras que nunca envía un navegador.
  • Una regla de firewall de aplicaciones web (WAF). La petición coincidió con una regla de seguridad.
  • Bloqueo por tu tráfico anterior. Una dirección IP que supera el límite de velocidad durante mucho tiempo puede empezar a recibir 403 en lugar de 429 al cabo de un tiempo.
  • Una página que de verdad requiere autorización. Una ruta a la que no se puede llegar sin iniciar sesión.

Qué hacer: no vuelvas a enviar la misma petición enseguida. Prueba a abrir la dirección en un navegador real con la misma IP. Si se abre en el navegador, el problema está en tu propia petición (cabeceras, velocidad); si no, el problema es la IP o la ubicación. Explicamos en detalle las causas de los bloqueos y las soluciones legítimas en Cómo hacer web scraping sin que te bloqueen.

¿Qué significa 407 Proxy Authentication Required?

407 no viene del sitio de destino, sino del servidor proxy. Significa que el proxy quiere que demuestres tu identidad antes de reenviar la petición al destino. Así que, cuando ves un 407, tienes que revisar tu conexión con el proxy, no las reglas del sitio de destino.

Causas probables:

  • El usuario o la contraseña son incorrectos.
  • La contraseña contiene caracteres especiales como @, : o / que no están codificados dentro de la dirección del proxy (@%40).
  • Se usa el método de lista blanca de IP, pero la dirección IP de la que viene la conexión no está en la lista.
  • La librería no envía al proxy las credenciales incluidas en la dirección.
  • Se ha agotado el saldo o la cuota de tráfico de la cuenta.

Las librerías muestran el 407 de formas distintas. En las peticiones HTTPS no se puede establecer el túnel del proxy, así que a menudo recibes una excepción en lugar de un objeto de respuesta: Python Requests lanza ProxyError, y HTTPX también lanza ProxyError; en Node.js, Axios devuelve un error con código de estado 407. Mostramos las diferencias entre librerías con ejemplos probados en Cómo usar un proxy en Node.js.

Qué hacer: no reintentes; corrige la configuración. Comprueba en la salida de curl -v si se envía la cabecera Proxy-Authorization; el comando está en Cómo usar un proxy con cURL. Recorremos paso a paso los dos métodos de autenticación y el diagnóstico del 407 en Autenticación de proxy: user:pass o lista blanca de IP.

¿Qué significa 429 Too Many Requests?

429 indica que enviaste demasiadas peticiones en un periodo determinado. El código está definido en la sección 4 de RFC 6585, que establece que el servidor puede enviar con la respuesta una cabecera Retry-After para indicar cuánto debes esperar.

Sobre qué se cuenta el límite de velocidad varía según el sitio:

  • Por dirección IP: se suman las peticiones desde la misma dirección.
  • Por sesión o cookie: se suman las peticiones de la misma sesión; cambiar de IP no reinicia el contador.
  • Por clave de API: en las API oficiales, el límite suele ir ligado a la clave.
  • Por endpoint: en endpoints pesados como las páginas de búsqueda, el límite es menor que en las páginas de producto.

Algunas API informan de tu cuota restante con cabeceras como X-RateLimit-Remaining y X-RateLimit-Reset. Estos nombres de cabecera no son estándar y cambian de un servicio a otro; consulta la documentación de la API que uses.

Qué hacer: si hay Retry-After, espera ese tiempo. Si no, aplica backoff exponencial: 1 segundo en el primer intento y después 2, 4 y 8 segundos, añadiendo un componente aleatorio a cada espera. Baja la concurrencia al mismo tiempo; si esperas y sigues a la misma velocidad, pronto volverás a recibir 429.

¿Qué significa 503 Service Unavailable?

503 indica que el servidor no puede atender la petición en este momento, pero que la situación es temporal. El mantenimiento, la sobrecarga o un servidor de aplicación caído detrás son causas típicas. Según RFC 9110, el servidor puede indicar con Retry-After cuándo volver a intentarlo.

En el scraping, 503 tiene dos caras distintas:

  • Carga real o mantenimiento. El sitio devuelve 503 a todo el mundo. No queda más que esperar.
  • Un bloqueo temporal de la protección de bots. Algunos sistemas de protección devuelven 503 con una página de verificación al tráfico que consideran sospechoso. Si el cuerpo contiene una pantalla de verificación, el problema no es la carga del servidor sino tu propio tráfico.

Qué hacer: distingue los dos casos mirando el cuerpo. Si es carga real, espera con Retry-After o backoff exponencial. Si ves una página de verificación, revisa tu velocidad y tu identidad de cliente en lugar de reintentar.

502, 504 y errores de conexión

Un scraper que usa un proxy se topa con errores del propio proxy además de con las respuestas del sitio de destino. Estos códigos sirven para averiguar en qué eslabón de la cadena está el problema.

  • 500 Internal Server Error: se produjo un error en la aplicación de destino. A veces es específico de una página. Puede reintentarse una o dos veces; si se repite, omite esa dirección.
  • 502 Bad Gateway: una pasarela intermedia (proxy, CDN o reverse proxy) no pudo obtener una respuesta válida del servidor que tiene detrás. En el contexto de un proxy, puede significar que el proxy no pudo llegar al destino. Reintenta tras una breve espera.
  • 504 Gateway Timeout: la pasarela no recibió a tiempo la respuesta del servidor de detrás. Aparece cuando el destino es lento o el punto de salida del proxy está lejos. Reintenta.
  • 408 Request Timeout: el servidor agotó el tiempo esperando a que se completara la petición. Reintenta.
  • Errores de conexión: no hay código; el cliente lanza una excepción. Conexión rechazada (ECONNREFUSED), conexión restablecida (ECONNRESET), timeout, host no encontrado (ENOTFOUND), etc. Si la dirección o el puerto son incorrectos, ningún reintento lo arreglará; en situaciones temporales, como una caída de red, reintentar ayuda.
  • Códigos propios de CDN: algunas CDN usan códigos no estándar en el rango 520-526. Suelen describir problemas de conexión entre la CDN y el servidor propio del sitio.

404 Not Found y 410 Gone indican que la página no existe. En lugar de reintentar, quita la dirección de tu lista; si de repente ves muchos 404, puede que haya cambiado la estructura de URL del sitio.

¿Cómo se usa Retry-After?

La cabecera Retry-After puede llegar de dos formas:

  • Un número de segundos: Retry-After: 120 → espera 120 segundos.
  • Una fecha HTTP: Retry-After: Wed, 16 Sep 2026 07:28:00 GMT → inténtalo después de esa fecha.

Cuatro reglas para usarla bien:

  1. Si la cabecera está, no adivines; síguela. El tiempo que da el servidor es más preciso que cualquier backoff que calcules.
  2. Fija un límite superior. Si la cabecera indica algo largo, como varias horas, deja la dirección para una ejecución posterior en lugar de dejar tu script colgado todo ese tiempo.
  3. Aplica la espera al sitio, no solo a esa petición. Si retienes la petición que recibió el 429 pero sigues enviando en paralelo otras peticiones al mismo sitio, el límite se sigue superando.
  4. Interpreta también la forma de fecha. Un código que solo espera un número ignora una cabecera que llega como fecha. Compartimos una función de Python que gestiona ambas formas en Cómo hacer web scraping sin que te bloqueen.

¿Qué códigos se reintentan y ante cuáles se para?

CódigoSignificadoCausa probable en el scrapingQué hacer
200ÉxitoLa página llegó, pero puede ser una página de verificaciónComprueba que el contenido tiene un elemento esperado
301 / 302RedirecciónLa dirección cambió o te enviaron a una página de inicio de sesiónSigue la redirección; si es una página de inicio de sesión, revisa la sesión
400Petición incorrectaParámetro o cuerpo defectuosoPara y corrige la petición
401Se requiere autenticaciónFalta el inicio de sesión o la clave de APIPara y añade credenciales
403RechazadaTipo de IP, ubicación, cabeceras, regla WAFPara y diagnostica
404 / 410Página no encontradaProducto eliminado, estructura de URL cambiadaQuítala de la lista
407Se requiere autenticación del proxyContraseña incorrecta, carácter sin codificar, lista blancaPara y corrige la configuración del proxy
408Timeout de la peticiónConexión lentaReintenta
429Demasiadas peticionesLímite de velocidad superadoEspera lo que indique Retry-After, ve más despacio
500Error del servidorError en la aplicación de destinoPrueba unas pocas veces y luego omite
502Pasarela incorrectaEl proxy o la CDN no pudieron llegar al destinoReintenta tras una breve espera
503Servicio no disponibleCarga, mantenimiento o página de protecciónRevisa el cuerpo; espera con Retry-After
504Timeout de pasarelaDestino lento, punto de salida lejanoReintenta; usa una ubicación más cercana si hace falta
Error de conexiónSin respuestaCaída de red, dirección o puerto incorrectosReintenta si es temporal; corrige la configuración si es permanente

Ejemplo: reintentos que deciden según el código de estado

El ejemplo siguiente usa el cliente asíncrono de HTTPX. Con los códigos 408, 429 y 5xx respeta la cabecera Retry-After; si no hay cabecera, aplica backoff exponencial con un componente aleatorio. Con los códigos en los que reintentar no cambia el resultado, como 403, 404 y 407, se detiene lanzando una excepción aparte. Los errores de autenticación del proxy (que en las peticiones HTTPS llegan como ProxyError) también entran en este grupo.

python
import asyncio
import random

import httpx

RETRY_STATUS = {408, 429, 500, 502, 503, 504}
STOP_STATUS = {400, 401, 403, 404, 407, 410}


class StopScraping(Exception):
    """Casos en los que reintentar no cambia el resultado."""


def backoff(response, attempt, base=1.0, cap=60.0):
    retry_after = response.headers.get("Retry-After") if response is not None else None
    if retry_after and retry_after.isdigit():
        return min(int(retry_after), cap)
    return min(cap, base * 2**attempt) * random.uniform(0.5, 1.0)


async def fetch(client, url, attempts=5):
    for attempt in range(attempts):
        response = None
        try:
            response = await client.get(url)
        except httpx.ProxyError as exc:
            raise StopScraping(f"Error de proxy (revisa tus credenciales): {exc}") from exc
        except httpx.TransportError:
            pass  # conexión caída, timeout: se puede reintentar
        else:
            status = response.status_code
            if status < 400:
                return response
            if status in STOP_STATUS:
                raise StopScraping(f"{url}: HTTP {status}, no se reintentará")
            if status not in RETRY_STATUS:
                return response
        await asyncio.sleep(backoff(response, attempt))
    raise RuntimeError(f"{url}: sin resultado tras {attempts} intentos")


async def main():
    proxy = "http://user:pass@pr.proxynet.io:8000"
    async with httpx.AsyncClient(proxy=proxy, timeout=20, follow_redirects=True) as client:
        urls = ["https://example.com/product/1", "https://example.com/product/2"]
        results = await asyncio.gather(*(fetch(client, u) for u in urls), return_exceptions=True)
        for url, result in zip(urls, results):
            print(url, result if isinstance(result, Exception) else result.status_code)


asyncio.run(main())

Amplía el ejemplo en dos puntos para tu propio trabajo. Primero, asyncio.gather inicia todas las direcciones a la vez; en un trabajo real, limita con asyncio.Semaphore el número de peticiones simultáneas al mismo sitio. Segundo, Retry-After también puede llegar como fecha HTTP; si necesitas interpretar esa forma, usa la función enlazada más arriba.

Registrar y vigilar los errores

Usa los códigos de estado no solo para decidir en el momento, sino también para vigilar la salud del trabajo. Registrar estas cifras en cada ejecución te ayuda a detectar problemas pronto:

  • Respuestas por código: si sube la tasa de 429, vas demasiado rápido; si sube la de 403, algo ha cambiado en el lado de la IP o de las cabeceras.
  • Distribución por sitio y endpoint: ¿el problema está en un sitio o en todos? Los errores que suben a la vez en todos los sitios suelen apuntar al proxy o a la red.
  • Número de reintentos y tiempo total de espera: si los reintentos ocupan buena parte del tiempo del trabajo, el ajuste de concurrencia es incorrecto.
  • Respuestas 200 sin el elemento esperado: la única señal de una pérdida de datos silenciosa.

Casos de uso

  • Seguimiento diario de precios: los productos que devuelven 404 se quitan de la lista, y en la siguiente ejecución se baja la concurrencia en los sitios que devolvieron 429. La configuración general está en nuestra página de solución de extracción de datos.
  • Recogida de catálogos de muchos sitios: una tasa de 403 creciente en un sitio necesita un diagnóstico aparte para ese sitio; para repartir la carga se usa un Proxies rotativos.
  • Tu propio panel que necesita sesión: una redirección 302 a la página de inicio de sesión puede indicar que la IP cambió en mitad de la sesión; para mantener la misma IP durante la sesión se prefiere un Proxies de sesión fija.
  • Una configuración de proxy nueva: si las primeras peticiones devuelven 407 o ProxyError, el problema no es el destino sino las credenciales.

Errores comunes

  • Reintentar el mismo número de veces ante cualquier error. Repetir 403 y 407 no cambia el resultado; solo genera tráfico innecesario.
  • No leer la cabecera Retry-After. Volver antes de tiempo por tu cuenta cuando el servidor te dijo cuánto esperar.
  • No añadir un componente aleatorio al backoff. Cientos de peticiones que fallaron en el mismo momento se reintentan en el mismo momento y crean una nueva acumulación.
  • Aceptar una respuesta 200 sin mirar el contenido. Las páginas de verificación producen datos vacíos en silencio.
  • Tratar el 407 como un error del sitio de destino. Tienes que revisar la configuración de tu proxy, no el destino.
  • No poner límite a los reintentos. Un bucle que sigue para siempre ante un problema permanente desgasta tanto tus recursos como el sitio de destino.

Guía de decisión

Lo que vesLo primero que hay que hacer
429Espera lo que indique Retry-After, baja la concurrencia
503 con una página de error normalEspera y reintenta
503 con una página de verificaciónPara, revisa la velocidad y la identidad de cliente
403Prueba con la misma IP en un navegador y diagnostica la causa
407 o ProxyErrorRevisa el usuario, la contraseña y la lista blanca del proxy
502 / 504Reintenta tras una breve espera; si continúa, cambia la ubicación de salida
404 / 410Quita la dirección de la lista
200 pero sin datosComprueba si es una página de verificación o una carga dinámica

Preguntas frecuentes

¿Qué diferencia hay entre 403 y 401?

401 Unauthorized indica que la petición requiere autenticación y que no enviaste credenciales válidas. 403 Forbidden indica que el servidor entendió la petición pero la rechaza con independencia de las credenciales. Con 401, añadir credenciales puede ser la solución; con 403, normalmente no.

¿Por qué el error 407 viene del proxy y no del sitio de destino?

Porque la petición nunca llegó al destino. El proxy te autentica antes de reenviar la conexión y, si la autenticación falla, responde él mismo. Por eso el 407 no tiene nada que ver con las reglas del sitio de destino.

¿Cambiar de IP tras un 429 es una solución?

Si el límite de velocidad se cuenta por IP, puede ayudar temporalmente, pero no cambia la velocidad que causó el problema, y no ayuda en absoluto si el límite es por sesión o por cuenta. La reacción correcta es esperar e ir más despacio; la rotación debe planificarse desde el principio para repartir la carga.

¿Cuánto debo esperar si no hay cabecera Retry-After?

Aplica backoff exponencial: empieza con unos segundos, duplica en cada intento, fija un límite superior y añade un componente aleatorio a cada espera. Si sigue fallando tras unos pocos intentos, deja la dirección para una ejecución posterior.

¿Un error 503 significa que el sitio está caído?

No siempre. También puede ser mantenimiento, carga temporal o un bloqueo de la protección de bots. Mira el cuerpo de la respuesta: ¿es un mensaje de mantenimiento, una página de error genérica o una pantalla de verificación?

Recibo errores 502 al usar un proxy. ¿Dónde está el problema?

502 indica que la pasarela intermedia no pudo obtener una respuesta válida del servidor de detrás. En el contexto de un proxy, puede significar que el proxy no pudo llegar al destino o que el destino cortó la conexión. Prueba la misma dirección sin proxy: si funciona sin proxy, el problema está en el punto de salida del proxy; si tampoco funciona sin proxy, el problema está en el sitio de destino.

En resumen

Los códigos de estado HTTP le dicen a tu scraper qué hacer: 429 y 503 piden esperar, 403 pide un diagnóstico, 407 pide corregir la configuración del proxy y 404 pide quitar la dirección. Si hay una cabecera Retry-After, síguela; si no, usa backoff exponencial con un componente aleatorio y pon límite a todo bucle de reintentos. No olvides revisar también el contenido de las respuestas 200. Encontrarás tipos de proxy adecuados para tu trabajo de recogida de datos en nuestros servicios de proxy.