Cómo monitorizar subvenciones del BDNS con una API

Aprende a consultar y monitorizar subvenciones públicas de España usando la API del BDNS. Filtra por CCAA, sector e importe. Ejemplos en JavaScript y Python.

El BDNS (Base de Datos Nacional de Subvenciones) es el repositorio oficial del Ministerio de Hacienda con todas las convocatorias de subvenciones públicas de España — estado, comunidades autónomas, diputaciones y ayuntamientos.

El problema: la interfaz web del BDNS es lenta, poco filtrable y no tiene ninguna opción de notificación automática. Si quieres monitorizar subvenciones para tu empresa o tus clientes, necesitas estar entrando manualmente todos los días.

En este tutorial te mostramos cómo automatizarlo completamente.

Qué datos expone el BDNS

Cada convocatoria incluye:

  • Título y descripción de la convocatoria
  • Órgano convocante (ministerio, consejería, ayuntamiento…)
  • Importe total de la convocatoria
  • Beneficiario (empresas, autónomos, investigadores, ONGs…)
  • CCAA a la que aplica
  • Sector económico
  • Fecha de convocatoria y fecha de resolución
  • Estado: abierta, resuelta, archivada
  • URL oficial en el BDNS para más detalles

Obtener tu API key

Primero necesitas una API key gratuita — 20 requests/mes sin tarjeta:

  1. Ve a apispain.esEmpezar gratis
  2. Introduce tu email y guarda la key

Consultar subvenciones abiertas

Con curl

# Todas las subvenciones abiertas
curl "https://api.apispain.es/v1/bdns/subvenciones?abiertas=true" \
  -H "Authorization: Bearer TU_API_KEY"

# Solo en Madrid
curl "https://api.apispain.es/v1/bdns/subvenciones?ccaa=Madrid&abiertas=true" \
  -H "Authorization: Bearer TU_API_KEY"

# Por sector
curl "https://api.apispain.es/v1/bdns/subvenciones?sector=tecnologia&abiertas=true" \
  -H "Authorization: Bearer TU_API_KEY"

Respuesta:

[
  {
    "id": "abc123",
    "bdnsId": "BDNS-789456",
    "convocatoria": "Ayudas para la modernización del sector industrial",
    "organoConvocante": "Ministerio de Industria y Turismo",
    "importe": null,
    "importeTotal": "15000000.00",
    "beneficiario": "PYMEs industriales",
    "ccaa": "Nacional",
    "sector": "industria",
    "fechaConvocatoria": "2026-03-01T00:00:00.000Z",
    "fechaResolucion": "2026-06-30T00:00:00.000Z",
    "estado": "abierta",
    "urlBdns": "https://www.infosubvenciones.es/bdnstrans/GE/es/convocatoria/789456"
  }
]

Con JavaScript

const API_KEY = process.env.APISPAIN_API_KEY

async function buscarSubvenciones({ ccaa, sector, importeMin } = {}) {
  const url = new URL('https://api.apispain.es/v1/bdns/subvenciones')
  url.searchParams.set('abiertas', 'true')
  if (ccaa)       url.searchParams.set('ccaa', ccaa)
  if (sector)     url.searchParams.set('sector', sector)
  if (importeMin) url.searchParams.set('importeMin', importeMin)

  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${API_KEY}` }
  })

  if (!res.ok) throw new Error(`Error ${res.status}`)
  return res.json()
}

// Subvenciones de tecnología en Cataluña con más de 100.000€
const subvenciones = await buscarSubvenciones({
  ccaa: 'Cataluña',
  sector: 'tecnologia',
  importeMin: 100000,
})

console.log(`${subvenciones.length} convocatorias encontradas`)
subvenciones.forEach(s => {
  console.log(`• ${s.convocatoria}`)
  console.log(`  Órgano: ${s.organoConvocante}`)
  console.log(`  Importe: ${s.importeTotal ? `${Number(s.importeTotal).toLocaleString('es-ES')}€` : 'No especificado'}`)
  console.log(`  Cierre: ${new Date(s.fechaResolucion).toLocaleDateString('es-ES')}`)
  console.log(`  Más info: ${s.urlBdns}`)
  console.log()
})

Con Python

import requests
import os
from datetime import datetime

API_KEY = os.environ['APISPAIN_API_KEY']
BASE_URL = 'https://api.apispain.es/v1'

def buscar_subvenciones(ccaa=None, sector=None, importe_min=None):
    params = {'abiertas': 'true'}
    if ccaa:        params['ccaa'] = ccaa
    if sector:      params['sector'] = sector
    if importe_min: params['importeMin'] = importe_min

    res = requests.get(
        f'{BASE_URL}/bdns/subvenciones',
        params=params,
        headers={'Authorization': f'Bearer {API_KEY}'}
    )
    res.raise_for_status()
    return res.json()

# Subvenciones de I+D en Andalucía
subvenciones = buscar_subvenciones(ccaa='Andalucía', sector='investigacion')

for s in subvenciones:
    fecha_cierre = datetime.fromisoformat(s['fechaResolucion'].replace('Z', '+00:00'))
    print(f"• {s['convocatoria']}")
    print(f"  Importe: {float(s['importeTotal'] or 0):,.0f}€")
    print(f"  Cierre: {fecha_cierre.strftime('%d/%m/%Y')}")
    print(f"  URL: {s['urlBdns']}")
    print()

Sistema de alertas por email

El caso de uso más valioso: recibir un email automático cuando se abra una subvención relevante. Este script comprueba las novedades y envía alertas:

import nodemailer from 'nodemailer'
import fs from 'fs/promises'

const API_KEY = process.env.APISPAIN_API_KEY

// Configuración de alertas por perfil
const PERFILES = [
  {
    nombre: 'Startup Tecnología Madrid',
    email: 'gestor@tuempresa.com',
    filtros: { ccaa: 'Madrid', sector: 'tecnologia' },
  },
  {
    nombre: 'Empresa Agroalimentaria Andalucía',
    email: 'direccion@otraempresa.com',
    filtros: { ccaa: 'Andalucía', sector: 'agricultura' },
  },
]

async function cargarSubvencionesVistas() {
  try {
    const raw = await fs.readFile('subvenciones-vistas.json', 'utf-8')
    return new Set(JSON.parse(raw))
  } catch {
    return new Set()
  }
}

async function guardarSubvencionesVistas(vistas) {
  await fs.writeFile('subvenciones-vistas.json', JSON.stringify([...vistas]))
}

async function revisarYNotificar() {
  const vistas = await cargarSubvencionesVistas()
  const nuevasIds = []

  for (const perfil of PERFILES) {
    const url = new URL('https://api.apispain.es/v1/bdns/subvenciones')
    url.searchParams.set('abiertas', 'true')
    Object.entries(perfil.filtros).forEach(([k, v]) => url.searchParams.set(k, v))

    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${API_KEY}` }
    })
    const subvenciones = await res.json()

    const nuevas = subvenciones.filter(s => !vistas.has(s.bdnsId))

    if (nuevas.length > 0) {
      await enviarAlerta(perfil, nuevas)
      nuevas.forEach(s => nuevasIds.push(s.bdnsId))
    }
  }

  if (nuevasIds.length > 0) {
    nuevasIds.forEach(id => vistas.add(id))
    await guardarSubvencionesVistas(vistas)
    console.log(`✅ ${nuevasIds.length} nuevas subvenciones notificadas`)
  } else {
    console.log('✅ Sin novedades')
  }
}

async function enviarAlerta(perfil, subvenciones) {
  // Configura tu SMTP aquí (o usa Resend, SendGrid, etc.)
  const transporter = nodemailer.createTransport({ /* tu config */ })

  const lista = subvenciones.map(s => `
    <li>
      <strong>${s.convocatoria}</strong><br/>
      Órgano: ${s.organoConvocante}<br/>
      Importe: ${s.importeTotal ? `${Number(s.importeTotal).toLocaleString('es-ES')}€` : 'No especificado'}<br/>
      <a href="${s.urlBdns}">Ver en BDNS →</a>
    </li>
  `).join('')

  await transporter.sendMail({
    from: 'Alertas Subvenciones <alertas@tudominio.com>',
    to: perfil.email,
    subject: `${subvenciones.length} nueva(s) subvención(es) para ${perfil.nombre}`,
    html: `<ul>${lista}</ul>`,
  })
}

revisarYNotificar()

Ejecútalo con un cron cada 6 horas (que es la frecuencia de actualización del BDNS):

0 */6 * * * node /ruta/alertas-subvenciones.js >> /var/log/subvenciones.log 2>&1

Webhooks en tiempo real (plan Pro)

Si no quieres gestionar el cron tú mismo, el plan Pro incluye webhooks. Apispain ejecuta el rastreo y te notifica automáticamente:

import Apispain from 'apispain'

const client = new Apispain({ apiKey: process.env.APISPAIN_API_KEY })

const webhook = await client.webhooks.create({
  url: 'https://tu-servidor.com/webhook/subvenciones',
  secret: 'tu-secreto',
  eventos: ['bdns.subvencion'],
  filtros: {
    ccaa: 'Madrid',
    sector: 'tecnologia',
  },
})

Subvenciones por beneficiario específico

Si buscas subvenciones para un tipo concreto de empresa:

// Subvenciones para autónomos en toda España
const autonomos = await fetch(
  'https://api.apispain.es/v1/bdns/subvenciones?beneficiario=autonomos&abiertas=true',
  { headers: { Authorization: `Bearer ${API_KEY}` } }
).then(r => r.json())

// Subvenciones para startups
const startups = await fetch(
  'https://api.apispain.es/v1/bdns/subvenciones?beneficiario=startup&abiertas=true',
  { headers: { Authorization: `Bearer ${API_KEY}` } }
).then(r => r.json())

Próximos pasos

Empieza gratis en 2 minutos

20 requests/mes gratis, sin tarjeta de crédito. API key lista al momento.

Ver documentación