npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

apiempresas

v1.5.0

Published

SDK oficial de APIEmpresas.es para Node.js y TypeScript

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 apiempresas

Inicializació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 message

Cada 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 confirmarla

En 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