apiempresas
v1.5.0
Published
SDK oficial de APIEmpresas.es para Node.js y TypeScript
Maintainers
Readme
APIEmpresas.es - Node.js SDK
SDK oficial para interactuar con la API de APIEmpresas.es desde Node.js utilizando TypeScript o JavaScript.
Instalación
npm install apiempresasInicialización
import { ApiEmpresas } from 'apiempresas';
const api = new ApiEmpresas({
apiKey: 'TU_API_KEY', // Obligatorio
});Uso Básico
1. Obtener datos de una empresa
async function fetchCompany() {
try {
const company = await api.companies.get('A15075062');
console.log(company.name);
} catch (error) {
console.error('Error fetching company:', error.message);
}
}2. Historial de Actos del BORME
async function fetchBorme() {
const bormeData = await api.companies.borme('A15075062');
console.log(bormeData.events);
}3. Consulta Múltiple (Batch)
async function fetchBatch() {
const response = await api.companies.batch({
cifs: ['A15075062', 'A46103834']
});
console.log('Resultados:', response.data);
console.log('Coste:', response.meta.cost);
}4. Administradores, búsqueda con varios resultados, Match y Radar
// Administradores y cargos (Pro/Business)
const conAdmins = await api.companies.get('A15075062', { admin: true });
// Varias empresas por nombre, con paginación
let pagina = await api.companies.searchMultiple('software', { limit: 20 });
while (pagina.meta.next_cursor) {
pagina = await api.companies.searchMultiple('software', { cursor: pagina.meta.next_cursor });
}
// Match (Business): el sector del vendedor es obligatorio
const match = await api.companies.match('A15075062', 'software');
// Radar de empresas nuevas (todos los planes: 10 resultados en Free con nombre y CIF ocultos, 100 en Pro, 1.000 en Business). range: 'hoy' (por defecto) o días atrás ('7', '30')
const nuevas = await api.companies.radar({ province: 'Madrid', range: '7' });
// Filtros añadidos en 1.2.0: cnae, min_score, main_act_type, has_phone
const software = await api.companies.radar({ cnae: '62', min_score: 70, has_phone: true });Requiere Node.js 18 o superior (usa fetch nativo).
5. Verificación KYB (Pro) — 2 consultas
const v = await api.companies.verify('A46103834', {
name: 'Mercadona SA', // se compara con la razón social
person: 'Juan Roig Alfonso', // ¿es administrador vigente?
vat: true, // NIF-IVA en VIES
iban: 'ES9121000418450200051332' // (1.5.0) ¿está bien formado? No dice de quién es la cuenta
});
console.log(v.decision_hint); // 'pass' | 'review' | 'fail'
console.log(v.flags); // alertas con code, severity y messageCada empresa trae además status_code (ACTIVE, INSOLVENCY, EXTINCT...), status_source, status_date y, en Pro/Business, financials (tramo de tamaño y último año de cuentas). Los administradores traen since. Desde la 1.3.0 la respuesta lleva también identifiers (código LEI) y legal_form (forma jurídica con su código ISO 20275) en todos los planes, y en Pro/Business group (matriz directa y última según GLEIF) y ownership (socio único según el BORME). En verify, con person, checks.person.is_sole_shareholder dice si quien firma es el socio único. Desde la 1.5.0, en Pro/Business, size (tramo de empleados con su fuente: Kit Digital, TED o PLACSP; null si no consta) y address_density (cuántas sociedades activas comparten la dirección exacta), y en verify con iban, checks.iban (si no es válido, alerta IBAN_INVALID y decision_hint review). filter() admite size_category (micro, small, medium, large).
Nombre a CIF (Pro)
const { data, meta } = await api.companies.reconcile(['Mercadona', { name: 'Talleres Pérez', province: 'Madrid' }]);
// data[i].status: match (con company.cif) | ambiguous (candidates) | no_match. Solo se cobran los match.Segmentos de empresas
const total = await api.companies.count({ cnae: ['62'], province: 'MADRID', has_phone: true }); // gratis
const { data, meta } = await api.companies.filter({ cnae: ['62'], province: 'MADRID', limit: 100 }); // Business: 5 consultas por fila
// Siguiente página: api.companies.filter({ ...mismosFiltros, cursor: meta.next_cursor })6. Vigilancia de empresas (Pro: 100, Business: 1.000) — sin coste
await api.watchlist.add(['A46103834', 'A28015865']);
const { data: vigiladas, meta } = await api.watchlist.list(); // meta.watch_limit
const { data: cambios } = await api.watchlist.events({ since: '2026-09-01', types: ['borme_act', 'status_change'] });
await api.watchlist.remove('A28015865');7. Webhooks (Business)
const hook = await api.webhooks.create({ url: 'https://tu-servidor.com/apiempresas', event: 'watchlist.*' });
// (1.5.0) Sociedades nuevas del BORME del día, con filtros opcionales
const nuevas = await api.webhooks.create({
url: 'https://tu-servidor.com/nuevas',
event: 'company.created',
filters: { province: ['MADRID', 'BARCELONA'], keywords: ['software', 'informática'], min_capital: 3000 }
});
// Guarda hook.secret: con él se comprueba la firma.
await api.webhooks.test(hook.id); // envía un test.ping y devuelve lo que respondió tu servidor
// Historial de entregas (lo más reciente primero): estado, intentos, código HTTP y error
const { data, meta } = await api.webhooks.deliveries(hook.id, { status: 'dead', limit: 20 });En tu servidor, comprueba la firma con el cuerpo en bruto (no el JSON ya parseado):
import express from 'express';
import { constructWebhookEvent } from 'apiempresas';
app.post('/apiempresas', express.raw({ type: 'application/json' }), (req, res) => {
try {
const evento = constructWebhookEvent(req.body, req.header('X-ApiEmpresas-Signature'), process.env.APIEMPRESAS_WEBHOOK_SECRET!);
// evento.event: company.created | company.borme_act | company.status_changed | company.risk_level_changed | test.ping
// Usa evento.id (o la cabecera X-ApiEmpresas-Delivery) para no procesar dos veces el mismo envío.
res.sendStatus(200);
} catch {
res.sendStatus(400);
}
});Si tu servidor no responde 2xx, se reintenta a los 1 min, 5 min, 30 min, 2 h y 6 h.
8. Licitaciones públicas abiertas (Business) — 1 consulta
// Concursos en plazo: Plataforma de Contratación del Sector Público y plataformas autonómicas
const { tenders, pagination } = await api.tenders.list({ q: 'limpieza', province: 'Madrid', type: 'services' });
console.log(pagination.total, tenders[0].title, tenders[0].deadline, tenders[0].url);
// Las parecidas a los contratos que una empresa ya ha ganado (ordenadas por match_score)
const forCompany = await api.tenders.forCompany('A82018474');9. Listas de sanciones (Business) — 1 consulta
// Una empresa y sus administradores vigentes, en las listas de la UE, la ONU y OFAC
const r = await api.sanctions.screen({ cif: 'A46103834' });
console.log(r.summary.result); // 'clear' | 'possible_match' | 'match'
// Un nombre suelto
const p = await api.sanctions.screen({ name: 'Vladimir Putin', type: 'person' });
// Cada coincidencia trae level (strong | possible), list, programs, countries y birth_dates para confirmarlaEn companies.verify() con Business, el resultado incluye checks.sanctions.
10. Subvenciones y ayudas públicas (Business, desde 1.5.0) — 1 consulta
// Concesiones de la BDNS desde 2022 (Kit Digital incluido)
const s = await api.companies.subsidies('B12345678', { year: 2025, limit: 50 });
console.log(s.summary.total_grants, s.summary.total_amount); // importes en texto con dos decimales
console.log(s.summary.by_year, s.summary.top_calls);
for (const g of s.grants) console.log(g.grant_date, g.amount, g.call);11. Feed de cambios (Business, desde 1.5.0) — 1 consulta por página
// Todas las sociedades con un acto en el BORME desde una fecha (hasta 30 días atrás)
const { data, meta } = await api.companies.changes({ since: '2026-10-19', types: ['address', 'officers'], province: 'MADRID' });
// Siguiente página: api.companies.changes({ ...mismosFiltros, cursor: meta.next_cursor })
// O recorre todas las páginas
for await (const c of api.companies.iterateChanges({ since: '2026-10-19' })) {
console.log(c.id, c.cif, c.change_types, c.status_code); // usa c.id para no procesar dos veces el mismo cambio
}Solo salen sociedades con CIF: las recién constituidas todavía no lo tienen y llegan por el webhook company.created. Los tipos de cambio están en catalog().change_types.
12. Estadísticas del BORME (todos los planes, desde 1.5.0) — 1 consulta
const st = await api.stats({ metrics: ['incorporations', 'dissolutions'], province: 'MADRID', from: '2025-01', to: '2025-12' });
console.log(st.totals.incorporations);
for (const fila of st.series) console.log(fila.period, fila.incorporations);
// Por años: api.stats({ group: 'year', from: '2015', to: '2025' })Entorno de Pruebas (Sandbox)
Para probar la integración sin consumir saldo, apunta a la URL del Sandbox con tu misma API Key. Devuelve datos simulados: A15075062 (empresa encontrada) y B00000000 (no encontrada). Tiene también subsidies() y changes(); stats() y usage() no.
const api = new ApiEmpresas({
apiKey: 'TU_API_KEY',
baseURL: 'https://apiempresas.es/api/sandbox/v1'
});Consumo
const u = await api.usage(); // no gasta consultas; no está en el Sandbox
console.log(u.stats.remaining_calls, u.stats.wallet_balance, u.stats.quota_resets_at);Valores de los filtros
const cat = await api.catalog(); // no gasta consultas; también en el Sandbox
console.log(cat.province, cat.size_band, cat.radar.priority, cat.webhook_events, cat.change_types);Manejo de Errores
El SDK arrojará un ApiError detallado si ocurre algún problema (ej. Plan Insuficiente, CIF inválido).
import { ApiError } from 'apiempresas';
try {
await api.companies.score('B00000000');
} catch (error) {
if (error instanceof ApiError) {
console.error(`Status: ${error.status}`);
console.error(`Message: ${error.message}`);
console.error(`Code: ${error.code}`); // p. ej. QUOTA_EXCEEDED, TOO_MANY_REQUESTS, API_KEY_INVALID
console.error(`Request: ${error.requestId}`); // dáselo a soporte
console.error(`Retry after: ${error.retryAfter}`); // segundos, en los 429 por velocidad
console.error(`Plan: ${error.requiredPlan} ${error.checkoutUrl}`); // en los 403 de plan
}
}Un 429 con code: 'TOO_MANY_REQUESTS' se resuelve esperando y reintentando; con code: 'QUOTA_EXCEEDED' no: hay que recargar saldo o cambiar de plan. errorCode sigue devolviendo el campo error de la respuesta, como en versiones anteriores.
Licencia
MIT
