Documentacion API
REST + JSON. Sin autenticacion. CORS abierto. Base URL: https://openmercantil.es
Especificación machine-readable
OpenAPI 3.1Toda la API está descrita en un fichero OpenAPI 3.1 consumible directamente por Swagger Editor, Postman, Insomnia, Stoplight, generadores de clientes (openapi-generator) y agregadores de catálogos (DCAT-AP, data.europa.eu, etc.).
También disponible vía Accept: application/json o sufijo .json. CORS abierto (Access-Control-Allow-Origin: *) para tooling externo.
Catálogo DCAT-AP-ES en /catalog.rdf (RDF/XML).
Empezar en 30 segundos
Copia cualquiera de estos comandos. No necesitas clave de API.
1. Buscar una empresa
curl "https://openmercantil.es/api/v1/search?q=mercadona&limit=5"
2. Obtener ficha completa de empresa
curl "https://openmercantil.es/api/v1/company/mercadona-sa-a46103834"
3. Estado del sistema
curl "https://openmercantil.es/api/v1/health"
Rate limiting
Free: 60 req/min y 200 req/día por IP. Planes superiores (Profesional 5.000 req/día, MAX 50.000 req/día, Enterprise 500.000+ req/día) según cuenta y API key.
| Plan | Por minuto | Por día | Acceso |
|---|---|---|---|
| Free | 60 | 200 | Anónimo, por IP |
| Profesional | 120 | 5.000 | API key personal |
| MAX | 600 | 50.000 | API key personal |
| Enterprise | a convenir | 500.000+ | Contrato |
Las respuestas incluyen estas cabeceras (RFC draft-ietf-httpapi-ratelimit-headers):
| Cabecera | Descripción |
|---|---|
X-RateLimit-Limit | Cuota diaria del plan actual (ej. 200 en Free). |
X-RateLimit-Remaining | Peticiones restantes en la ventana diaria. |
X-RateLimit-Reset | Timestamp Unix en que se reinicia el contador. |
X-OpenMercantil-Plan | Etiqueta del plan resuelto (Free, Profesional, MAX, Enterprise). |
Retry-After | Segundos a esperar (solo en respuestas 429). |
Para cargas masivas usa los datasets descargables en lugar de recorrer la API en bucle.
Errores
| Código | Significado | Causa habitual |
|---|---|---|
200 OK | Exito | Peticion correcta con resultados. |
400 Bad Request | Parametro invalido | Falta q, formato de fecha incorrecto, etc. |
404 Not Found | Recurso inexistente | Slug de empresa o persona desconocido. |
429 Too Many Requests | Rate limit superado | Más de 60 req/min o 200 req/día desde la misma IP en Free. |
500 Internal Server Error | Error interno | Artefacto corrupto o fallo de proceso. |
Ejemplo de respuesta de error:
{
"error": "not_found",
"message": "Company slug not found",
"status": 404
}
Paginacion
La API usa dos esquemas según el endpoint:
| Endpoint | Parametros | Notas |
|---|---|---|
Búsqueda (/search) | limit + offset | Desplazamiento clasico. Max limit=100. |
Eventos empresa (/events) | page + page_size | Paginacion por número de pagina. Default page_size=50. |
La respuesta siempre incluye total o count para calcular el número de paginas.
Bulk downloads
Si necesitas miles de registros, descarga los datasets completos desde /descargas en lugar de llamar la API en bucle. Estan disponibles:
- Indice de empresas en JSON (comprimido .gz)
- Indice de personas en JSON
- Dataset completo de eventos en CSV por ano
- Snapshots mensuales de cargos vigentes
Usa la API para consultas puntuales y los datasets para analisis masivo, ETL o entrenamiento de modelos.
Búsqueda de empresas
Busca empresas (y opcionalmente personas) por nombre o CIF usando un indice por prefijos. Devuelve resultados ordenados por relevancia.
Parametros
| Nombre | Tipo | Requerido | Default | Descripción |
|---|---|---|---|---|
q | string | si | — | Texto de búsqueda. Minimo 2 caracteres. |
limit | int | no | 20 | Resultados por pagina (1-100). |
offset | int | no | 0 | Desplazamiento para paginacion. |
include_persons | 0|1 | no | 0 | Si es 1, incluye personas en la respuesta. |
Respuesta
| Campo | Tipo | Descripción |
|---|---|---|
query | string | Texto de búsqueda utilizado. |
count | int | Total de empresas encontradas. |
items | array | Lista de empresas: {name, cif, slug, status}. |
persons | array | Lista de personas (solo si include_persons=1). |
persons_count | int | Total de personas encontradas (solo si include_persons=1). |
curl "https://openmercantil.es/api/v1/search?q=inditex&limit=5"import requests
r = requests.get("https://openmercantil.es/api/v1/search", params={"q":"inditex","limit":5})
print(r.json())const res = await fetch("https://openmercantil.es/api/v1/search?q=inditex&limit=5");
const data = await res.json();
console.log(data.items);Ficha completa de empresa
Devuelve el informe completo de una empresa: datos registrales, KPIs, timeline de actividad, cargos y lista de actos.
Parametros
| Nombre | Tipo | Requerido | Default | Descripción |
|---|---|---|---|---|
slug | string | si | — | Identificador slug de la empresa (nombre-cif). Ej: inditex-sa-a15075062. |
Respuesta
| Campo | Tipo | Descripción |
|---|---|---|
company.name | string | Nombre oficial. |
company.cif | string | CIF/NIF registral. |
company.status | string | Estado actual: ACTIVA, EXTINGUIDA, DISOLUCION, etc. |
company.company_type | string | Tipo societario (SA, SL, etc.). |
company.aliases | array | Nombres anteriores o variaciones. |
company.address | string | Domicilio social. |
company.capital | string | Capital social declarado. |
company.website | string | Sitio web si consta en BORME. |
company.workers | int | Num. de trabajadores si consta. |
company.date_creation | string | Fecha de constitucion. |
kpis.acts_count | int | Total de actos registrados. |
kpis.first_seen | string | Primera fecha en el sistema. |
kpis.last_seen | string | Última fecha en el sistema. |
timeline | array | Array {period, count} mensual. |
top_event_types | array | Tipos de acto mas frecuentes. |
top_provinces | array | Provincias de actividad. |
events | array | Actos recientes (max 50 por defecto). |
officers.current | array | Cargos vigentes. |
officers.historical | array | Cargos anteriores. |
summary_text | string | Resumen en lenguaje natural. |
curl "https://openmercantil.es/api/v1/company/inditex-sa-a15075062"import requests
slug = "inditex-sa-a15075062"
r = requests.get(f"https://openmercantil.es/api/v1/company/{slug}")
data = r.json()
print(data["kpis"]["acts_count"])const slug = "inditex-sa-a15075062";
const res = await fetch(`https://openmercantil.es/api/v1/company/${slug}`);
const { company, kpis } = await res.json();
console.log(company.name, kpis.last_seen);Eventos paginados de empresa
Devuelve los actos registrales de una empresa para un ano concreto, paginados para respuestas ligeras.
Parametros
| Nombre | Tipo | Requerido | Default | Descripción |
|---|---|---|---|---|
slug | string | si | — | Slug de la empresa. |
year | int | no | — | Ano a filtrar (YYYY). Sin valor, devuelve todos. |
page | int | no | 1 | Número de pagina (base 1). |
page_size | int | no | 50 | Resultados por pagina. |
Respuesta
| Campo | Tipo | Descripción |
|---|---|---|
slug | string | Slug de la empresa. |
year | int | Ano filtrado. |
page | int | Pagina actual. |
page_size | int | Resultados por pagina. |
total | int | Total de eventos para el ano/filtro. |
events | array | Array de actos: {id, date, type, province, description}. |
curl "https://openmercantil.es/api/v1/company/inditex-sa-a15075062/events?year=2025&page=1&page_size=20"import requests
r = requests.get("https://openmercantil.es/api/v1/company/inditex-sa-a15075062/events",
params={"year":2025,"page":1,"page_size":20})
print(r.json()["total"])const res = await fetch(
"https://openmercantil.es/api/v1/company/inditex-sa-a15075062/events?year=2025&page=1"
);
const { events, total } = await res.json();Cargos de empresa
/api/v1/company/{slug}/officers
Devuelve administradores, apoderados y otros cargos, separados en vigentes e historicos.
Parametros
| Nombre | Tipo | Requerido | Default | Descripción |
|---|---|---|---|---|
slug | string | si | — | Slug de la empresa. |
Respuesta
| Campo | Tipo | Descripción |
|---|---|---|
slug | string | Slug de la empresa. |
officers.current | array | Cargos vigentes: {name, role, person_slug, date_from}. |
officers.historical | array | Cargos anteriores: {name, role, person_slug, date_from, date_to}. |
curl "https://openmercantil.es/api/v1/company/inditex-sa-a15075062/officers"import requests
r = requests.get("https://openmercantil.es/api/v1/company/inditex-sa-a15075062/officers")
for c in r.json()["officers"]["current"]:
print(c["name"], c["role"])const res = await fetch("https://openmercantil.es/api/v1/company/inditex-sa-a15075062/officers");
const { officers } = await res.json();
officers.current.forEach(c => console.log(c.name, c.role));Exportar empresa (JSON adjunto)
Devuelve el informe completo como fichero descargable con Content-Disposition: attachment.
Parametros
| Nombre | Tipo | Requerido | Default | Descripción |
|---|---|---|---|---|
slug | string | si | — | Slug de la empresa. |
format | string | no | json | Formato de exportacion (actualmente solo json). |
Respuesta
| Campo | Tipo | Descripción |
|---|---|---|
— | application/json | Mismo esquema que /company/{slug} pero con cabecera de descarga. |
curl -O -J "https://openmercantil.es/api/v1/company/inditex-sa-a15075062/export?format=json"import requests
r = requests.get("https://openmercantil.es/api/v1/company/inditex-sa-a15075062/export?format=json")
with open("informe.json","wb") as f:
f.write(r.content)const res = await fetch("https://openmercantil.es/api/v1/company/inditex-sa-a15075062/export?format=json");
const blob = await res.blob();
const url = URL.createObjectURL(blob);
// asignar a un <a download="informe.json">Búsqueda de personas
Busca personas fisicas por nombre en el indice de cargos del BORME.
Parametros
| Nombre | Tipo | Requerido | Default | Descripción |
|---|---|---|---|---|
q | string | si | — | Nombre (o fragmento) a buscar. Minimo 3 caracteres. |
limit | int | no | 20 | Resultados maximos (1-100). |
Respuesta
| Campo | Tipo | Descripción |
|---|---|---|
query | string | Texto buscado. |
count | int | Total de coincidencias. |
items | array | Lista: {name, slug, companies_count, active_positions_count}. |
curl "https://openmercantil.es/api/v1/person/search?q=amancio+ortega&limit=5"import requests
r = requests.get("https://openmercantil.es/api/v1/person/search", params={"q":"amancio ortega"})
print(r.json()["items"])const res = await fetch("https://openmercantil.es/api/v1/person/search?q=amancio%20ortega");
const { items } = await res.json();Ficha de persona
Devuelve el perfil completo de una persona: cargos activos, historicos y empresas vinculadas.
Parametros
| Nombre | Tipo | Requerido | Default | Descripción |
|---|---|---|---|---|
slug | string | si | — | Slug normalizado de la persona. |
Respuesta
| Campo | Tipo | Descripción |
|---|---|---|
slug | string | Slug de la persona. |
name | string | Nombre completo. |
first_seen | string | Primera aparicion en BORME. |
last_seen | string | Última aparicion en BORME. |
companies_count | int | Total de empresas en las que ha tenido cargo. |
active_positions | array | Cargos vigentes: {company_name, slug, role, date_from}. |
inactive_positions | array | Cargos anteriores: {company_name, slug, role, date_from, date_to}. |
curl "https://openmercantil.es/api/v1/person/amancio-ortega-gaona"import requests
r = requests.get("https://openmercantil.es/api/v1/person/amancio-ortega-gaona")
p = r.json()
print(p["name"], "—", p["companies_count"], "empresas")const res = await fetch("https://openmercantil.es/api/v1/person/amancio-ortega-gaona");
const person = await res.json();
console.log(person.active_positions);Sumario diario BORME
Devuelve todos los actos publicados en el BORME para una fecha, agrupados por seccion y provincia. Alias: /api/v1/summary/date/{YYYY-MM-DD}.
Parametros
| Nombre | Tipo | Requerido | Default | Descripción |
|---|---|---|---|---|
YYYY-MM-DD | string | si | — | Fecha en formato ISO 8601. Ej: 2026-04-17. |
Respuesta
| Campo | Tipo | Descripción |
|---|---|---|
date | string | Fecha consultada. |
sections | object | Objeto con secciones BORME (1=SA/SL, 2=otros, 3=concursal). Cada seccion es un objeto provincia → [{id, company, type, slug}]. |
curl "https://openmercantil.es/api/v1/daily/2026-04-17"import requests
r = requests.get("https://openmercantil.es/api/v1/daily/2026-04-17")
data = r.json()
for prov, actos in data["sections"]["1"].items():
print(prov, len(actos), "actos")const res = await fetch("https://openmercantil.es/api/v1/daily/2026-04-17");
const { date, sections } = await res.json();
const seccion1 = sections["1"];
console.log(Object.keys(seccion1)); // provinciasHealth check
Devuelve el estado operativo del sistema, la edad de los artefactos y metricas de volumen.
Parametros
| Nombre | Tipo | Requerido | Default | Descripción |
|---|
Respuesta
| Campo | Tipo | Descripción |
|---|---|---|
status | string | ok o degraded. |
service | string | Nombre del servicio. |
version | string | Version de la API. |
artifacts_age_hours | float | Horas desde la última actualizacion de artefactos. |
artifacts_last_modified | string | Fecha ISO del último artefacto. |
latest_borme_processed | string | Última fecha de BORME procesada. |
companies_indexed_approx | int | Empresas indexadas aproximadas. |
total_persons_approx | int | Personas indexadas aproximadas. |
total_events_approx | int | Eventos totales aproximados. |
csv_size_mb | float | Tamano del CSV principal en MB. |
timestamp | string | Fecha/hora de la respuesta en ISO 8601. |
curl "https://openmercantil.es/api/v1/health"import requests
r = requests.get("https://openmercantil.es/api/v1/health")
h = r.json()
print(h["status"], "—", h["companies_indexed_approx"], "empresas")const res = await fetch("https://openmercantil.es/api/v1/health");
const health = await res.json();
console.log(health.status, health.latest_borme_processed);Export de eventos en CSV
Streaming CSV de todos los eventos, opcionalmente filtrado por ano. Util para analisis de datos y ETL.
Parametros
| Nombre | Tipo | Requerido | Default | Descripción |
|---|---|---|---|---|
year | int | no | — | Ano a filtrar (YYYY). Sin valor, devuelve todos. |
format | string | no | csv | Solo csv por ahora. |
Respuesta
| Campo | Tipo | Descripción |
|---|---|---|
— | text/csv | Fichero CSV con cabecera: id, date, company_slug, company_name, type, province, description. |
curl -O "https://openmercantil.es/api/v1/export/events?year=2025&format=csv"import requests
r = requests.get("https://openmercantil.es/api/v1/export/events", params={"year":2025,"format":"csv"}, stream=True)
with open("eventos_2025.csv","wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)const res = await fetch("https://openmercantil.es/api/v1/export/events?year=2025&format=csv");
const text = await res.text();
// parsear con PapaParse u otro parser CSVExport CSV directo
Endpoint legado de exportacion CSV. Acepta empresa, persona o búsqueda libre.
Parametros
| Nombre | Tipo | Requerido | Default | Descripción |
|---|---|---|---|---|
format | string | si | csv | Debe ser csv. |
empresa | string | no | — | Slug de empresa. |
persona | string | no | — | Slug de persona. |
q | string | no | — | Texto de búsqueda para exportar resultados. |
Respuesta
| Campo | Tipo | Descripción |
|---|---|---|
— | text/csv | CSV de los actos de la empresa/persona o resultados de búsqueda. |
# Por empresa
curl -O "https://openmercantil.es/export?format=csv&empresa=inditex-sa-a15075062"
# Por persona
curl -O "https://openmercantil.es/export?format=csv&persona=amancio-ortega-gaona"
# Por búsqueda
curl -O "https://openmercantil.es/export?format=csv&q=mercadona"import requests
r = requests.get("https://openmercantil.es/export",
params={"format":"csv","empresa":"inditex-sa-a15075062"})
with open("empresa.csv","wb") as f:
f.write(r.content)const res = await fetch("https://openmercantil.es/export?format=csv&empresa=inditex-sa-a15075062");
const blob = await res.blob();
const url = URL.createObjectURL(blob);Endpoints v1.1 — Sectores, Score, Fuentes externas
Endpoints añadidos en abril 2026 para navegación CNAE, score de actividad, gadgets visuales y datos de fuentes externas.
| Endpoint | Descripción |
|---|---|
GET /api/v1/cnae/tree |
Árbol jerárquico CNAE 2009 (21 secciones + 88 divisiones) con conteos. |
GET /api/v1/cnae/{code} |
Info de un código CNAE (ej. J o 62). |
GET /api/v1/sector/{code}/companies |
Empresas del sector. Params: limit, offset, sort, province. |
GET /api/v1/sector/{code}/ratios |
Ratios financieros sectoriales (ROE, ROA, margen) — fuente BdE. |
GET /api/v1/company/{slug}/score |
Score 0-100 de actividad + breakdown (volume, recency, diversity, officers). |
GET /api/v1/company/{slug}/activity |
Timeseries mensual de actos para gráficos. |
GET /api/v1/company/{slug}/similar |
Empresas similares (mismo CNAE × misma provincia). Param: limit. |
GET /api/v1/company/{slug}/geocode |
Coordenadas lat/lng del domicilio social (cache Nominatim). |
GET /api/v1/company/{slug}/grants |
Subvenciones públicas recibidas por NIF (fuente BDNS). |
GET /api/v1/company/{slug}/sanctions |
Cruce con listas Open Sanctions (PEP / sanciones internacionales). |
GET /api/v1/company/{slug}/cnmv |
Datos de cotización + hechos relevantes (si la empresa cotiza). |
GET /api/v1/sources/status |
Estado de cada fuente externa: última sincronización, items count. |
POST /api/v1/checkout |
Crear sesión Stripe Checkout (suscripción Pro/Business/Agency). Auth requerida. |
POST /api/v1/donation |
Crear sesión Stripe one-time (donación). Anónima permitida. Body: {amount_cents: 500-100000}. |
Ejemplo: empresas TI en Madrid ordenadas por actividad
curl "https://openmercantil.es/api/v1/sector/62/companies?province=madrid&sort=acts&limit=10"
Ejemplo: score + similares de una empresa
curl "https://openmercantil.es/api/v1/company/inditex-sa-a15075062/score" | jq
curl "https://openmercantil.es/api/v1/company/inditex-sa-a15075062/similar?limit=5" | jq
🆕 Endpoints v1.4 (mayo 2026)
Cruce abierto BORME × CNMV × OEPM × PLACSP × BOE × sanciones. Sin API key, rate-limit suave, cache 1h.
Trust Score (educativo)
GET /api/v1/company/{slug}/trust-score
Score 0-100 basado en datos públicos. NO es scoring crediticio. Devuelve {score, band, label, factors[], disclaimer}. Cache 24h.
Sociograma de cargos
GET /api/v1/company/{slug}/network
Bipartite graph: empresa central → admins → otras empresas con admins comunes. Renderizable con vis-network o D3.js. Limit 12 personas × 4 empresas.
Embargos AEAT/TGSS (BOE)
GET /api/v1/company/{slug}/embargoes
Diligencias de embargo + subastas + liberaciones publicadas en BOE Sección V-B. Devuelve {count, summary, items[]} con tipo, importe, expediente, organismo, fecha y URL al anuncio oficial.
Contratos PLACSP (persona)
GET /api/v1/persona/{slug}/contracts
Contratos públicos adjudicados a empresas que esta persona administra(ba). Solo cuenta contratos firmados durante el período del cargo. Devuelve {count, total_eur, by_company[], top[], by_cpv[]}.
Top empresas por licitaciones
GET /api/v1/contracts/top-companies?limit=50&province=madrid&cnae=F
GET /api/v1/contracts/top-companies.csv?limit=100 # Excel-friendly UTF-8 BOM
Filtros opcionales: province (slug), cnae (sección A-U), limit (1-500).
Top personas por licitaciones
GET /api/v1/contracts/top-persons?limit=30
GET /api/v1/contracts/top-persons.csv?limit=100
Personas administradoras con más contratos públicos en empresas que dirigen. Período del cargo se respeta.
Estadísticas sectoriales (CNAE)
GET /api/v1/sectores/stats # JSON
GET /api/v1/sectores/stats.csv # Excel
Por sección CNAE: total empresas, activas/extintas/concurso, capital total inscrito, contratos PLACSP. Cache 24h.
Estadísticas por CCAA
GET /api/v1/ccaa/stats # 18 CCAA + Ceuta + Melilla
Distribución geográfica por Comunidad Autónoma. Cruce automático provincia → CCAA.
Geocode + mapa
GET /api/v1/company/{slug}/geocode[?force=1]
Devuelve {lat, lng, address, source}. Cache hit instantáneo. Cache miss: live Nominatim OSM (rate-limit 1 req/sec). force=1 bypassa cache.
⚠️ Nota legal: Trust Score es educativo (NO scoring crediticio profesional). Embargos pueden no incluir resoluciones posteriores. Contratos PLACSP no incluyen modificaciones contractuales. Para due diligence consulta fuentes oficiales o proveedores acreditados.
Changelog
| Version | Fecha | Cambios |
|---|---|---|
| v1.4 | 2026-05-05 | NEW: /company/{slug}/trust-score, /company/{slug}/embargoes, /company/{slug}/network, /persona/{slug}/contracts, /contracts/top-companies + top-persons (.csv export), /sectores/stats, /ccaa/stats (.csv/.json). Geocode con fallback Nominatim live (sin pipeline previo). |
| v1.2 | 2026-04-27 | Endpoints fuentes externas: /grants (BDNS), /sanctions (OpenSanctions), /cnmv, /geocode. Endpoint /sources/status con frescura por fuente. |
| v1.1 | 2026-04-27 | Endpoints CNAE: árbol jerárquico, info por código, listado por sector. Endpoints empresa: score, activity timeseries, similares. Endpoint sector: ratios financieros (BdE). Stripe: POST /checkout, POST /donation. |
| v1.0 | 2026-04-17 | Release inicial. Endpoints de búsqueda, ficha empresa/persona, sumario diario, exportacion CSV, health check y documentacion pública. |