📡 API REST PÚBLICA · DuckDB · 32M filas

API REST sobre el
presupuesto del Perú.

Un endpoint HTTP que acepta SQL DuckDB y devuelve JSON. Acceso libre a la tabla mef_historico con 137 columnas y 32M de filas (años 2013-2026). Sin auth, sin API keys, sin SDKs: cualquier herramienta que hable HTTP puede consumirla.

POST: https://app.gestionpublicaperu.com.pe/api/insights/query

Endpoints disponibles

Cuatro rutas, todas bajo https://app.gestionpublicaperu.com.pe. Sin headers especiales — solo Content-Type: application/json para los POST.

GET /api/insights/schema

Metadata: 137 columnas + tipos, año min/max, total de filas y una query de ejemplo.

Body:
POST /api/insights/query

Ejecuta una query DuckDB sobre mef_historico. Solo SELECT/WITH. Cap 10k filas, timeout 30s.

Body: { "sql": "SELECT ...", "limit": 1000 }
GET /api/insights/slices/

Lista los JSON precomputados (kpis, evolucion, sectores, categoria, departamentos, pliegos, meta).

Body:
GET /api/insights/slices/{nombre}.json

Sirve un slice JSON específico. Mismo dato que sirve el CDN público, útil para clientes que ya hablan con este origin.

Body:
GET /api/insights/dimensions

Valores distintos de niveles, fuentes, funciones, departamentos, sectores y categorías de gasto. Ideal para poblar dropdowns. Cacheado 5 min.

Body:
GET /api/insights/serie?sector=11

Serie anual PIM/Devengado/Avance% con filtros simples (sector, pliego, departamento, nivel, fuente, funcion). Sin SQL — solo query params.

Body:
GET /api/insights/top-sectores?anio=2025

Top sectores por PIM en un año dado. Default = último año cerrado. Devuelve hasta 33 filas con código, nombre, PIM, devengado y avance %.

Body:

Cómo usarla

Cuatro variantes para 4 audiencias. Elegí la que te sirva.

🐚 curl (bash · terminal)

curl -X POST https://app.gestionpublicaperu.com.pe/api/insights/query \
  -H "Content-Type: application/json" \
  -d '{
    "sql": "SELECT anio, ROUND(SUM(MTO_PIM)/1e9, 1) AS pim_mil_M FROM mef_historico WHERE SECTOR LIKE \"11%\" GROUP BY anio ORDER BY anio"
  }'

🐍 Python (httpx · ideal para notebooks y scripts)

import httpx

r = httpx.post(
    "https://app.gestionpublicaperu.com.pe/api/insights/query",
    json={
        "sql": """
            SELECT anio,
                   ROUND(SUM(MTO_PIM)/1e9, 1)            AS pim_mil_M,
                   ROUND(SUM(DEVENGADO_)*100
                       / NULLIF(SUM(MTO_PIM),0), 1)      AS avance_pct
            FROM mef_historico
            WHERE PLIEGO LIKE '036%'   -- MTC
            GROUP BY anio
            ORDER BY anio
        """,
    },
    timeout=60,
)
data = r.json()
print(data["columnas"])   # ['anio', 'pim_mil_M', 'avance_pct']
print(data["filas"])      # [[2013, 4.2, 87.5], [2014, 5.1, 89.1], ...]

🌐 JavaScript (fetch · browser o Node)

const r = await fetch("https://app.gestionpublicaperu.com.pe/api/insights/query", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    sql: `
      SELECT DEPARTAMENTO_META,
             ROUND(SUM(MTO_PIM)/1e6, 1) AS pim_M
      FROM mef_historico
      WHERE anio = 2025 AND DEPARTAMENTO_META != '00 '
      GROUP BY DEPARTAMENTO_META
      ORDER BY pim_M DESC
      LIMIT 5
    `,
  }),
});
const { columnas, filas, n_filas } = await r.json();
console.table(filas);

📊 Excel · Power Query M

// Excel → Datos → Obtener datos → De otras fuentes → Consulta vacía → Editor avanzado
let
    url     = "https://app.gestionpublicaperu.com.pe/api/insights/query",
    sql     = "SELECT anio, ROUND(SUM(MTO_PIM)/1e9, 1) AS pim FROM mef_historico GROUP BY anio ORDER BY anio",
    body    = Json.FromValue([sql = sql]),
    resp    = Web.Contents(url, [
                Content = body,
                Headers = [#"Content-Type" = "application/json"]
              ]),
    json    = Json.Document(resp),
    tabla   = Table.FromRows(json[filas], json[columnas])
in
    tabla

En Excel: Datos → Obtener datos → De otras fuentes → Consulta vacía → Editor avanzado. Pegá el bloque y refrescá cuando quieras datos nuevos.

Casos de uso reales

Cuatro preguntas frecuentes, con la SQL que las contesta y un preview de la respuesta.

¿Cuánto creció el sector Educación entre 2013 y 2025?

#1

SQL:

SELECT anio,
       ROUND(SUM(MTO_PIM)/1e9, 1)      AS pim_mil_M,
       ROUND(SUM(DEVENGADO_)/1e9, 1)   AS dev_mil_M
FROM mef_historico
WHERE SECTOR LIKE '10%'   -- 10: EDUCACION
GROUP BY anio
ORDER BY anio

Output:

anio  pim_mil_M  dev_mil_M
2013  18.8       17.4
2025  41.1       36.8
→ +118% en 12 años

Top 10 pliegos por PIM en 2025

#2

SQL:

SELECT PLIEGO,
       ROUND(SUM(MTO_PIM)/1e9, 2) AS pim_mil_M
FROM mef_historico
WHERE anio = 2025
GROUP BY PLIEGO
ORDER BY pim_mil_M DESC
LIMIT 10

Output:

PLIEGO                                  pim_mil_M
006. INSTITUTO PERUANO DE SEGURIDAD ...  14.92
036. M. DE TRANSPORTES Y COMUNICACIO... 12.40
010. M. DE EDUCACION                     9.87
011. M. DE SALUD                         8.54
...

Avance de ejecución por nivel de gobierno (2025)

#3

SQL:

SELECT NIVEL_GOBIERNO,
       ROUND(SUM(MTO_PIM)/1e9, 1)            AS pim_mil_M,
       ROUND(SUM(DEVENGADO_)*100
           / NULLIF(SUM(MTO_PIM),0), 1)      AS avance_pct
FROM mef_historico
WHERE anio = 2025
GROUP BY NIVEL_GOBIERNO
ORDER BY pim_mil_M DESC

Output:

NIVEL_GOBIERNO          pim_mil_M  avance_pct
E. GOBIERNO NACIONAL    180.4      78.3
R. GOBIERNOS REGION...   52.1      82.5
M. GOBIERNOS LOCALES     40.0      71.2

Sin SQL: serie anual de Salud con un GET

#4

SQL:

curl "https://app.gestionpublicaperu.com.pe/api/insights/serie?sector=11"
# o también:
#   /api/insights/serie?pliego=036         (MTC)
#   /api/insights/serie?departamento=15    (Lima)
#   /api/insights/serie?funcion=20.%20SALUD
#   /api/insights/serie?nivel=R.%20GOBIERNOS%20REGIONALES

Output:

{
  "filtros": { "sector": "11", ... },
  "serie": [
    {"anio": 2013, "pim_mil_M": 8.04, "dev_mil_M": 7.31, "avance_pct": 90.9, ...},
    {"anio": 2025, "pim_mil_M": 14.52, "dev_mil_M": 14.15, "avance_pct": 97.4, ...}
  ]
}
→ Ideal cuando no querés escribir SQL.

Devengado mensual de Salud en Lima (2025)

#5

SQL:

SELECT MTO_DEVENGA_01 AS ene, MTO_DEVENGA_02 AS feb,
       MTO_DEVENGA_03 AS mar, MTO_DEVENGA_04 AS abr
FROM mef_historico
WHERE anio = 2025
  AND SECTOR LIKE '11%'         -- Salud
  AND DEPARTAMENTO_META LIKE '15.%'  -- Lima

Output:

Cada fila es una partida; ideal para
GROUP BY + SUM si querés el agregado.
Devuelve hasta 10.000 filas, agregá
WHERE más fino si tu universo es grande.

Límites y reglas

Pensado para análisis ad-hoc, no para producción de aplicaciones críticas. Por ahora sin auth ni rate limit, pero con caps de seguridad razonables.

🛡️

Solo SELECT / WITH

Cualquier intento de INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, COPY, PRAGMA, ATTACH y compañía devuelve HTTP 400. La base es de solo lectura.

📦

Cap 10.000 filas

El backend wrappea tu query con LIMIT 10000. Si necesitás más, agregá un GROUP BY o paginá con WHERE anio = ....

⏱️

Timeout 30 segundos

Si la query tarda más, devuelve HTTP 504. La mayoría de queries con GROUP BY tardan menos de 1s, así que esto solo aparece con full-scans sin filtros.

🔓

Sin auth, sin keys

Endpoint público. No hay rate limit visible. Si abusás y tirás el server, avísanos primero — sería triste tener que poner barreras.

Schema rápido

Las 15 columnas más usadas. Para la lista completa de 137 columnas y sus tipos, consultá /api/insights/schema .

Columna Tipo Descripción
anio INT Año fiscal (2013-2026). Para el año en curso ver es_parcial.
es_parcial BOOL true si es snapshot del año en curso (datos vivos). false si es año cerrado.
SECTOR VARCHAR Formato "NN: NOMBRE" con dos puntos. Filtrá con LIKE 'NN%', nunca con =.
PLIEGO VARCHAR Formato "NNN. NOMBRE" (con punto). Ej "036. M. DE TRANSPORTES...".
SEC_EJEC VARCHAR Código Unidad Ejecutora — sin ceros a la izquierda (ej "154", no "000154").
NIVEL_GOBIERNO VARCHAR E. GOBIERNO NACIONAL · R. GOBIERNOS REGIONALES · M. GOBIERNOS LOCALES.
DEPARTAMENTO_META VARCHAR Formato "NN. NOMBRE" (con punto). Ej "15. LIMA". Filtrá "00 " (vacío) si querés solo geo válida.
FUENTE_FINANC VARCHAR Fuente de financiamiento (1=Recursos Ordinarios, 2=Recaudados, etc.).
FUNCION VARCHAR Función presupuestal (ej "22. EDUCACION", "20. SALUD").
CATEGORIA_GASTO VARCHAR 5 GASTO CORRIENTE · 6 GASTO DE CAPITAL · 7 SERVICIO DE DEUDA.
GENERICA VARCHAR Genérica del clasificador (ej "21" = Personal). Sin puntos al filtrar.
MTO_PIA DOUBLE Presupuesto Inicial de Apertura (S/).
MTO_PIM DOUBLE Presupuesto Institucional Modificado (S/) — techo vigente.
DEVENGADO_ DOUBLE Devengado acumulado del año (S/).
MTO_DEVENGA_01..12 DOUBLE 12 columnas — devengado mensual por mes calendario.

💡 Pista: SECTOR usa formato "NN: NOMBRE" (dos puntos) mientras que PLIEGO y DEPARTAMENTO_META usan "NN. NOMBRE" (punto). Siempre filtrá con LIKE 'NN%' para evitar errores por formato.

📖 OpenAPI interactivo

Explorá cada endpoint con un Swagger UI live, o descargate la spec JSON para llevarla a Postman, Insomnia, tu generador de código o tu LLM favorito.

¿Construyendo algo con esto?

Nos encantaría verlo. Escribinos a soporte@gestionpublicaperu.com.pe .

🌐 Productos relacionados: Dashboard /insights · API REST /api · MCP server /mcp · Status /status