Timeouts, errores y autenticación en Resquests [Python]

Un programa que consume APIs tiene que sobrevivir a servidores lentos, errores HTTP y credenciales. Esta parte cubre los tres temas.

En el articulo anterior vimos Params, headers y POST con JSON usando Requests en Python

Por qué un script sin manejo de errores es frágil

En tu ordenador, con buena conexión, casi todas las peticiones funcionan y es fácil pensar que el código es correcto. En producción ocurre lo contrario: el DNS falla, el servidor se satura, la API cambia su formato o tu token caduca.

Un programa robusto no asume que todo saldrá bien; decide de antemano qué hacer en cada caso. Esta parte te da las tres piezas para conseguirlo: límites de tiempo, excepciones y credenciales bien manejadas.

Paso 5: Timeouts y manejo de errores

Siempre pasa timeout: requests no tiene ninguno por defecto, y un servidor que no responde deja tu programa colgado para siempre.

import requests

# Un número = mismo límite para conectar y para leer
requests.get("https://httpbin.org/get", timeout=5)

# Una tupla = (segundos para conectar, segundos para leer)
requests.get("https://httpbin.org/get", timeout=(3.05, 10))

Un código 404 o 500 no lanza excepción por sí solo. Llama a raise_for_status() para convertirlo en error, y captura las excepciones de la jerarquía de requests:

import requests

def obtener_json(url):
    try:
        r = requests.get(url, timeout=5)
        r.raise_for_status()          # lanza HTTPError si el código es 4xx o 5xx
        return r.json()
    except requests.exceptions.Timeout:
        print("El servidor tardó demasiado en responder")
    except requests.exceptions.ConnectionError:
        print("No se pudo conectar (DNS, red caída o servidor apagado)")
    except requests.exceptions.HTTPError as e:
        print(f"Error HTTP {e.response.status_code}")
    except requests.exceptions.JSONDecodeError:
        print("La respuesta no es JSON válido")
    except requests.exceptions.RequestException as e:
        print(f"Error inesperado: {e}")
    return None

obtener_json("https://httpbin.org/status/404")   # Error HTTP 404
obtener_json("https://httpbin.org/delay/10")     # El servidor tardó demasiado...

El orden importa: RequestException es la clase base de todas las demás, así que va siempre al final.

Qué mide realmente el timeout

Es un detalle que confunde a muchos: el timeout no es un límite para toda la petición. El tiempo de conexión limita cuánto esperas a establecer el contacto con el servidor, y el tiempo de lectura limita cuánto esperas entre un bloque de datos y el siguiente.

Si un servidor envía un byte cada pocos segundos, la petición puede durar minutos sin activar el timeout. Para descargas largas, combina un timeout razonable con stream=True, que veremos en la Parte 4, y controla tú el tiempo total si lo necesitas.

Qué hacer con cada tipo de error

No todos los errores merecen la misma respuesta:

  • Timeout o error de conexión: suelen ser temporales, así que reintentar tiene sentido.
  • 400 o 422: tu petición está mal construida. Reintentar no sirve; hay que corregir el código.
  • 401: el token falta o ha caducado. Renuévalo antes de volver a intentarlo.
  • 403: estás autenticado pero no tienes permiso. Revisa los permisos de tu cuenta o token.
  • 429: superaste el límite de uso. Lee la cabecera Retry-After, si existe, y espera ese tiempo.
  • 5xx: falló el servidor; reintenta con espera creciente.

Paso 6: Autenticación con Basic y Bearer token

Las dos formas más comunes de autenticarte contra una API son usuario y contraseña (Basic) o un token en la cabecera Authorization (Bearer).

import os
import requests

# Basic: pasa una tupla (usuario, contraseña)
r = requests.get(
    "https://httpbin.org/basic-auth/agus/secreto",
    auth=("agus", "secreto"),
    timeout=10,
)
print(r.status_code)  # 200

# Bearer: el token va en la cabecera Authorization
token = os.environ["MI_TOKEN"]   # nunca escribas el token en el código
r = requests.get(
    "https://httpbin.org/bearer",
    headers={"Authorization": f"Bearer {token}"},
    timeout=10,
)
print(r.json())  # {'authenticated': True, 'token': '...'}

Lee siempre las claves desde variables de entorno (o un archivo .env fuera del repositorio) y nunca las subas a GitHub. Si una clave se filtra, revócala y genera otra.

Buenas prácticas para guardar tus claves

Un token en el código fuente termina, tarde o temprano, en un repositorio público. Sigue estas reglas desde el primer día:

  1. Guarda las claves en variables de entorno, o en un archivo .env que añadas a .gitignore.
  2. Cárgalo con la librería python-dotenv (pip install python-dotenv):
import os
import requests
from dotenv import load_dotenv

load_dotenv()                      # lee el archivo .env de la carpeta actual
token = os.environ["MI_TOKEN"]

r = requests.get(
    "https://httpbin.org/bearer",
    headers={"Authorization": f"Bearer {token}"},
    timeout=10,
)
  1. No imprimas ni registres las cabeceras completas: contienen tu token.
  2. Da a cada token solo los permisos que necesita y rótalo periódicamente.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *