Descripción General

La API de Imagina Energía implementa rate limiting (limitación de tasa) para proteger el servicio contra:

  • 🛡️Ataques de Denegación de Servicio (DoS)
  • 🔄Uso excesivo de recursos
  • 🐛Bucles infinitos en código cliente
  • 📊Uso abusivo no intencionado

El sistema limita el número de peticiones que un cliente puede realizar en un período de tiempo específico.

Límites por Defecto

La API implementa dos niveles de límites:

1. Límites por Usuario/IP

Todos los endpoints tienen aplicados los siguientes límites individuales:

Período Límite Descripción
Por hora 200 peticiones Límite individual para uso normal
Por minuto 50 peticiones Protección individual contra ráfagas

2. Límites Globales (Aplicación Completa)

Además, existe un límite global que protege el servidor de sobrecarga total:

Período Límite Descripción
Por hora 5,000 peticiones Límite total para todos los usuarios
Por minuto 1,000 peticiones Protección global contra ráfagas
🔥 Importante:
  • Los límites por usuario se aplican individualmente (cada usuario tiene su cuota)
  • Los límites globales se aplican a TODA la aplicación (compartidos entre todos)
  • Si se alcanza el límite global, todos los usuarios recibirán error 429
  • Los límites globales son más altos para permitir uso simultáneo de múltiples usuarios

¿Cómo Interactúan los Límites?

Si haces 60 peticiones en un minuto:
  ❌ Rechazada - Excedes tu límite individual (50/min)
  
Si 15 usuarios hacen 50 peticiones cada uno simultáneamente (750 total):
  ✅ OK - Dentro del límite global (1000/min)
  
Si 25 usuarios hacen 50 peticiones cada uno simultáneamente (1250 total):
  ❌ Rechazada - Excede el límite global (1000/min)
  ⚠️ Afecta a TODOS los usuarios, incluso los que no han alcanzado su límite individual
⚠️ Importante: Los límites se aplican por usuario autenticado (JWT sub) o por dirección IP si no hay autenticación.

Límites por Endpoint

Algunos endpoints críticos tienen límites más estrictos:

Endpoints de Contratación

Endpoints
POST /contrato/residencial/c1
POST /contrato/empresa/c1

Límites:

  • 10 peticiones por minuto
  • 50 peticiones por hora

Razón: Creación de contratos es una operación costosa que involucra:

  • Validaciones complejas
  • Credit check externo
  • Creación en sistema Neuro
  • Envío a firma electrónica

Endpoints de Consulta

Endpoints
GET /contrato/{id_contrato}
GET /contratos
GET /tarifas

Límites:

  • 100 peticiones por minuto
  • 500 peticiones por hora

Razón: Operaciones de solo lectura más ligeras.

Endpoints de Documentos

Endpoints
POST /documento
GET /documento/{id}

Límites:

  • 20 peticiones por minuto (POST)
  • 100 peticiones por minuto (GET)

Razón: La subida de documentos consume ancho de banda y almacenamiento.

Headers de Rate Limit

Cada respuesta incluye headers informativos sobre el estado de tu límite:

Header Descripción Ejemplo
X-RateLimit-Limit Número máximo de peticiones permitidas 50
X-RateLimit-Remaining Peticiones restantes en la ventana actual 47
X-RateLimit-Reset Timestamp Unix cuando se resetea el contador 1738598400

Ejemplo de Headers

HTTP
HTTP/1.1 200 OK
X-RateLimit-Limit: 50
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1738598400
Content-Type: application/json

Manejo de Errores

Error 429: Too Many Requests

Cuando excedes un límite (individual o global), recibes:

HTTP
HTTP/1.1 429 Too Many Requests
Retry-After: 42
X-RateLimit-Limit: 50
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1738598442
Content-Type: application/json

{
  "error": "Demasiadas peticiones. Por favor, intenta de nuevo más tarde.",
  "retry_after": "42 seconds"
}

Headers importantes:

  • Retry-After: Segundos que debes esperar antes de reintentar
  • X-RateLimit-Reset: Timestamp exacto cuando se resetea tu cuota

¿Límite Individual o Global?

El error 429 puede ocurrir por dos razones:

1. Límite individual alcanzado: Solo tú has excedido tu límite de 200/hora o 50/min

  • Otros usuarios no se ven afectados
  • Espera el tiempo indicado en Retry-After y reintenta

2. Límite global alcanzado: El servidor está sobrecargado (5000/hora o 1000/min total)

  • Todos los usuarios están siendo limitados
  • Espera más tiempo antes de reintentar (puede haber congestión)
  • Considera contactar soporte si ocurre frecuentemente
💡 Tip: Si recibes 429 repetidamente a pesar de hacer pocas peticiones, probablemente se alcanzó el límite global.

Identificación de Clientes

El sistema identifica a los clientes de dos formas:

1. Por Usuario Autenticado (Preferido)

Si envías un token JWT válido:

HTTP
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

El límite se aplica por: sub (subject) del JWT

Ventajas:

  • ✅ Múltiples IPs pueden compartir el mismo usuario
  • ✅ Límites personalizables por usuario
  • ✅ Auditoría completa

2. Por Dirección IP (Fallback)

Si no hay JWT o el JWT es inválido:

El límite se aplica por: Dirección IP del cliente

Consideraciones:

  • ⚠️ Puede afectar a múltiples usuarios detrás de un NAT
  • ⚠️ No permite auditoría detallada

Buenas Prácticas

✅ Monitorea los Headers

Siempre revisa los headers X-RateLimit-* en tus respuestas:

Python
import requests

response = requests.get(
    'https://api.imaginaenergia.com/contratos',
    headers={'Authorization': f'Bearer {token}'}
)

remaining = int(response.headers.get('X-RateLimit-Remaining', 0))
if remaining < 10:
    print(f"⚠️ Solo quedan {remaining} peticiones")

✅ Implementa Exponential Backoff

Cuando recibas un 429, espera antes de reintentar:

Python
import time
import requests

def hacer_peticion_con_retry(url, headers, max_retries=3):
    for intento in range(max_retries):
        response = requests.get(url, headers=headers)
        
        if response.status_code == 429:
            retry_after = int(response.headers.get('Retry-After', 60))
            print(f"⏳ Rate limit alcanzado. Esperando {retry_after}s...")
            time.sleep(retry_after)
            continue
        
        return response
    
    raise Exception("Máximo de reintentos alcanzado")

✅ Cachea Resultados

Evita peticiones innecesarias cacheando datos que no cambian frecuentemente:

Python
import time

class TarifasCache:
    def __init__(self):
        self.cache = None
        self.timestamp = 0
        self.ttl = 3600  # 1 hora
    
    def get_tarifas(self, api_client):
        now = time.time()
        if self.cache is None or (now - self.timestamp) > self.ttl:
            self.cache = api_client.get_tarifas()
            self.timestamp = now
        return self.cache

❌ Evita Polling Agresivo

No hagas polling cada segundo. Usa intervalos razonables:

Python
# ❌ Mal: 60 peticiones por minuto
while True:
    estado = api.get_contrato_estado(id)
    time.sleep(1)

# ✅ Bien: 2 peticiones por minuto
while True:
    estado = api.get_contrato_estado(id)
    if estado == 'completado':
        break
    time.sleep(30)  # 30 segundos

✅ Usa Webhooks en Lugar de Polling

Para operaciones asíncronas, usa callbacks:

Python
# ✅ Mejor: Sin polling, usas callbacks
response = api.crear_contrato(
    datos=datos_contrato,
    callback_url='https://tuservidor.com/webhook/contrato'
)
# Tu servidor recibirá la notificación cuando esté listo

Ejemplos de Código

Python con Manejo Completo

Python
import requests
import time
from typing import Optional, Dict, Any

class ImaginaEnergiaClient:
    def __init__(self, token: str, base_url: str = "https://api.imaginaenergia.com"):
        self.token = token
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({
            'Authorization': f'Bearer {token}',
            'Content-Type': 'application/json'
        })
    
    def _request_with_rate_limit(
        self, 
        method: str, 
        endpoint: str, 
        max_retries: int = 3,
        **kwargs
    ) -> requests.Response:
        """Realiza una petición con manejo automático de rate limiting"""
        
        url = f"{self.base_url}{endpoint}"
        
        for intento in range(max_retries):
            response = self.session.request(method, url, **kwargs)
            
            # Mostrar info de rate limit
            remaining = response.headers.get('X-RateLimit-Remaining')
            limit = response.headers.get('X-RateLimit-Limit')
            if remaining and limit:
                print(f"📊 Rate limit: {remaining}/{limit} peticiones restantes")
            
            # Manejar rate limit
            if response.status_code == 429:
                retry_after = int(response.headers.get('Retry-After', 60))
                print(f"⏳ Rate limit alcanzado. Esperando {retry_after}s...")
                time.sleep(retry_after + 1)  # +1 segundo de margen
                continue
            
            # Manejar otros errores
            response.raise_for_status()
            return response
        
        raise Exception(f"Máximo de reintentos ({max_retries}) alcanzado")
    
    def get_contratos(self, pagina: int = 1, por_pagina: int = 20) -> Dict[Any, Any]:
        """Obtiene listado de contratos"""
        response = self._request_with_rate_limit(
            'GET',
            '/contratos',
            params={'pagina': pagina, 'por_pagina': por_pagina}
        )
        return response.json()
    
    def crear_contrato(self, datos: Dict[Any, Any]) -> Dict[Any, Any]:
        """Crea un nuevo contrato"""
        response = self._request_with_rate_limit(
            'POST',
            '/contrato/residencial/c1',
            json=datos
        )
        return response.json()

# Uso
client = ImaginaEnergiaClient(token='tu_token_jwt')

try:
    contratos = client.get_contratos(pagina=1)
    print(f"✅ Contratos obtenidos: {len(contratos)}")
except Exception as e:
    print(f"❌ Error: {e}")

JavaScript con Axios

JavaScript
const axios = require('axios');

class ImaginaEnergiaClient {
    constructor(token, baseURL = 'https://api.imaginaenergia.com') {
        this.client = axios.create({
            baseURL,
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json'
            }
        });
        
        // Interceptor para manejar rate limiting
        this.client.interceptors.response.use(
            response => {
                // Mostrar info de rate limit
                const remaining = response.headers['x-ratelimit-remaining'];
                const limit = response.headers['x-ratelimit-limit'];
                if (remaining && limit) {
                    console.log(`📊 Rate limit: ${remaining}/${limit} peticiones restantes`);
                }
                return response;
            },
            async error => {
                if (error.response && error.response.status === 429) {
                    const retryAfter = parseInt(error.response.headers['retry-after'] || 60);
                    console.log(`⏳ Rate limit alcanzado. Esperando ${retryAfter}s...`);
                    
                    await new Promise(resolve => setTimeout(resolve, (retryAfter + 1) * 1000));
                    
                    // Reintentar la petición
                    return this.client.request(error.config);
                }
                return Promise.reject(error);
            }
        );
    }
    
    async getContratos(pagina = 1, porPagina = 20) {
        const response = await this.client.get('/contratos', {
            params: { pagina, por_pagina: porPagina }
        });
        return response.data;
    }
    
    async crearContrato(datos) {
        const response = await this.client.post('/contrato/residencial/c1', datos);
        return response.data;
    }
}

// Uso
const client = new ImaginaEnergiaClient('tu_token_jwt');

(async () => {
    try {
        const contratos = await client.getContratos(1);
        console.log(`✅ Contratos obtenidos: ${contratos.length}`);
    } catch (error) {
        console.error(`❌ Error: ${error.message}`);
    }
})();

Incremento de Límites

Si necesitas límites más altos para tu caso de uso:

Requisitos

  1. Justificación técnica: Describe por qué necesitas más peticiones
  2. Plan de uso: Explica cómo usarás la API
  3. Medidas de caché: Qué estás haciendo para minimizar peticiones

Proceso

  1. Contacta con: serviciosistemas@imaginaenergia.com
  2. Incluye tu client_id o email de autenticación
  3. Proporciona los detalles arriba mencionados

Respuesta

  • ⏱️ Tiempo de respuesta: 2-5 días laborables
  • 📊 Evaluaremos tu caso de uso
  • ✅ Si se aprueba, ajustaremos los límites de tu cuenta

FAQ

¿Los límites se comparten entre diferentes endpoints?

No. Cada límite es independiente por categoría de endpoint.

¿Qué pasa si tengo múltiples servicios usando el mismo token?

Los límites se suman. Si tienes 3 servicios usando el mismo token, todos comparten la misma cuota.

Recomendación: Usa tokens diferentes para cada servicio si es posible.

¿Los límites se resetean exactamente cada hora?

Sí. El sistema usa ventanas fijas. Si tu primera petición es a las 10:15, tu cuota se resetea a las 11:00, no a las 11:15.

¿Puedo ver mi uso histórico?

Actualmente no hay un endpoint dedicado, pero estamos trabajando en ello. Entre tanto, puedes trackear tu uso guardando los headers X-RateLimit-* de cada respuesta.

¿Qué pasa si envío peticiones desde diferentes IPs pero con el mismo JWT?

El límite se aplica por JWT sub, no por IP. Todas tus peticiones comparten la misma cuota independientemente de la IP.

¿Por qué recibo error 429 si no he hecho muchas peticiones?

Puede ser por dos razones:

  1. Tus peticiones previas: Recuerda que los límites son por hora/minuto. Si hiciste muchas peticiones hace 30 minutos, aún cuentan para tu cuota.
  2. Límite global alcanzado: Otros usuarios han saturado el servidor. El límite global (5000/hora o 1000/min) protege el servidor de sobrecarga total. Cuando se alcanza, todos los usuarios son limitados temporalmente.

Solución: Espera el tiempo indicado en Retry-After. Si ocurre frecuentemente, contacta a soporte.

¿Cómo sé si me limitaron por mi uso o por el límite global?

Si estás bien dentro de tu límite individual (ej: 10 peticiones vs límite de 50/min) pero recibes 429, probablemente se alcanzó el límite global. Desafortunadamente, los headers no distinguen entre ambos casos actualmente.

¿Los límites globales se pueden incrementar?

Los límites globales están configurados para el hardware actual del servidor. Si necesitas más capacidad para tu organización, contacta a soporte para explorar soluciones dedicadas o planes enterprise.

Documentación Adicional

👨‍💻 Documentación para Administradores

Si eres administrador del sistema y necesitas configurar o ajustar los límites de rate limiting, consulta la Guía de Configuración de Rate Limiting que incluye:

  • Variables de entorno y configuración
  • Guías de dimensionamiento por tamaño de servidor
  • Configuración de Redis para producción
  • Monitorización y troubleshooting

Soporte

Si tienes dudas o problemas relacionados con rate limiting:

📧 Email: serviciosistemas@imaginaenergia.com
📚 Documentación: Índice de documentación


Última actualización: 10 de Febrero, 2026 | Versión: 1.0