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:
- Guarda las claves en variables de entorno, o en un archivo
.envque añadas a.gitignore. - 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,
)
- No imprimas ni registres las cabeceras completas: contienen tu token.
- Da a cada token solo los permisos que necesita y rótalo periódicamente.