httpx y requests
requests es el cliente HTTP síncrono clásico; httpx es su sucesor moderno con soporte asíncrono, HTTP/2 y valores predeterminados más estrictos. Ambos pertenecen al conjunto de herramientas de todo desarrollador Python para APIs, webhooks y llamadas de servicio a servicio.
Receta
import httpx
with httpx.Client(timeout=httpx.Timeout(5.0, connect=2.0)) as client:
response = client.get("https://httpbin.org/get", params={"q": "python"})
response.raise_for_status()
data = response.json()Cuándo usar esto:
- Llamar a APIs REST/JSON desde scripts, workers o tareas en segundo plano de FastAPI
- Necesidad de HTTP asíncrono en rutas
asyncio(httpx.AsyncClient) - Subir archivos o transmitir respuestas grandes
- Reemplazar llamadas
urllibad hoc con código legible y testeable
Ejemplo de Trabajo
from __future__ import annotations
import httpx
from pydantic import BaseModel
class User(BaseModel):
id: int
email: str
def fetch_user(user_id: int) -> User:
timeout = httpx.Timeout(10.0, connect=3.0)
with httpx.Client(base_url="https://api.example.com", timeout=timeout) as client:
response = client.get(f"/users/{user_id}")
response.raise_for_status()
return User.model_validate(response.json())
async def fetch_user_async(user_id: int) -> User:
timeout = httpx.Timeout(10.0, connect=3.0)
async with httpx.AsyncClient(base_url="https://api.example.com", timeout=timeout) as client:
response = await client.get(f"/users/{user_id}")
response.raise_for_status()
return User.model_validate(response.json())
if __name__ == "__main__":
print(fetch_user(1))Lo que esto demuestra:
base_urlevita repetir prefijos de hostTimeoutexplícito divide los límites de conexión y lecturaraise_for_status()convierte 4xx/5xx en excepciones- Pydantic valida las cargas útiles JSON en el límite
- El mismo patrón funciona de forma síncrona (
Client) y asíncrona (AsyncClient)
Inmersión Profunda
Cómo Funciona
- Agrupación de conexiones - Reutilizar un
Clientmantiene las sesiones TCP/TLS activas;httpx.get()de un solo uso crea un nuevo grupo en cada llamada. - Timeouts -
httpx.Timeoutacepta límites de conexión, lectura, escritura y agrupación; los valores no establecidos recurren al valor predeterminado (5s en httpx). - Redirecciones - Se siguen por defecto; desactiva con
follow_redirects=Falsepara OAuth o URLs firmadas. - Streaming -
client.stream("GET", url)produce fragmentos sin cargar el cuerpo completo en memoria.
Ciclo de Vida del Cliente
| Patrón | Usar Cuando |
|---|---|
with httpx.Client() | Scripts, herramientas CLI, workers síncronos |
| Cliente a nivel de módulo (inicio de la aplicación) | Aplicaciones FastAPI/Flask con reutilización de conexiones |
AsyncClient por solicitud | Raro; prefiere un cliente por ciclo de vida de la aplicación |
httpx.get() de un solo uso | Solo sondeos rápidos en REPL |
Notas de Python
# Mapea errores de transporte a tu capa de dominio
try:
response = client.post("/orders", json=payload)
response.raise_for_status()
except httpx.TimeoutException as exc:
raise ServiceUnavailable("upstream timeout") from exc
except httpx.HTTPStatusError as exc:
if exc.response.status_code == 404:
raise NotFound("order missing")
raiseTrampas Comunes
- Sin timeout establecido - Las esperas infinitas bloquean los workers y ocultan las interrupciones. Solución: pasa siempre
timeout=. - Crear un nuevo cliente por solicitud - Destruye la agrupación y ralentiza los apretones de manos TLS. Solución: reutiliza
Client/AsyncClient. - Confiar en JSON sin validación - La deriva del esquema causa
KeyErroren la lógica de negocio. Solución: valida con Pydantic en el límite HTTP. - Bloquear
requestsdentro de rutas asíncronas - Bloquea el bucle de eventos bajo carga. Solución: usahttpx.AsyncClientoasyncio.to_thread. - Ignorar redirecciones en POST - Algunas APIs devuelven 302 a un host diferente. Solución: registra
response.historyal depurar flujos de autenticación. - Secretos codificados en URLs - Los tokens de consulta terminan en los registros. Solución: usa encabezados (
Authorization) y redáctalos en el middleware de registro.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
urllib.request (stdlib) | Scripts sin dependencias en entornos restringidos | Necesitas timeouts, reintentos o ergonomía JSON |
aiohttp | Ecosistema asíncrono maduro ya en el proyecto | El estándar del equipo es httpx/FastAPI |
requests | Bases de código heredadas con uso intensivo de requests | Iniciando servicios asíncronos desde cero |
| SDK HTTP de la nube (boto3 botocore) | APIs de servicios AWS con firma | REST genérico a terceros |
Preguntas Frecuentes
¿Deben los nuevos proyectos usar httpx o requests?
Prefiere httpx para código nuevo: misma ergonomía más soporte asíncrono y HTTP/2. Mantén requests solo donde el coste de migración supere el beneficio.
¿Cómo comparto un AsyncClient entre rutas de FastAPI?
Crea el cliente en un manejador de ciclo de vida y almacénalo en app.state, luego inyéctalo a través de Depends en las rutas.
¿Verifica httpx los certificados TLS?
Sí, por defecto. Pasa verify=False solo en desarrollo local con certificados autofirmados; nunca en producción.
¿Cómo envío cargas de archivos multipart?
with open("report.pdf", "rb") as f:
client.post("/upload", files={"file": ("report.pdf", f, "application/pdf")})¿Qué pasa con HTTP/2?
Habilítalo con httpx.Client(http2=True) cuando el servidor lo soporte; útil para gateways de API multiplexados.
¿Cómo simulo peticiones HTTP en pruebas?
Usa httpx.MockTransport o pytest-httpx para devolver respuestas predefinidas sin E/S de red.
¿Debo reintentar las peticiones fallidas automáticamente?
No dentro del cliente por defecto; envuélvelo con tenacity o lógica de reintento a nivel de aplicación que respete la idempotencia.
¿Cómo establezco un User-Agent personalizado?
Pasa headers={"User-Agent": "my-service/1.0"} o establece client.headers en la instancia del cliente.
¿Puedo usar httpx con Django?
Sí, para llamadas salientes síncronas en vistas o comandos de gestión. Para vistas asíncronas de Django, usa AsyncClient.
¿Cómo registro cuerpos de petición/respuesta de forma segura?
Registra el método, URL, estado y duración; redácta los encabezados Authorization y nunca registres cargas útiles completas de tarjetas/PII.
Relacionado
- tenacity - reintentos y backoff para upstreams inestables
- pydantic-settings - configuración tipada para URLs base de API
- Conceptos básicos de FastAPI - rutas asíncronas que llaman a APIs externas
- beautifulsoup4 & lxml - análisis de HTML obtenido por HTTP
Versiones de Stack: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.