Rate limiting

Intermedio
FastAPI
FastAPI
Actualizado: 01/07/2025

¡Desbloquea el curso completo!

IA
Ejercicios
Certificado
Entrar

Instalación de slowapi

Para implementar rate limiting en nuestras aplicaciones FastAPI, utilizaremos la biblioteca slowapi, que es una adaptación de Flask-Limiter específicamente diseñada para FastAPI. Esta biblioteca nos permite controlar la frecuencia de las peticiones de manera sencilla y eficiente.

slowapi se integra perfectamente con FastAPI y proporciona decoradores que podemos aplicar directamente a nuestras rutas para establecer límites de peticiones. La biblioteca utiliza un sistema de ventanas deslizantes para contar las peticiones y aplicar las restricciones correspondientes.

Instalación mediante pip

La instalación de slowapi es directa utilizando el gestor de paquetes pip. Ejecuta el siguiente comando en tu terminal:

pip install slowapi

Para proyectos que requieren un control de versiones más estricto, puedes especificar una versión concreta:

pip install slowapi==0.1.9

Verificación de la instalación

Una vez completada la instalación, podemos verificar que slowapi está correctamente instalado creando un pequeño script de prueba:

from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded

print("slowapi instalado correctamente")
print(f"Versión disponible: {Limiter.__module__}")

Configuración básica en FastAPI

Para comenzar a utilizar slowapi en nuestra aplicación FastAPI, necesitamos importar los componentes principales y configurar un limitador básico:

from fastapi import FastAPI, Request
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded

# Crear el limitador usando la IP del cliente
limiter = Limiter(key_func=get_remote_address)

# Crear la aplicación FastAPI
app = FastAPI()

# Asociar el limitador con la aplicación
app.state.limiter = limiter

# Configurar el manejador de errores para rate limiting
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)

La función get_remote_address extrae automáticamente la dirección IP del cliente desde la petición HTTP, que será utilizada como clave para identificar y limitar las peticiones de cada usuario.

Dependencias adicionales

En algunos casos, especialmente cuando trabajamos con aplicaciones que utilizan proxies o balanceadores de carga, podríamos necesitar instalar dependencias adicionales para el manejo de almacenamiento en memoria:

pip install slowapi[memory]

Esta instalación incluye componentes adicionales que mejoran el rendimiento del sistema de rate limiting cuando se manejan grandes volúmenes de peticiones.

Con slowapi correctamente instalado y configurado, nuestra aplicación FastAPI estará preparada para implementar restricciones de velocidad que protejan nuestros endpoints contra el uso excesivo y los ataques de fuerza bruta.

¿Te está gustando esta lección?

Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.

Progreso guardado
Asistente IA
Ejercicios
Iniciar sesión gratis

Más de 25.000 desarrolladores ya confían en CertiDevs

Aplicar límites por IP y por usuario

Una vez configurado slowapi en nuestra aplicación, podemos implementar diferentes estrategias de limitación según nuestras necesidades de seguridad. Los límites por IP protegen contra ataques automatizados, mientras que los límites por usuario autenticado ofrecen un control más granular sobre el uso de la API.

Límites por dirección IP

La limitación por IP es la forma más básica y efectiva de proteger nuestra API contra ataques de fuerza bruta y uso excesivo. Utilizamos decoradores que se aplican directamente a nuestras rutas:

from fastapi import FastAPI, Request, HTTPException
from slowapi import Limiter
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded

limiter = Limiter(key_func=get_remote_address)
app = FastAPI()
app.state.limiter = limiter

@app.get("/api/datos")
@limiter.limit("10/minute")
async def obtener_datos(request: Request):
    return {"mensaje": "Datos obtenidos correctamente"}

@app.post("/api/login")
@limiter.limit("5/minute")
async def login(request: Request, credenciales: dict):
    # Lógica de autenticación
    return {"token": "ejemplo_token"}

En este ejemplo, el endpoint /api/datos permite 10 peticiones por minuto por IP, mientras que /api/login tiene un límite más restrictivo de 5 peticiones por minuto, ideal para prevenir ataques de fuerza bruta contra el sistema de autenticación.

Configuración de diferentes ventanas temporales

slowapi admite múltiples formatos de tiempo para definir las ventanas de limitación:

@app.get("/api/publico")
@limiter.limit("100/hour")  # 100 peticiones por hora
async def endpoint_publico(request: Request):
    return {"data": "información pública"}

@app.get("/api/sensible")
@limiter.limit("1/second")  # 1 petición por segundo
async def endpoint_sensible(request: Request):
    return {"data": "información sensible"}

@app.post("/api/upload")
@limiter.limit("3/day")  # 3 peticiones por día
async def subir_archivo(request: Request):
    return {"mensaje": "Archivo procesado"}

Límites por usuario autenticado

Para aplicaciones que requieren autenticación, podemos implementar límites específicos por usuario utilizando una función personalizada que extraiga el identificador del usuario:

from fastapi import Depends
from fastapi.security import HTTPBearer

security = HTTPBearer()

def get_user_id(request: Request) -> str:
    """Extrae el ID del usuario desde el token de autorización"""
    try:
        # Obtener el token del header Authorization
        auth_header = request.headers.get("authorization")
        if not auth_header:
            return get_remote_address(request)  # Fallback a IP
        
        # Extraer el user_id del token (simplificado)
        token = auth_header.replace("Bearer ", "")
        # Aquí decodificarías el JWT y extraerías el user_id
        # Por simplicidad, usamos el token como identificador
        return f"user_{hash(token) % 10000}"
    except:
        return get_remote_address(request)

# Crear limitador específico para usuarios
user_limiter = Limiter(key_func=get_user_id)
app.state.user_limiter = user_limiter

Aplicación de límites por usuario

Con el limitador configurado para usuarios, podemos aplicar restricciones personalizadas basadas en la identidad del usuario autenticado:

@app.get("/api/usuario/perfil")
@user_limiter.limit("50/hour")
async def obtener_perfil(request: Request, token: str = Depends(security)):
    return {"perfil": "datos del usuario"}

@app.post("/api/usuario/actualizar")
@user_limiter.limit("10/hour")
async def actualizar_perfil(request: Request, datos: dict, token: str = Depends(security)):
    return {"mensaje": "Perfil actualizado"}

Combinación de límites IP y usuario

En escenarios más complejos, podemos aplicar múltiples limitadores a un mismo endpoint para proporcionar capas adicionales de protección:

@app.post("/api/operacion-critica")
@limiter.limit("20/hour")  # Límite por IP
@user_limiter.limit("5/hour")  # Límite por usuario
async def operacion_critica(request: Request, datos: dict, token: str = Depends(security)):
    return {"resultado": "operación completada"}

Manejo de excepciones personalizado

Para mejorar la experiencia del usuario, podemos personalizar las respuestas cuando se exceden los límites:

from slowapi.errors import RateLimitExceeded
from fastapi.responses import JSONResponse

@app.exception_handler(RateLimitExceeded)
async def rate_limit_handler(request: Request, exc: RateLimitExceeded):
    response = JSONResponse(
        status_code=429,
        content={
            "error": "Límite de peticiones excedido",
            "detalle": f"Límite: {exc.detail}",
            "reintentar_en": "60 segundos"
        }
    )
    response.headers["Retry-After"] = "60"
    return response

Límites condicionales

También podemos implementar límites dinámicos que varíen según el contexto o tipo de usuario:

def get_dynamic_limit(request: Request) -> str:
    """Determina el límite basado en el tipo de usuario"""
    auth_header = request.headers.get("authorization", "")
    
    if "premium_user" in auth_header:
        return "100/hour"
    elif "basic_user" in auth_header:
        return "20/hour"
    else:
        return "5/hour"  # Usuario no autenticado

@app.get("/api/contenido")
async def obtener_contenido(request: Request):
    # Aplicar límite dinámico
    limite = get_dynamic_limit(request)
    # Lógica para aplicar el límite correspondiente
    return {"contenido": "datos según tipo de usuario"}

Esta aproximación nos permite crear un sistema de rate limiting flexible que se adapta a diferentes tipos de usuarios y escenarios de uso, proporcionando la protección necesaria sin comprometer la funcionalidad para usuarios legítimos.

Aprendizajes de esta lección

  • Comprender qué es rate limiting y su importancia en la protección de APIs.
  • Instalar y configurar la biblioteca slowapi en una aplicación FastAPI.
  • Aplicar límites de peticiones por dirección IP y por usuario autenticado.
  • Implementar diferentes ventanas temporales y límites condicionales según el tipo de usuario.
  • Personalizar el manejo de excepciones cuando se exceden los límites de peticiones.

Completa FastAPI y certifícate

Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.

Asistente IA

Resuelve dudas al instante

Ejercicios

Practica con proyectos reales

Certificados

Valida tus conocimientos

Más de 25.000 desarrolladores ya se han certificado con CertiDevs

⭐⭐⭐⭐⭐
4.9/5 valoración