Documentación API REST
Bienvenido a la documentación oficial de la API REST de CONTROLATOR.ES. Esta API te permite integrar todas las funcionalidades de la plataforma en tus aplicaciones y sistemas externos.
https://controlator.es/api.php
Características principales
- RESTful - Arquitectura REST estándar con verbos HTTP semánticos
- JSON - Todas las peticiones y respuestas en formato JSON
- Autenticación JWT - Tokens Bearer seguros para autenticación
- CRUD completo - Crear, Leer, Actualizar y Eliminar en todos los módulos
- Paginación - Soporte para limit/offset en listados
- Filtros - Búsqueda y filtrado avanzado por campos
Autenticación
La API utiliza autenticación basada en tokens JWT. Para acceder a los endpoints protegidos, debes incluir el token en el header Authorization de cada petición.
Authorization: Bearer tu_token_jwt_aquiLos tokens tienen una validez de 7 días. Después de ese tiempo, debes volver a autenticarte con /auth/login.
Formato de Respuestas
Todas las respuestas de la API siguen un formato JSON estándar:
Respuesta Exitosa
{
"success": true,
"data": {
// Datos de la respuesta
}
}Respuesta de Error
{
"success": false,
"error": "Descripción del error"
}Códigos de Error HTTP
| Código | Significado | Descripción |
|---|---|---|
200 |
OK | La petición se completó exitosamente |
201 |
Created | Recurso creado exitosamente |
400 |
Bad Request | La petición contiene datos inválidos |
401 |
Unauthorized | Token de autenticación inválido o expirado |
403 |
Forbidden | No tienes permisos para esta operación |
404 |
Not Found | Recurso no encontrado |
500 |
Internal Server Error | Error interno del servidor |
POST /auth/login
Autenticar un usuario y obtener un token JWT para las siguientes peticiones.
Parámetros del Body (JSON)
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
username |
string | SÍ | Nombre de usuario |
password |
string | SÍ | Contraseña del usuario |
curl -X POST https://controlator.es/api.php/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "tu_contraseña"
}'{
"success": true,
"data": {
"token": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6...",
"user": {
"id": "USR-2026-00001",
"username": "admin",
"nombre": "Administrador Sistema",
"rol": "superadmin"
}
}
}POST /auth/logout
Cerrar la sesión actual e invalidar el token.
curl -X POST https://controlator.es/api.php/auth/logout \
-H "Authorization: Bearer tu_token_aqui"{
"success": true,
"data": {
"message": "Sesión cerrada exitosamente"
}
}GET /me
Obtener información del usuario autenticado actual.
curl -X GET https://controlator.es/api.php/me \
-H "Authorization: Bearer tu_token_aqui"{
"success": true,
"data": {
"_id": "USR-2026-00001",
"username": "admin",
"nombre": "Administrador Sistema",
"rol": "superadmin",
"email": "admin@municipio.es",
"_created": "2026-01-01 10:00:00"
}
}Módulo: Denuncias
El módulo de denuncias permite gestionar todas las denuncias ciudadanas. Incluye información del denunciante, hechos denunciados y seguimiento del estado.
Estructura de Datos
| Campo | Tipo | Descripción |
|---|---|---|
_id | string | ID único (DEN-2026-00001) |
denunciante_nombre | string | Nombre completo del denunciante |
denunciante_dni | string | DNI/NIE del denunciante (cifrado) |
denunciante_telefono | string | Teléfono de contacto (cifrado) |
denunciante_email | string | Email de contacto |
denunciante_domicilio | string | Domicilio del denunciante (cifrado) |
tipo | string | hurto, robo, lesiones, daños, etc. |
fecha_hechos | datetime | Fecha y hora de los hechos |
lugar_hechos | string | Lugar donde ocurrieron los hechos |
descripcion | text | Descripción detallada de los hechos |
testigos | array | Información de testigos si los hay |
estado | string | pendiente, investigacion, archivada, judicializada |
agente_asignado | string | ID del agente responsable |
prioridad | string | baja, media, alta, urgente |
adjuntos | array | URLs de documentos adjuntos |
observaciones | text | Observaciones internas |
curl -X POST https://controlator.es/api.php/denuncias \
-H "Authorization: Bearer tu_token" \
-H "Content-Type: application/json" \
-d '{
"denunciante_nombre": "María García López",
"denunciante_dni": "12345678A",
"denunciante_telefono": "666777888",
"denunciante_email": "maria@example.com",
"tipo": "hurto",
"fecha_hechos": "2026-01-12 14:30:00",
"lugar_hechos": "Plaza Mayor, 5",
"descripcion": "Sustracción de bolso en la vía pública",
"estado": "pendiente",
"prioridad": "media"
}'Módulo: Sanciones
Gestión de sanciones de tráfico y ordenanza municipal. Control del estado de tramitación y pagos.
Estructura de Datos
| Campo | Tipo | Descripción |
|---|---|---|
_id | string | ID único (SAN-2026-00001) |
tipo | string | trafico, ordenanza_municipal, ambiental |
expediente | string | Número de expediente |
infractor_nombre | string | Nombre del infractor |
infractor_dni | string | DNI del infractor (cifrado) |
infractor_domicilio | string | Domicilio (cifrado) |
vehiculo_matricula | string | Matrícula del vehículo si aplica |
fecha_infraccion | datetime | Fecha y hora de la infracción |
lugar_infraccion | string | Lugar de la infracción |
infraccion_codigo | string | Código de la infracción |
infraccion_descripcion | text | Descripción de la infracción |
importe | decimal | Importe de la sanción en euros |
importe_reducido | decimal | Importe con descuento por pronto pago |
estado | string | borrador, notificada, alegacion, firme, cobrada, anulada |
agente_denunciante | string | ID del agente que levanta la sanción |
fecha_notificacion | date | Fecha de notificación al infractor |
fecha_pago | date | Fecha de pago si está cobrada |
puntos_carnet | integer | Puntos del carnet descontados |
{
"tipo": "trafico",
"expediente": "2026/T/00123",
"infractor_nombre": "Pedro Martínez Ruiz",
"infractor_dni": "87654321B",
"vehiculo_matricula": "1234ABC",
"fecha_infraccion": "2026-01-12 16:45:00",
"lugar_infraccion": "Calle Mayor, km 2",
"infraccion_codigo": "80.1",
"infraccion_descripcion": "Exceso de velocidad (20-30 km/h)",
"importe": 100.00,
"importe_reducido": 50.00,
"puntos_carnet": 2,
"estado": "borrador",
"agente_denunciante": "PER-2026-00005"
}Módulo: Atestados
Documentación de procedimientos judiciales. Incluye diligencias, pruebas y remisión a juzgado.
Campos Principales
_id: ATE-2026-00001tipo: penal, civil, contenciosojuzgado: Juzgado de destinofecha_remision: Fecha de envío al juzgadoimputados: Array con datos de imputadosvictimas: Array con datos de víctimasdiligencias: Array con diligencias practicadasdocumentos_adjuntos: Pruebas documentales
Módulo: Personal
Gestión de la plantilla municipal: datos personales, escalas, destinos, formación y permisos.
Estructura de Datos
| Campo | Tipo | Descripción |
|---|---|---|
_id | string | ID único (PER-2026-00001) |
tip | string | Tarjeta de Identificación Profesional |
nif | string | DNI (cifrado) |
nombre | string | Nombre completo |
apellidos | string | Apellidos |
fecha_nacimiento | date | Fecha de nacimiento (cifrado) |
escala | string | oficial, subinspector, inspector, comisario |
categoria | string | Categoría profesional |
destino | string | Unidad de destino |
fecha_ingreso | date | Fecha de ingreso en el cuerpo |
situacion | string | activo, excedencia, jubilado, baja |
contacto_telefono | string | Teléfono (cifrado) |
contacto_email | string | Email corporativo |
formacion | array | Cursos y titulaciones |
idiomas | array | Idiomas y nivel |
Módulo: Vehículos
Control del parque móvil: vehículos oficiales, mantenimiento, asignaciones y consumos.
Campos Principales
_id: VEH-2026-00001matricula: Matrícula del vehículomarca,modelo: Identificación del vehículotipo: patrulla, moto, furgon, todoterrenobastidor: Número de bastidorkm_actuales: Kilometraje actualfecha_matriculacion: Fecha de matriculaciónfecha_itv: Próxima ITVestado: operativo, taller, bajaasignado_a: ID del agente asignado
Módulo: Sala 092
Registro de llamadas al 092: incidencias ciudadanas, emergencias y despacho de servicios.
Campos Principales
_id: 092-2026-00001numero_llamada: Teléfono del llamante (cifrado)fecha_hora_entrada: Timestamp de la llamadatipo_incidencia: urgente, normal, informacioncategoria: trafico, seguridad_ciudadana, ruidos, etc.descripcion: Motivo de la llamadaubicacion: Lugar de la incidenciaoperador: ID del operador que atiendepatrulla_asignada: Unidad despachadaestado: pendiente, en_curso, resueltaresultado: Resolución de la incidencia
Módulo: Turnos
Planificación de turnos y servicios. Control de presencia y horas trabajadas.
Campos Principales
_id: TUR-2026-00001agente_id: ID del agentefecha: Fecha del turnotipo_turno: mañana, tarde, nochehora_inicio,hora_fin: Horario del turnoservicio: patrulla, oficina, sala092, traficovehiculo_asignado: Vehículo si aplicacompañero: ID del compañero de patrullaestado: planificado, confirmado, realizado, ausencia
Módulo: Equipamiento
Inventario de material municipal: armas, chalecos, radios, uniformes, etc.
Campos Principales
_id: EQU-2026-00001tipo: arma, chaleco, radio, uniforme, informaticonombre: Nombre del equipamientonumero_serie: Número de serie si aplicamarca,modelo: Identificaciónestado: disponible, asignado, mantenimiento, bajaasignado_a: ID del agente asignadofecha_asignacion: Fecha de asignaciónubicacion: Ubicación físicaobservaciones: Notas adicionales
GET /{módulo} - Listar Registros
Obtener listado de registros con paginación y filtros opcionales.
Parámetros Query
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
limit |
integer | NO | Número de registros a devolver (default: 100) |
offset |
integer | NO | Número de registros a saltar (default: 0) |
{campo} |
mixed | NO | Filtrar por cualquier campo del registro |
curl -X GET "https://controlator.es/api.php/denuncias?limit=20&offset=0&estado=pendiente" \
-H "Authorization: Bearer tu_token_aqui"{
"success": true,
"data": {
"records": [
{
"_id": "DEN-2026-00001",
"denunciante_nombre": "Juan Pérez",
"denunciante_dni": "12345678A",
"tipo": "hurto",
"estado": "pendiente",
"_created": "2026-01-12 10:30:00"
}
],
"total": 150,
"limit": 20,
"offset": 0
}
}GET /{módulo}/:id - Obtener por ID
Obtener un registro específico por su ID.
curl -X GET https://controlator.es/api.php/denuncias/DEN-2026-00001 \
-H "Authorization: Bearer tu_token_aqui"{
"success": true,
"data": {
"_id": "DEN-2026-00001",
"denunciante_nombre": "Juan Pérez",
"denunciante_dni": "12345678A",
"denunciante_telefono": "666555444",
"tipo": "hurto",
"fecha_hechos": "2026-01-12 10:30:00",
"lugar_hechos": "Plaza Mayor",
"descripcion": "Sustracción de cartera",
"estado": "investigacion",
"agente_asignado": "PER-2026-00003",
"_created": "2026-01-12 11:00:00",
"_modified": "2026-01-12 15:30:00"
}
}{
"success": false,
"error": "Registro no encontrado"
}POST /{módulo} - Crear Registro
Crear un nuevo registro en el módulo especificado. El ID se genera automáticamente.
Los campos _id, _created, _created_by, _modified, _version y _hash se generan automáticamente. No es necesario incluirlos en la petición.
curl -X POST https://controlator.es/api.php/denuncias \
-H "Authorization: Bearer tu_token_aqui" \
-H "Content-Type: application/json" \
-d '{
"denunciante_nombre": "María García López",
"denunciante_dni": "87654321B",
"denunciante_telefono": "677888999",
"denunciante_email": "maria@example.com",
"tipo": "robo",
"fecha_hechos": "2026-01-12 18:00:00",
"lugar_hechos": "Calle Principal, 42",
"descripcion": "Robo con fuerza en las cosas en vivienda. Acceso por ventana lateral.",
"estado": "pendiente",
"prioridad": "alta"
}'{
"success": true,
"data": {
"_id": "DEN-2026-00045",
"denunciante_nombre": "María García López",
"denunciante_dni": "87654321B",
"denunciante_telefono": "677888999",
"denunciante_email": "maria@example.com",
"tipo": "robo",
"fecha_hechos": "2026-01-12 18:00:00",
"lugar_hechos": "Calle Principal, 42",
"descripcion": "Robo con fuerza en las cosas en vivienda. Acceso por ventana lateral.",
"estado": "pendiente",
"prioridad": "alta",
"_created": "2026-01-12 19:15:23",
"_created_by": "USR-2026-00001",
"_modified": "2026-01-12 19:15:23",
"_modified_by": "USR-2026-00001",
"_version": 1
}
}{
"success": false,
"error": "Datos requeridos"
}PUT /{módulo}/:id - Actualizar Registro
Actualizar un registro existente. Solo se modifican los campos enviados en la petición.
No es necesario enviar todos los campos del registro. Solo se actualizarán los campos incluidos en el body de la petición. Los campos _modified, _modified_by y _version se actualizan automáticamente.
curl -X PUT https://controlator.es/api.php/denuncias/DEN-2026-00045 \
-H "Authorization: Bearer tu_token_aqui" \
-H "Content-Type: application/json" \
-d '{
"estado": "investigacion",
"agente_asignado": "PER-2026-00007",
"observaciones": "Asignada a la unidad de investigación. Solicitar grabaciones CCTV de la zona."
}'{
"success": true,
"data": {
"_id": "DEN-2026-00045",
"denunciante_nombre": "María García López",
"denunciante_dni": "87654321B",
"tipo": "robo",
"estado": "investigacion",
"agente_asignado": "PER-2026-00007",
"observaciones": "Asignada a la unidad de investigación. Solicitar grabaciones CCTV de la zona.",
"_created": "2026-01-12 19:15:23",
"_created_by": "USR-2026-00001",
"_modified": "2026-01-12 21:45:00",
"_modified_by": "USR-2026-00003",
"_version": 2
}
}{
"success": false,
"error": "Registro no encontrado"
}DELETE /{módulo}/:id - Eliminar Registro
Eliminar un registro mediante soft delete. El registro se marca como eliminado pero se conserva para auditoría.
Los registros no se eliminan físicamente del sistema. Se marca el campo _deleted con la fecha y hora de eliminación. Esto garantiza trazabilidad completa y cumple con el Esquema Nacional de Seguridad (ENS) y la LO 7/2021 de protección de datos municipales.
curl -X DELETE https://controlator.es/api.php/denuncias/DEN-2026-00045 \
-H "Authorization: Bearer tu_token_aqui"{
"success": true,
"data": {
"message": "Registro eliminado exitosamente"
}
}{
"success": false,
"error": "Sin permisos para esta operación"
}Paginación y Filtros Avanzados
La API soporta paginación y filtrado avanzado en todos los endpoints de listado.
Parámetros de Paginación
| Parámetro | Tipo | Default | Descripción |
|---|---|---|---|
limit |
integer | 100 | Número máximo de registros a devolver (máx: 1000) |
offset |
integer | 0 | Número de registros a saltar desde el inicio |
Filtros Dinámicos
Puedes filtrar por cualquier campo del registro añadiéndolo como parámetro query:
# Filtrar denuncias por estado
curl "https://controlator.es/api.php/denuncias?estado=pendiente" \
-H "Authorization: Bearer tu_token"
# Filtrar sanciones por tipo y estado
curl "https://controlator.es/api.php/sanciones?tipo=trafico&estado=firme" \
-H "Authorization: Bearer tu_token"
# Filtrar personal por escala
curl "https://controlator.es/api.php/personal?escala=oficial&situacion=activo" \
-H "Authorization: Bearer tu_token"
# Combinar paginación y filtros
curl "https://controlator.es/api.php/denuncias?estado=investigacion&limit=50&offset=100" \
-H "Authorization: Bearer tu_token"Cálculo de Páginas
// Ejemplo de paginación en JavaScript
const perPage = 20;
let currentPage = 1;
async function loadPage(page) {
const offset = (page - 1) * perPage;
const response = await fetch(
`https://controlator.es/api.php/denuncias?limit=${perPage}&offset=${offset}`,
{ headers: { 'Authorization': `Bearer ${token}` }}
);
const data = await response.json();
const totalPages = Math.ceil(data.data.total / perPage);
console.log(`Página ${page} de ${totalPages}`);
return data.data.records;
}Sistema de Permisos
La API implementa un sistema RBAC (Role-Based Access Control) con 6 roles predefinidos y permisos granulares.
Roles del Sistema
| Rol | Descripción | Permisos |
|---|---|---|
superadmin |
Super Administrador | Todos los permisos (*.*) |
administrador |
Administrador Municipal | Gestión completa excepto configuración |
jefe |
Alcalde | Supervisión y gestión de personal |
oficial |
Concejal | Gestión de casos asignados |
agente |
Técnico Municipal | Operaciones básicas en su ámbito |
consulta |
Solo Consulta | Ver registros sin modificar |
Formato de Permisos
Los permisos siguen el formato {módulo}.{acción}:
denuncias.ver- Ver denunciasdenuncias.crear- Crear denunciasdenuncias.editar- Editar denunciasdenuncias.eliminar- Eliminar denunciasdenuncias.*- Todos los permisos del módulo*.*- Todos los permisos del sistema
Mapeo de Operaciones HTTP a Permisos
| Método HTTP | Permiso Requerido | Ejemplo |
|---|---|---|
GET |
{módulo}.ver |
denuncias.ver |
POST |
{módulo}.crear |
sanciones.crear |
PUT |
{módulo}.editar |
personal.editar |
DELETE |
{módulo}.eliminar |
vehiculos.eliminar |
Si intentas acceder a un endpoint sin los permisos necesarios, recibirás un error 403 Forbidden con el mensaje "Sin permisos para esta operación".
Seguridad y Cumplimiento Normativo
La API implementa múltiples capas de seguridad para cumplir con ENS, RGPD y LO 7/2021.
Cifrado de Datos Sensibles
Los campos sensibles se cifran automáticamente con AES-256-GCM:
- DNI/NIE (denunciante, infractor, personal)
- Teléfonos de contacto
- Direcciones y domicilios
- Datos médicos si aplica
- Información bancaria
El cifrado es transparente. Envía los datos en texto plano en tus peticiones y se cifrarán automáticamente. Al recuperar registros, los datos sensibles se descifran automáticamente si tienes permisos.
Auditoría y Trazabilidad
Todas las operaciones quedan registradas en el log de auditoría:
- Usuario que realiza la operación (
_created_by,_modified_by) - Fecha y hora exacta de cada cambio
- Versión del registro (
_version) - Hash SHA-256 de integridad (
_hash) - Soft delete con marca temporal (
_deleted)
Tokens de Autenticación
- Validez: 7 días desde la emisión
- Almacenamiento: Nunca almacenes tokens en localStorage en frontend, usa sessionStorage o cookies HttpOnly
- Renovación: Solicita nuevo token con
/auth/logincuando expire - Cierre de sesión: Invalida el token con
/auth/logout
Buenas Prácticas de Seguridad
// ✅ CORRECTO: Almacenamiento seguro del token
sessionStorage.setItem('api_token', token);
// ❌ INCORRECTO: No usar localStorage para tokens
localStorage.setItem('api_token', token);
// ✅ CORRECTO: Validar siempre las respuestas
if (response.success) {
// Procesar datos
} else {
// Manejar error
console.error(response.error);
}
// ✅ CORRECTO: Implementar timeout en peticiones
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000);
fetch(url, {
signal: controller.signal,
headers: { 'Authorization': `Bearer ${token}` }
}).finally(() => clearTimeout(timeout));Buenas Prácticas de Desarrollo
1. Manejo de Errores
Implementa siempre un manejo robusto de errores:
async function apiRequest(endpoint, options = {}) {
try {
const response = await fetch(`https://controlator.es/api.php${endpoint}`, {
...options,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
...options.headers
}
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Error en la petición');
}
return data.data;
} catch (error) {
if (error.name === 'AbortError') {
console.error('Petición cancelada por timeout');
} else if (!navigator.onLine) {
console.error('Sin conexión a internet');
} else {
console.error('Error:', error.message);
}
throw error;
}
}2. Reintentos con Backoff Exponencial
async function retryRequest(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}3. Cacheo de Respuestas
Implementa cacheo para reducir peticiones innecesarias:
class APICache {
constructor(ttl = 60000) {
this.cache = new Map();
this.ttl = ttl; // Time to live en ms
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() > item.expiry) {
this.cache.delete(key);
return null;
}
return item.value;
}
set(key, value) {
this.cache.set(key, {
value,
expiry: Date.now() + this.ttl
});
}
}
const cache = new APICache(60000); // Cache de 1 minuto4. Validación de Datos en Cliente
Valida los datos antes de enviarlos para reducir errores:
function validateDenuncia(data) {
const errors = [];
if (!data.denunciante_nombre || data.denunciante_nombre.length < 3) {
errors.push('Nombre del denunciante requerido');
}
if (!/^\d{8}[A-Z]$/.test(data.denunciante_dni)) {
errors.push('DNI inválido');
}
if (!data.descripcion || data.descripcion.length < 20) {
errors.push('Descripción demasiado corta (mín. 20 caracteres)');
}
return errors.length === 0 ? null : errors;
}5. Paginación Eficiente
class PaginatedRequest {
constructor(endpoint, perPage = 50) {
this.endpoint = endpoint;
this.perPage = perPage;
this.currentPage = 1;
this.totalRecords = 0;
}
async loadPage(page) {
const offset = (page - 1) * this.perPage;
const url = `${this.endpoint}?limit=${this.perPage}&offset=${offset}`;
const response = await apiRequest(url);
this.totalRecords = response.total;
this.currentPage = page;
return response.records;
}
get totalPages() {
return Math.ceil(this.totalRecords / this.perPage);
}
hasNextPage() {
return this.currentPage < this.totalPages;
}
async nextPage() {
if (!this.hasNextPage()) return null;
return this.loadPage(this.currentPage + 1);
}
}Ejemplos de Código Completos
JavaScript (Fetch API)
// Autenticación
const login = async () => {
const response = await fetch('https://controlator.es/api.php/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'admin',
password: 'tu_contraseña'
})
});
const data = await response.json();
return data.data.token;
};
// Obtener denuncias
const getDenuncias = async (token) => {
const response = await fetch('https://controlator.es/api.php/denuncias?limit=20', {
headers: { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
return data.data.records;
};PHP (cURL)
// Autenticación
$ch = curl_init('https://controlator.es/api.php/auth/login');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'username' => 'admin',
'password' => 'tu_contraseña'
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($ch), true);
$token = $response['data']['token'];Python (Requests)
import requests
# Autenticación
response = requests.post('https://controlator.es/api.php/auth/login', json={
'username': 'admin',
'password': 'tu_contraseña'
})
token = response.json()['data']['token']
# Obtener denuncias
headers = {'Authorization': f'Bearer {token}'}
response = requests.get('https://controlator.es/api.php/denuncias?limit=20', headers=headers)
denuncias = response.json()['data']['records']Rate Limits
Para garantizar la estabilidad del servicio, la API implementa los siguientes límites:
- 1000 peticiones por hora por token de autenticación
- 50 peticiones por minuto por IP
Cuando se excede el límite, recibirás un error 429 Too Many Requests.