Requests es una de las bibliotecas más populares para realizar solicitudes HTTP con Python. Permite descargar páginas, consultar APIs, enviar formularios, trabajar con JSON y mantener sesiones mediante una interfaz sencilla. Para usarla de forma fiable debes configurar tiempos de espera, validar estados, controlar redirecciones y tratar las respuestas como datos externos.
Para ampliar conocimientos, consulta las guías sobre consumir APIs REST, JSON, excepciones, logging y entornos virtuales. Como referencias externas, consulta el Quickstart oficial de Requests y la guía oficial de uso avanzado.
Instalación correcta
python -m pip install requestsUtilizar python -m pip ayuda a instalar el paquete en el intérprete correcto. Comprueba después con python -c "import requests; print(requests.__version__)".
Solicitud GET básica
import requests
respuesta = requests.get(
"https://httpbin.org/get",
timeout=10,
)
print(respuesta.status_code)
print(respuesta.text)Siempre configura un tiempo de espera. Sin él, una conexión problemática puede mantener el proceso bloqueado indefinidamente.
Validar el estado
respuesta.raise_for_status()Este método lanza una excepción para respuestas de error. Aun así, debes interpretar algunos estados según la aplicación: una respuesta 404 puede ser un resultado esperado al buscar un recurso inexistente.
Parámetros y cabeceras
parametros = {"q": "python", "page": 1}
cabeceras = {"Accept": "application/json"}
respuesta = requests.get(
"https://api.example.com/search",
params=parametros,
headers=cabeceras,
timeout=10,
)No concatentes manualmente parámetros. Requests se encarga de codificar caracteres y construir la URL final.
Trabajar con JSON
respuesta.raise_for_status()
try:
datos = respuesta.json()
except requests.exceptions.JSONDecodeError:
print("La respuesta no contiene JSON válido")El encabezado del servidor puede ser incorrecto y un estado 200 puede contener HTML de error. Valida estructura y tipos después de decodificar.
POST con cuerpo JSON
payload = {
"nombre": "Ana",
"activo": True,
}
respuesta = requests.post(
"https://api.example.com/usuarios",
json=payload,
timeout=10,
)El parámetro json serializa el diccionario y configura Content-Type. Para formularios tradicionales utiliza data.
Subir archivos
with open("informe.pdf", "rb") as archivo:
respuesta = requests.post(
"https://api.example.com/archivos",
files={"archivo": ("informe.pdf", archivo, "application/pdf")},
timeout=30,
)Abre archivos en modo binario y limita tamaño antes de enviarlos. No reutilices un descriptor cerrado en reintentos.
Autenticación
import os
token = os.environ["API_TOKEN"]
respuesta = requests.get(
url,
headers={"Authorization": f"Bearer {token}"},
timeout=10,
)No almacenes tokens en el repositorio ni los muestres en logs. También puedes usar autenticación básica mediante auth, siempre sobre HTTPS y únicamente cuando el servicio lo admita.
Session para conexiones reutilizables
with requests.Session() as sesion:
sesion.headers.update({"User-Agent": "MiCliente/1.0"})
primera = sesion.get(url_1, timeout=10)
segunda = sesion.get(url_2, timeout=10)Una sesión conserva cookies, cabeceras y reutiliza conexiones, lo que mejora eficiencia en múltiples solicitudes al mismo servidor.
Cookies
Requests administra cookies dentro de una sesión. No copies cookies de usuarios ni las guardes sin necesidad. Algunas representan sesiones autenticadas y deben protegerse como credenciales.
Redirecciones
respuesta = requests.get(
url,
allow_redirects=False,
timeout=10,
)Las solicitudes GET siguen redirecciones normalmente. En sistemas sensibles, valida el destino final para evitar enviar información a un dominio inesperado.
Descargas grandes
with requests.get(url, stream=True, timeout=30) as respuesta:
respuesta.raise_for_status()
with open("descarga.bin", "wb") as salida:
for bloque in respuesta.iter_content(chunk_size=1024 * 1024):
if bloque:
salida.write(bloque)Comprueba la cabecera de tamaño cuando exista, establece un límite propio y valida el archivo antes de procesarlo.
Tiempos de espera
Una tupla como timeout=(3, 20) separa conexión y lectura. No existe un valor universal: una interfaz interactiva necesita respuestas rápidas, mientras que una descarga legítima puede requerir más tiempo.
Manejo de errores
try:
respuesta = requests.get(url, timeout=10)
respuesta.raise_for_status()
except requests.exceptions.Timeout:
logging.warning("Tiempo agotado")
except requests.exceptions.ConnectionError:
logging.warning("No fue posible conectar")
except requests.exceptions.HTTPError as error:
logging.warning("Estado HTTP %s", error.response.status_code)
except requests.exceptions.RequestException:
logging.exception("Fallo inesperado de Requests")Reintentos y adaptadores
Los reintentos deben aplicarse a fallos temporales y métodos apropiados. Configura un máximo, espera creciente y una lista concreta de estados. No repitas automáticamente operaciones que puedan crear cargos o registros duplicados.
Proxies y certificados
Requests respeta configuraciones de proxy del entorno. Mantén la verificación TLS activada. Usar verify=False oculta el síntoma y permite ataques de intermediario; instala la autoridad certificadora correcta o corrige la red.
Pruebas
Simula respuestas para probar éxito, JSON inválido, redirecciones, timeout y errores HTTP. Una prueba no debería depender de que un servicio externo esté disponible. También comprueba que tokens y cabeceras sensibles nunca aparezcan en mensajes.
Errores frecuentes
Los errores más comunes son omitir timeout, llamar a json() sin validar, desactivar TLS, construir URLs a mano, descargar todo en memoria y usar una sesión global sin cerrarla. Otro fallo es tratar cualquier código diferente de 200 como error, ignorando respuestas válidas como 201 o 204.
Conclusión
Requests simplifica HTTP, pero un cliente sólido necesita decisiones explícitas. Configura tiempos de espera, valida estados y datos, utiliza sesiones, protege credenciales y transmite archivos por streaming. Con manejo de errores y pruebas aisladas, tus integraciones serán más seguras y fáciles de mantener.





