Límites de Tasa
Aprende sobre la limitación de tasa de la API y cómo manejar respuestas de límite de tasa.
Resumen
La API de LosCenotes implementa limitación de tasa para asegurar uso justo y mantener calidad de servicio para todos los partners. Los límites de tasa se aplican por clave API y varían por nivel de plan.
Niveles de Límite de Tasa
Entorno Sandbox
Todas las claves API de sandbox tienen límites de tasa mejorados para desarrollo y pruebas:
| Plan | Peticiones/Hora | Peticiones/Minuto | Límite de Ráfaga |
|---|---|---|---|
| Básico | 10,000 | 200 | 50 |
| Pro | 100,000 | 2,000 | 500 |
| Empresarial | Ilimitado | Ilimitado | Ilimitado |
Entorno de Producción
Los límites de tasa de producción son más conservadores para asegurar estabilidad:
| Plan | Peticiones/Hora | Peticiones/Minuto | Límite de Ráfaga |
|---|---|---|---|
| Básico | 1,000 | 20 | 10 |
| Pro | 10,000 | 200 | 50 |
| Empresarial | Personalizado | Personalizado | Personalizado |
Límites Específicos por Endpoint
Algunos endpoints tienen límites adicionales:
| Tipo de Endpoint | Límite Adicional | Razón |
|---|---|---|
| POST /reservations | 100/hora | Prevenir spam de reservaciones |
| POST /webhooks | 10/minuto | Configuración de webhooks |
| GET /cenotes/search | 500/hora | Optimización de búsqueda |
Headers de Límite de Tasa
Todas las respuestas de la API incluyen headers de límite de tasa:
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1642261200
X-RateLimit-Window: 3600
X-RateLimit-Retry-After: 60
Descripción de Headers
| Header | Descripción |
|---|---|
X-RateLimit-Limit | Máximo de peticiones permitidas en la ventana actual |
X-RateLimit-Remaining | Peticiones restantes en la ventana actual |
X-RateLimit-Reset | Timestamp Unix cuando se restablece la ventana |
X-RateLimit-Window | Tamaño de ventana en segundos |
X-RateLimit-Retry-After | Segundos a esperar antes de la siguiente petición (cuando está limitado) |
Respuesta de Límite de Tasa Excedido
Cuando excedas tu límite de tasa, recibirás un código de estado 429:
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Demasiadas peticiones. Por favor intenta más tarde",
"status": 429,
"details": {
"limit": 1000,
"window": "1 hora",
"resetTime": "2024-01-15T11:00:00Z",
"retryAfter": 60
}
}
}
Manejo de Límites de Tasa
1. Monitorea los Headers de Límite de Tasa
import { LosCenotesClient } from "@loscenotes/partner-sdk";
const client = new LosCenotesClient({
apiKey: "sk_test_your_api_key",
onRateLimit: (headers) => {
console.log(`Límite de tasa: ${headers.remaining}/${headers.limit}`);
console.log(`Restablece en: ${new Date(headers.reset * 1000)}`);
// Alerta cuando se aproxime al límite
if (headers.remaining < 10) {
console.warn("¡Aproximándose al límite de tasa!");
}
},
});
2. Implementa Backoff Exponencial
class RateLimitHandler {
private baseDelay = 1000; // 1 segundo
private maxDelay = 60000; // 1 minuto
private maxRetries = 5;
async executeWithBackoff<T>(
apiCall: () => Promise<T>,
attempt: number = 0
): Promise<T> {
try {
return await apiCall();
} catch (error) {
if (error.status === 429 && attempt < this.maxRetries) {
const delay = Math.min(
this.baseDelay * Math.pow(2, attempt),
this.maxDelay
);
console.log(`Limitado por tasa. Reintentando en ${delay}ms...`);
await this.sleep(delay);
return this.executeWithBackoff(apiCall, attempt + 1);
}
throw error;
}
}
private sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
// Uso
const rateLimitHandler = new RateLimitHandler();
const cenotes = await rateLimitHandler.executeWithBackoff(() =>
client.cenotes.list({ page: 1, perPage: 10 })
);
3. Usa Cola de Peticiones
class RequestQueue {
private queue: Array<() => Promise<any>> = [];
private processing = false;
private requestsPerSecond = 5; // Mantente bajo el límite de tasa
private interval = 1000 / this.requestsPerSecond;
async enqueue<T>(apiCall: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
this.queue.push(async () => {
try {
const result = await apiCall();
resolve(result);
} catch (error) {
reject(error);
}
});
this.processQueue();
});
}
private async processQueue(): Promise<void> {
if (this.processing || this.queue.length === 0) {
return;
}
this.processing = true;
while (this.queue.length > 0) {
const request = this.queue.shift();
if (request) {
try {
await request();
} catch (error) {
console.error("Falló la petición:", error);
}
// Espera antes de la siguiente petición
await new Promise((resolve) => setTimeout(resolve, this.interval));
}
}
this.processing = false;
}
}
// Uso
const requestQueue = new RequestQueue();
// Todas las peticiones serán automáticamente encoladas y limitadas por tasa
const cenote1 = await requestQueue.enqueue(() => client.cenotes.get("cenote1"));
const cenote2 = await requestQueue.enqueue(() => client.cenotes.get("cenote2"));
const cenote3 = await requestQueue.enqueue(() => client.cenotes.get("cenote3"));
4. Implementa Patrón Circuit Breaker
enum CircuitBreakerState {
CLOSED = "CLOSED",
OPEN = "OPEN",
HALF_OPEN = "HALF_OPEN",
}
class CircuitBreaker {
private state = CircuitBreakerState.CLOSED;
private failures = 0;
private lastFailureTime = 0;
private threshold = 5; // Abre después de 5 fallas
private timeout = 60000; // 1 minuto de timeout
async execute<T>(apiCall: () => Promise<T>): Promise<T> {
if (this.state === CircuitBreakerState.OPEN) {
if (Date.now() - this.lastFailureTime > this.timeout) {
this.state = CircuitBreakerState.HALF_OPEN;
} else {
throw new Error("Circuit breaker está ABIERTO");
}
}
try {
const result = await apiCall();
// Éxito - restablece circuit breaker
if (this.state === CircuitBreakerState.HALF_OPEN) {
this.state = CircuitBreakerState.CLOSED;
this.failures = 0;
}
return result;
} catch (error) {
this.failures++;
this.lastFailureTime = Date.now();
if (error.status === 429) {
// Abre circuito en límite de tasa
if (this.failures >= this.threshold) {
this.state = CircuitBreakerState.OPEN;
}
}
throw error;
}
}
}
Mejores Prácticas
1. Cache de Datos Frecuentemente Solicitados
class CachedClient {
private cache = new Map<string, { data: any; expires: number }>();
private cacheTTL = 300000; // 5 minutos
async getCenoteWithCache(cenoteId: string) {
const cacheKey = `cenote:${cenoteId}`;
const cached = this.cache.get(cacheKey);
if (cached && cached.expires > Date.now()) {
return cached.data;
}
// Hacer llamada API solo si no está en cache
const cenote = await client.cenotes.get(cenoteId);
this.cache.set(cacheKey, {
data: cenote,
expires: Date.now() + this.cacheTTL,
});
return cenote;
}
}
2. Agrupa Peticiones Cuando sea Posible
// En lugar de múltiples peticiones individuales
const cenote1 = await client.cenotes.get("id1");
const cenote2 = await client.cenotes.get("id2");
const cenote3 = await client.cenotes.get("id3");
// Usa endpoints batch
const cenotes = await client.cenotes.getBatch(["id1", "id2", "id3"]);
3. Usa Webhooks en lugar de Polling
// En lugar de polling para actualizaciones de estado
setInterval(async () => {
const reservation = await client.reservations.get(reservationId);
if (reservation.status === "confirmed") {
// Maneja confirmación
}
}, 30000); // Cada 30 segundos
// Usa webhooks para actualizaciones en tiempo real
app.post("/webhooks/loscenotes", (req, res) => {
const event = req.body;
if (event.type === "reservation.confirmed") {
// Maneja confirmación inmediatamente
handleReservationConfirmed(event.data);
}
res.status(200).json({ received: true });
});
4. Monitorea el Uso de Límite de Tasa
class RateLimitMonitor {
private metrics = {
requestsLastHour: 0,
requestsLastMinute: 0,
rateLimitHits: 0,
averageResponseTime: 0,
};
recordRequest(responseTime: number, rateLimitHit: boolean = false): void {
this.metrics.requestsLastHour++;
this.metrics.requestsLastMinute++;
if (rateLimitHit) {
this.metrics.rateLimitHits++;
}
// Actualiza tiempo de respuesta promedio
this.metrics.averageResponseTime =
(this.metrics.averageResponseTime + responseTime) / 2;
// Envía métricas al servicio de monitoreo
this.sendMetrics();
}
private sendMetrics(): void {
// Envía a tu servicio de monitoreo (DataDog, New Relic, etc.)
console.log("Métricas de límite de tasa:", this.metrics);
}
}
Soporte de Límite de Tasa en SDK
SDK de TypeScript
import { LosCenotesClient } from "@loscenotes/partner-sdk";
const client = new LosCenotesClient({
apiKey: "sk_test_your_api_key",
rateLimitOptions: {
maxRetries: 3,
baseDelay: 1000,
maxDelay: 30000,
backoffFactor: 2,
},
});
// SDK maneja automáticamente límites de tasa con backoff exponencial
const cenotes = await client.cenotes.list();
SDK de Python
from loscenotes import LosCenotesClient
client = LosCenotesClient(
api_key='sk_test_your_api_key',
rate_limit_options={
'max_retries': 3,
'base_delay': 1.0,
'max_delay': 30.0,
'backoff_factor': 2
}
)
# SDK maneja límites de tasa automáticamente
cenotes = client.cenotes.list()
SDK de PHP
<?php
use LosCenotes\LosCenotesClient;
$client = new LosCenotesClient([
'apiKey' => 'sk_test_your_api_key',
'rateLimitOptions' => [
'maxRetries' => 3,
'baseDelay' => 1000,
'maxDelay' => 30000,
'backoffFactor' => 2
]
]);
// SDK maneja límites de tasa automáticamente
$cenotes = $client->cenotes->getList();
?>
Probando Límites de Tasa
Pruebas de Límite de Tasa en Sandbox
# Prueba límite de tasa en sandbox
for i in {1..50}; do
curl -X GET https://service-gateway.loscenotes.com/v1/test/rate-limit \
-H "Authorization: Bearer sk_test_your_api_key" \
-w "Estado: %{http_code}, Tiempo: %{time_total}s\n"
sleep 0.1
done
Script de Prueba de Carga
async function loadTest(): Promise<void> {
const promises: Promise<any>[] = [];
const startTime = Date.now();
// Envía 100 peticiones concurrentes
for (let i = 0; i < 100; i++) {
promises.push(
client.cenotes
.list({ page: 1, perPage: 1 })
.catch((error) => ({ error: error.status }))
);
}
const results = await Promise.all(promises);
const endTime = Date.now();
const successful = results.filter((r) => !r.error).length;
const rateLimited = results.filter((r) => r.error === 429).length;
console.log(`Prueba de carga completada en ${endTime - startTime}ms`);
console.log(`Peticiones exitosas: ${successful}`);
console.log(`Peticiones limitadas por tasa: ${rateLimited}`);
}
Monitoreo y Alertas
Configura Alertas de Límite de Tasa
function setupRateLimitAlerts() {
const threshold = 0.8; // Alerta al 80% del límite de tasa
client.onResponse((response, headers) => {
const usage = 1 - headers.remaining / headers.limit;
if (usage >= threshold) {
// Envía alerta
console.warn(`Uso de límite de tasa: ${(usage * 100).toFixed(1)}%`);
// Envía al servicio de monitoreo
sendAlert({
type: "RATE_LIMIT_WARNING",
usage: usage,
remaining: headers.remaining,
limit: headers.limit,
resetTime: new Date(headers.reset * 1000),
});
}
});
}
Métricas de Dashboard
Rastrea estas métricas en tu dashboard:
- Peticiones por hora/minuto
- Porcentaje de límite de tasa alcanzado
- Tiempo de respuesta promedio
- Tasa de éxito
- Longitud de cola (si usas cola de peticiones)
- Estado de circuit breaker
Actualizando tu Plan
Si constantemente alcanzas límites de tasa, considera actualizar:
-
Analiza Patrones de Uso
- Identifica horas pico de uso
- Encuentra oportunidades de optimización
- Calcula capacidad requerida
-
Optimiza Primero
- Implementa caching
- Usa endpoints batch
- Agrega cola de peticiones
-
Contacta Ventas
- Email: partners@loscenotes.com
- Proporciona estadísticas de uso
- Discute límites de tasa personalizados
Próximos Pasos
- Mejores Prácticas - Recomendaciones para producción
- Referencia API - Documentación completa de API