# Subvenciones BDNS (España) — convocatorias oficiales (`daniizarsan/bdns-subsidios`) Actor

Convierte la Base de Datos Nacional de Subvenciones (BDNS) en un dataset filtrable: convocatorias con presupuesto, plazos, sectores CNAE, bases reguladoras y ayudas concedidas (cruces tolerantes a fallos). Datos oficiales, actualizados a diario.

- **URL**: https://apify.com/daniizarsan/bdns-subsidios.md
- **Developed by:** [Daniel de la Zarza Sánchez](https://apify.com/daniizarsan) (community)
- **Categories:** Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Subvenciones BDNS (España) — convocatorias oficiales con cruces

Busca convocatorias de subvenciones públicas españolas en la **API oficial de la BDNS** (Base de Datos Nacional de Subvenciones, infosubvenciones.es) y las enriquece con tres cruces. Guarda todo en un dataset limpio y filtrable.

### Qué obtienes (hasta 45 campos por convocatoria)

- **Base** (búsqueda oficial): código BDNS, título, órgano convocante, administración, fecha de recepción, MRR y PDF oficial.
- **Cruce 1 — detalle**: tipo de convocatoria, presupuesto total, periodo de solicitud, bases reguladoras con enlace, sectores CNAE, regiones NUTS, instrumentos, tipos de beneficiario, finalidad, anuncios y documentos.
- **Cruce 2 — concesiones**: nº total de ayudas concedidas, importe sumado (muestra configurable) y fecha de la última concesión.
- **Cruce 3 — BOE (fuente externa)**: si las bases reguladoras son un documento del BOE, título oficial, rango normativo (Ley, Orden...), departamento, materias y PDF del BOE.
- **Derivados locales (sin peticiones)**: sección CNAE (A-U), comunidad autónoma (desde NUTS), etiquetas temáticas, estado del plazo y fecha de consulta.

### Reglas de diseño

- **El enriquecimiento es aditivo y solo en caso de éxito**: si un cruce no aporta nada (fallo de la fuente, bases fuera del BOE, sin concesiones), el item conserva SOLO los campos base — sin campos vacíos de relleno ni errores embebidos. Los fallos se cuentan en el KV `resumen` y van al log.
- **Nunca datos duplicados**: concesiones deduplicadas por `codConcesion`, sectores por código, y listas (regiones, materias, instrumentos...) únicas preservando orden. El importe sumado no se calcula dos veces.
- **El esquema base es duro**: si la fuente devuelve items fuera de esquema, el actor aborta y no guarda basura.
- **Checkpoint por página**: si la ejecución se interrumpe, la siguiente con `continuarDespues: true` y los MISMOS filtros reanuda desde la última página completa. El checkpoint se guarda después del guardado: en la ventana entre ambos, esa página puede repetirse entre ejecuciones (deduplicar por `codigoBDNS` al unir datasets).
- Rate limit: ~9 peticiones/segundo (máximo oficial 10/s), reintentos con backoff ante 429/5xx.
- Validación de entrada ANTES de llamar a la fuente: enums válidos, IDs de catálogo, coherencia de fechas y límites (`pageSize` ≤ 10.000). Error claro si algo no vale.

### Ejemplo de entrada

```json
{ "textoBusqueda": "digitalización", "fechaDesde": "2026-01-01", "maxItems": 500, "cruceDetalle": true, "cruceConcesiones": true, "cruceBOE": true }
```

Para pedir UNA convocatoria por su código: `{ "numeroConvocatoria": "939617" }` — devuelve esa convocatoria con TODOS los campos de cruce.

Otros filtros: `fechaHasta`, `tipoAdministracion` (C/A/L/O), `regiones`, `organos`, `instrumentos`, `tiposBeneficiario`, `finalidad`, `mrr`, `maxConcesionesMuestra` (0 = solo el total, sin muestreo), `continuarDespues`.

### Ejemplo de salida (real, 2026-11-04)

```json
{
  "id": 1141178,
  "codigoBDNS": "939617",
  "descripcion": "SUBVENCION NOMINATIVA AYUNTAMIENTO DE TEVERGA",
  "fechaRecepcion": "2026-11-03",
  "tipoAdministracion": "AUTONOMICA",
  "organoConvocante": "CONSEJERÍA ORDENACIÓN DE TERRITORIO, URBANISMO, VIVIENDA Y DERECHOS CIUDADANOS",
  "urlPdfOficial": "https://www.infosubvenciones.es/bdnstrans/api/convocatorias/pdf?id=1141178&vpd=GE",
  "tipoConvocatoria": "Concesión directa - instrumental",
  "presupuestoTotal": 49995.28,
  "fechaInicioSolicitud": "2026-01-01",
  "fechaFinSolicitud": "2026-10-29",
  "estadoPlazo": "cerrada",
  "descripcionFinalidad": "Acceso a la vivienda y fomento de la edificación",
  "urlBasesReguladoras": "https://www.boe.es/diario_boe/txt.php?id=BOE-A-2026-2888",
  "sectores": [{ "codigo": "68.3", "descripcion": "Actividades inmobiliarias por cuenta de terceros" }],
  "regiones": ["ES120 - Asturias"],
  "concesionesTotal": 0,
  "boeId": "BOE-A-2026-2888",
  "boeTitulo": "Ley del Principado de Asturias 5/2025, de 31 de diciembre, de Presupuestos Generales para 2026.",
  "boeRango": "Ley",
  "boeMaterias": ["Asturias", "Presupuestos de las Comunidades Autónomas", "..."],
  "sectorSeccion": ["L"],
  "comunidadAutonoma": ["Principado de Asturias"],
  "etiquetas": ["vivienda"],
  "enriquecido": true,
  "fechaConsulta": "2026-11-04"
}
```

### Integración por API (convenciones Apify)

Igual que cualquier actor de la Store. Para un resultado pequeño y síncrono (respuesta = items del dataset directamente):

```bash
curl -X POST "https://api.apify.com/v2/acts/~bdns-subsidios/run-sync-get-dataset-items?clean=1" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"numeroConvocatoria": "939617"}'
```

- `run-sync-get-dataset-items` corta a los 300s: úsalo con `maxItems` ≤ ~250 (con los dos cruces activos). Para más volumen, `POST /v2/acts/~bdns-subsidios/runs` y luego `GET /v2/datasets/{id}/items` (admite `clean=1`, `format=csv|json|xlsx`, paginación).
- Nombres de input en camelCase estándar de Apify (`maxItems`, `pageSize`, `proxyConfiguration` reservado para futuras necesidades).
- Campos de fecha en ISO (`AAAA-MM-DD`); importes como número decimal; listas sin duplicados.

### RGPD

Por defecto el cruce de concesiones **no incluye beneficiarios**: el campo `beneficiario` de la fuente mezcla NIF y nombre de personas físicas. Con `incluirBeneficiarios: true` se copian tal cual y su reutilización es responsabilidad del usuario (RGPD y RD 130/2019).

### Límites conocidos

- Con los cruces activos, cada convocatoria cuesta 1-3 peticiones: 1.000 convocatorias tardan ~4 minutos a 9 peticiones/segundo.
- El cruce BOE solo aplica cuando las bases reguladoras son un documento del BOE (no cuenta como fallo).
- `concesionesTotal` es siempre exacto; `concesionesImporteSuma` se calcula sobre la muestra (`maxConcesionesMuestra`, 200 por defecto, 0 = solo total).
- Datos oficiales dinámicos: sin garantía de integridad de los datos (aviso legal de la fuente).
- Última prueba de la fuente: 2026-11-04 (spec OpenAPI oficial + 45 tests + ejecuciones reales: búsqueda, por ID y con los 3 cruces, 0 fallos).

### Pasos futuros (roadmap)

1. **Contexto socioeconómico por región (Eurostat)** — verificado que la API responde a nivel NUTS: añadir paro/PIB por `comunidadAutonoma` cacheado por región (0-1 peticiones extra por región, no por convocatoria).
2. **Catálogos precargados** — descargar una vez `/regiones`, `/organos`, `/finalidades` para traducir IDs ↔ nombres en los filtros y devolver además el nombre legible del órgano.
3. **Modo incremental** — filtro automático `fechaDesde = última ejecución` para ejecuciones diarias programadas sin duplicar items.
4. **Exportaciones** — añadir `exportar` oficial de la BDNS (CSV/JSON masivo) como alternativa de carga masiva cuando `maxItems` sea muy grande.
5. **DIR3** — enriquecer el órgano convocante con su ficha del directorio común (pendiente de verificar la API pública).
6. **Precio PPR y publicación** — build en Apify, cálculo de coste real y precio por 1.000 items.

### Historial de versiones

- **1.0.0 (2026-11-04)** — Primera versión estable, desplegada en Apify (build SUCCEEDED + 2 runs de verificación SUCCEEDED: items con 45 campos, BOE 2/2, 0 fallos). 3 cruces (detalle, concesiones, BOE externo) tolerantes y solo en caso de éxito; limpieza y formateo de todos los campos de cruce; anti-duplicados garantizados; validación de filtros en el arranque; canarios de los 4 endpoints; checkpoint por página; Dockerfile corregido (COPY antes de pip). 45 tests verdes.
- **0.4.0** — Pre-lanzamiento: limpieza de cruces, validación de filtros, canarios de endpoints.
- **0.3.0** — Cruce 3 (BOE datos abiertos) + derivaciones locales (sección CNAE, comunidad autónoma).
- **0.2.0** — Implementación completa del actor: cliente con rate limit, cruces detalle/concesiones, checkpoint, validación dura.
- **0.1.0** — Esqueleto inicial (llamada a la API pendiente de fijar; resuelta el 2026-11-04).

### Publicar en la Store (pendiente, consola)

1. `console.apify.com` → *Actors* → `bdns-subsidios` → pestaña *Publishing*.
2. Subir logo/miniatura (no va en el repo), revisar descripción y README.
3. Categoría: `GOVERNMENT_AND_PUBLIC_DATA` (verificada como válida).
4. Precio PPR: calcular con una ejecución real (ver `_releases/ultima-ejecucion-1.0.json`) y margen 3x tras comisión del 20% — requiere OK del dueño.
5. Submit para revisión de Apify.

### Precio

Pago por resultado (PPR): ver pestaña de precios. La fuente es gratuita; pagas por la extracción, enriquecimiento y estructura lista para usar.

### Casos de uso reales

- **Consultoras de subvenciones**: extraccion diaria programada de convocatorias nuevas + alertas a clientes (receta n8n abajo).
- **Ayuntamientos y entidades**: comparar plazos y presupuestos de convocatorias similares antes de la propia.
- **Empresas y VOs**: monitorizar convocatorias de su sector (por CNAE) y las concesiones de competidores.
- **Desarrolladores**: el dataset limpio (45 campos, fechas ISO, importes numericos) listo para integrar por API o MCP.

### Receta: alertas diarias sin codigo (n8n / Make)

1. Schedule de Apify (ya activada por defecto a las 06:00 UTC) o node HTTP de n8n -> `POST /v2/acts/daniizarsan~bdns-subsidios/runs` con `{"fechaDesde": "ayer", "maxItems": 100}`.
2. Node "Get dataset items" -> filtra por `estadoPlazo == "abierta"`.
3. Envio por email/Slack/Google Sheets con el enlace `urlPdfOficial`.

La ejecucion diaria automatica mantiene los datos al dia sin tocar nada.

### Familia

Este actor forma parte de "España — datos oficiales": **BORME España** (`daniizarsan/borme-espana`, feed diario del Registro Mercantil) + este. Mismo formato de dataset y reglas de calidad.

***

### Familia "Espana — datos oficiales"

- **Catastro Espana** (`daniizarsan/catastro-espana`): cualquier inmueble por referencia catastral
  o direccion — uso, superficie, coeficiente, ano, construcciones por planta.
- **BORME Espana** (`daniizarsan/borme-espana`): nuevas sociedades, disoluciones y cargos del Registro Mercantil.
- **Subastas España** (`daniizarsan/subastas-espana`): subastas del BOE (inmuebles con valores y cargas, vehículos con matrícula y bastidor) — combina con Catastro para evaluar el bien subastado.

# Actor input Schema

## `textoBusqueda` (type: `string`):

Palabra clave en el título de la convocatoria, p. ej. "digitalización". Vacío = todas.

## `modoTexto` (type: `string`):

Cómo interpretar el texto buscado.

## `numeroConvocatoria` (type: `string`):

Si lo pones (p. ej. 939617), extrae solo esa convocatoria.

## `fechaDesde` (type: `string`):

Fecha de recepción en la BDNS. Formato AAAA-MM-DD. Vacío = sin filtro.

## `fechaHasta` (type: `string`):

Formato AAAA-MM-DD. Vacío = sin filtro.

## `tipoAdministracion` (type: `string`):

Vacío = todas.

## `regiones` (type: `string`):

IDs del catálogo de regiones de la BDNS (endpoint /regiones). Vacío = todas.

## `organos` (type: `string`):

IDs del catálogo de órganos (endpoint /organos). Vacío = todos.

## `instrumentos` (type: `string`):

IDs del catálogo de instrumentos (endpoint /instrumentos). Vacío = todos.

## `tiposBeneficiario` (type: `string`):

IDs del catálogo de tipos de beneficiario (endpoint /beneficiarios). Vacío = todos.

## `finalidad` (type: `integer`):

ID del catálogo de finalidades (endpoint /finalidades). Vacío = todas.

## `mrr` (type: `boolean`):

Marca para limitar a convocatorias del Mecanismo de Recuperación y Resiliencia.

## `pageSize` (type: `integer`):

La API admite hasta 10.000 registros por petición.

## `maxItems` (type: `integer`):

Cada convocatoria con cruces cuesta 1-3 peticiones extra (límite fuente: 10/seg).

## `cruceDetalle` (type: `boolean`):

Añade tipo, presupuesto, plazos de solicitud, bases reguladoras y URL. Si falla, el item sale sin esos campos y no aborta.

## `cruceConcesiones` (type: `boolean`):

Añade nº de concesiones, importe sumado muestreado y fecha de la última. Si falla, el item sale sin esos campos y no aborta.

## `maxConcesionesMuestra` (type: `integer`):

El nº total de concesiones (concesionesTotal) siempre es exacto; el importe sumado se calcula sobre esta muestra. 0 = solo el total, sin muestreo (1 petición por convocatoria).

## `incluirBeneficiarios` (type: `boolean`):

ATENCIÓN: el campo beneficiario de la fuente puede contener NIF y nombre de personas físicas. Activado, se copia tal cual; su reutilización es responsabilidad del usuario (RGPD y RD 130/2019).

## `cruceBOE` (type: `boolean`):

Si las bases reguladoras son un documento del BOE, cruza con sus datos abiertos: título oficial, rango normativo, departamento y materias. Fuente externa gratuita; si falla, no aborta.

## `continuarDespues` (type: `boolean`):

Guarda un checkpoint por página: si la ejecución se cae, la siguiente reanuda desde la última página completa (con los mismos filtros).

## Actor input object example

```json
{
  "modoTexto": "1",
  "mrr": false,
  "pageSize": 500,
  "maxItems": 500,
  "cruceDetalle": true,
  "cruceConcesiones": true,
  "maxConcesionesMuestra": 200,
  "incluirBeneficiarios": false,
  "cruceBOE": true,
  "continuarDespues": true
}
```

# Actor output Schema

## `resultados` (type: `string`):

Dataset de convocatorias: id, codigoBDNS, descripcion, fechas (AAAA-MM-DD), administracion, organoConvocante, urlPdfOficial, y campos de cruce mejor esfuerzo (presupuestoTotal, fechaFinSolicitud, urlBasesReguladoras, concesionesTotal, enriquecido).

## `resumen` (type: `string`):

Contadores de la ejecución: items extraídos, ratios de enriquecimiento y fallos de cruces.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("daniizarsan/bdns-subsidios").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("daniizarsan/bdns-subsidios").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call daniizarsan/bdns-subsidios --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,daniizarsan/bdns-subsidios"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Is6xGcWOGqS4XwZIU/builds/Wy5Ow8L83ifiGU682/openapi.json
