# Subvenciones y Ayudas Públicas España (BDNS) (`bluewither/my-actor-1`) Actor

Extrae convocatorias de subvenciones y ayudas públicas de la Base de Datos Nacional de Subvenciones (BDNS) española: órgano, importe, sector (CNAE), región, finalidad y plazos. Fuente: API oficial abierta de la IGAE, sin proxy ni riesgo de bloqueo.

- **URL**: https://apify.com/bluewither/my-actor-1.md
- **Developed by:** [Bluewither](https://apify.com/bluewither) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## bdns-subvenciones

Actor de Apify que lee la API oficial y abierta de la Base de Datos Nacional
de Subvenciones (BDNS) española y devuelve, por cada convocatoria: título,
órgano convocante, importe, sector (código CNAE), región, finalidad, plazos
y enlace a la ficha pública.

Segundo actor construido con la misma plantilla que
[`placsp-licitaciones`](../placsp-licitaciones): la separación
`bdnsClient.js` (red + paginación) / `parser.js` (normalización) /
`main.js` (orquestación + Apify SDK) es el mismo patrón, reutilizado.

### Por qué este dataset

- **API REST pública, JSON, sin autenticación** — mantenida por la IGAE
  (Intervención General de la Administración del Estado). Sin anti-bot, sin
  captchas, sin necesidad de proxy.
- **Sin datos personales**: las convocatorias son de organismos públicos y
  personas jurídicas, no de personas físicas beneficiarias.
- **Sin competencia detectada en Apify Store** en el momento de construirlo.
- **Mismo comprador que el actor 1** (PLACSP): consultoras, gestorías,
  constructoras e ingenierías que ya monitorizan licitaciones también
  monitorizan subvenciones — venta cruzada natural a la misma cartera de
  clientes potenciales.
- **Filtro por sector real**: el campo `sectores[].codigo` de la API es un
  código tipo CNAE, así que el input soporta `cnaePrefijos` igual que
  `placsp-licitaciones` soporta `cpvPrefijos`.

### Estado actual (2026-08-11)

- ✅ API investigada y verificada con peticiones reales (no solo
  documentación): endpoints de listado (`/convocatorias/busqueda`) y detalle
  (`/convocatorias?numConv=X&vpd=GE`) confirmados, con ejemplo real de
  respuesta capturado (ver `test/sample-detalle.json`, basado en la
  convocatoria BDNS 924629, Principado de Asturias).
- ✅ Patrón de URL pública para el campo `enlace` verificado navegando a la
  ficha real: `https://www.infosubvenciones.es/bdnstrans/GE/es/convocatorias/{codigoBDNS}`.
- ✅ Cliente, parser y orquestación implementados y probados (`npm test`,
  6/6 OK) contra fixtures con datos reales capturados, incluyendo un caso
  con campos opcionales ausentes.
- ⏳ **Pendiente de desplegar y probar con runs reales en Apify** (mismo
  motivo que en el actor 1: este entorno de desarrollo en la nube no tiene
  salida de red a `infosubvenciones.es`, solo a un puñado de dominios
  permitidos — `node src/main.js` aquí falla con `HTTP 403`, que es el
  bloqueo del sandbox, no necesariamente de BDNS. Hay que confirmarlo en
  Apify real, igual que se hizo con PLACSP).
- ⏳ Pendiente: ficha de tienda (`STORE_LISTING.md`) y precio — a fijar con
  datos reales de volumen del primer run, como se hizo con PLACSP.

### Estructura del proyecto

```
.actor/actor.json        Metadatos del actor para Apify
.actor/input_schema.json Formulario de input (fecha, filtro CNAE, límites)
Dockerfile                Imagen del actor (apify/actor-node:20)
src/config.js              Constantes de la API BDNS (URL base, cabeceras, concurrencia)
src/bdnsClient.js           Listado (con paginación) + detalle de convocatorias
src/parser.js                Normalización del JSON crudo -> esquema de salida
src/main.js                   Orquestación: input -> listado del día -> detalle -> dataset + monitor
test/parser.test.js           Tests con fixtures de datos reales capturados (npm test)
test/sample-detalle.json      Fixture: convocatoria real (BDNS 924629)
test/sample-detalle-minimo.json  Fixture: caso con campos opcionales ausentes
```

### Campos que devuelve cada convocatoria

`codigoBDNS`, `titulo`, `organo` (`ambito`, `entidad`, `departamento`),
`importe.presupuestoTotal`, `instrumentos` (tipo de ayuda), `tiposBeneficiarios`,
`sectores` (array de `{codigo, descripcion}`, tipo CNAE), `regiones` (array
de descripciones NUTS), `finalidad`, `basesReguladoras` (`titulo`, `url`),
`sePublicaDiarioOficial`, `abierto`, `plazo` (`fechaInicioSolicitud`,
`fechaFinSolicitud`), `mecanismoRecuperacionResiliencia` (fondos Next
Generation), `fechaRecepcion`, `enlace`.

### Cómo probarlo

```bash
npm install
npm test              # tests del parser contra datos reales capturados
node src/main.js      # ejecución completa (necesita salida a internet real)
```

`node src/main.js` en este entorno de desarrollo en la nube fallará con
`HTTP 403` — es la lista blanca de salida de red de este entorno, no
necesariamente un bloqueo real de BDNS (ver "Estado actual" arriba).
Pruébalo desde tu propio ordenador o, mejor, directamente en Apify.

### Input

Ver `.actor/input_schema.json`. Por defecto: fecha de hoy (Europe/Madrid),
sin filtro de sector CNAE, hasta 50 páginas de 100 resultados cada una.

### Riesgo conocido / a vigilar

- La API no documenta un rate limit numérico, solo advierte de que puede
  restringir el acceso ante "abuso manifiesto del servicio" (texto embebido
  en las propias respuestas). El cliente ya throttlea las peticiones de
  detalle (concurrencia limitada + pausa entre lotes, ver `src/config.js`) —
  vigilar si esto es suficiente con volumen real.
- El volumen diario real (~400 convocatorias/día a nivel nacional el
  2026-08-11, verificado) implica ~400 peticiones de detalle por run sin
  filtro — bastantes más peticiones por run que PLACSP. Repetir el mismo
  ejercicio que se hizo con PLACSP: correr sin filtro para ver el volumen y
  coste real, y con 1-2 `cnaePrefijos` para dimensionar el caso de uso por
  sector antes de fijar precio.

### Siguientes pasos (en orden)

1. Desplegar este actor en Apify (Web IDE, igual que PLACSP) y lanzar un run
   de prueba real sin filtro, para confirmar que no hay bloqueo y ver el
   volumen/coste real.
2. Repetir con 1-2 `cnaePrefijos` representativos (p. ej. `["62"]`
   programación informática, `["41"]` construcción) para calibrar el precio
   por sector.
3. Escribir `STORE_LISTING.md` con los datos reales de volumen (mismo
   patrón que PLACSP: no vender "toda España cada día" si el volumen
   nacional es demasiado alto para el precio por ítem).
4. Configurar monetización pay-per-event, publicar en la Store.
5. Reutilizar el mismo repo de monitorización (`placsp-licitaciones-monitor`)
   o crear uno nuevo análogo para este actor.

# Actor input Schema

## `fecha` (type: `string`):

Día (zona horaria Europe/Madrid) del que se quieren extraer las convocatorias publicadas/actualizadas, en formato AAAA-MM-DD. Si se deja vacío, se usa el día de hoy.

## `cnaePrefijos` (type: `array`):

Lista de prefijos de código de sector económico tipo CNAE (p. ej. "62" para programación informática, "41" para construcción de edificios). Si se deja vacío, se devuelven todas las convocatorias del día.

## `limiteConvocatorias` (type: `integer`):

Límite de seguridad sobre cuántas convocatorias del día se piden en detalle (0 = sin límite). Útil para pruebas rápidas o para acotar el coste en días de mucho volumen.

## `maxPaginas` (type: `integer`):

Límite de seguridad para no recorrer un histórico enorme si algo falla al filtrar por fecha. Cada página trae hasta "pageSize" convocatorias.

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

Cuántas convocatorias pide por página al listado de búsqueda. No cambiar salvo que se detecten problemas de rendimiento.

## Actor input object example

```json
{
  "cnaePrefijos": [],
  "limiteConvocatorias": 0,
  "maxPaginas": 50,
  "pageSize": 100
}
```

# 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 = {
    "fecha": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("bluewither/my-actor-1").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 = { "fecha": "" }

# Run the Actor and wait for it to finish
run = client.actor("bluewither/my-actor-1").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 '{
  "fecha": ""
}' |
apify call bluewither/my-actor-1 --silent --output-dataset

```

## MCP server setup

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

```

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/a3EvWyonZQqGPHXIN/builds/Hbe8WDjDPBFtT8gjE/openapi.json
