API pública v1
La API pública de Futuros es dato estático versionado. Futuros es una plataforma solo-frontend: no hay servidor de consultas detrás de /api/v1/. Todo se hornea desde public/data/ con scripts/bake-api.ts y se sirve como archivos planos por el CDN. Una compuerta post-horneado (scripts/check-api.ts, que corre en el mismo paso de prebuild) verifica que el horneado coincide con el corpus — los conteos de citas cuadran, cada celda está cubierta, los volcados masivos parsean, generated_at es al menos tan nuevo como el archivo de datos más reciente — y hace fallar el build ante cualquier deriva, de modo que el árbol servido no puede quedar obsoleto. Cada endpoint de abajo es un simple HTTP GET de un archivo — sin auth, sin API key, sin límite de tasa, totalmente cacheable.
- URL base:
https://futuros.xyz/api/v1/ - Descubrimiento (catálogo DCAT):
https://futuros.xyz/data.json(en la raíz del sitio, no bajo/api/v1/) - Página de docs para humanos:
https://futuros.xyz/datos-abiertos
Como las respuestas son archivos estáticos, un endpoint es simplemente su ruta. Descárguelo con curl, un navegador, fetch(), pandas.read_csv, R, o cualquier cliente SDMX/DCAT.
Cómo se hornea el árbol
bake-api.ts y luego check-api.ts corren como los dos pasos finales de prebuild (después del typecheck, la suite de tests y las demás compuertas de datos), de modo que cada deploy re-hornea /api/v1 desde el public/data/ actual y prueba el resultado antes de que vite build lo despache:
Invariantes del horneado (scripts/bake-api.ts):
- Promoción atómica. El árbol entero se escribe en
public/api/v1.tmp(máspublic/data.json.tmp) y se intercambia sobre las rutas vivas conrenameSynccomo paso final. Un crash a mitad de horneado deja intacto el árbol anterior — el viejo flujo de borrar-y-repoblar podía despachar un/api/v1a medio escribir. - Origen de las celdas. Una celda = un sobre
public/data/parameter-cache/<param>__<ISO3>.json; una celda cuyo payload no tiene indicadores se omite (la compuerta las cuenta como legítimamente ausentes, no como faltantes). - Aplanado de filas. Una fila CSV/SDMX por (indicador, año). Los años duplicados dentro de una serie se descartan (gana la primera aparición), y la fila del vintage más reciente se anexa solo cuando la serie no contiene ya ese año — un valor nunca se emite dos veces.
- Neutralización de inyección de fórmulas (OWASP). Cualquier celda de texto que empiece con
=+-@(o un tab/CR inicial) recibe el prefijo'antes de llegar al CSV o XLSX, de modo que Excel/Sheets/LibreOffice la rendericen como texto literal en vez de ejecutarla como fórmula. Los números pasan sin tocar. - Columnas solo-anexadas. Las columnas CSV nuevas se anexan, nunca se insertan (
label_en,label_ptvan después decitation_id), así los parsers posicionales siguen funcionando y los parsers por nombre de cabecera recogen las adiciones. - M49 va horneado. El mapa ISO3→M49 es una tabla fija de 26 entradas en el horneador (25 países +
LATAM=419), no una consulta en tiempo de ejecución.
Qué verifica check-api.ts (cualquier fallo imprime un diff y sale con 1):
| # | Aserción | Deriva que bloquea |
|---|---|---|
| 1 | index.json.counts.citations = longitud de public/data/citations.json | manifiesto desfasado del registro de citas |
| 2 | cada celda de caché no vacía tiene su observations/<param>__<ISO3>.json horneado | celdas descartadas en silencio |
| 3 | los CSV/SDMX/XLSX masivos existen, no están vacíos y parsean (CSV: cabecera + ≥1 fila; XLSX: abre) | volcados masivos corruptos |
| 4 | mtime del archivo index.json ≥ mtime más nuevo de public/data − 5 min de tolerancia | horneado obsoleto. Se compara mtime-vs-mtime porque los checkouts de git reescriben los mtimes de los archivos de datos — la vieja comparación con generated_at daba falsos fallos en checkouts frescos |
| 5 | filas de observaciones < 1.000.000 | desbordar el techo de hoja única de XLSX (1.048.576 filas) |
| 6 | cada referencia persona/caso del índice de búsqueda resuelve a un archivo en disco | hits de recuperación muertos (137 referencias de persona muertas se despacharon en julio de 2026) |
| 7 | etiquetas ES/EN/PT completas y con coincidencia exacta contra el registro de métricas, con un piso de cobertura de instancias ≥90% | que la API pública regrese a solo-español, o un join de etiquetas desincronizado en silencio |
Contrato de estabilidad
El manifiesto lo declara verbatim:
Shapes are additive-only within v1; breaking changes go to
/api/v2/.
En la práctica:
- Los campos pueden añadirse a cualquier objeto dentro de v1. No asuma un conjunto fijo de claves — lea por clave, ignore las desconocidas.
- Los nombres, tipos y significados de los campos existentes no cambiarán dentro de v1.
- Un cambio incompatible (renombrar/eliminar un campo, cambiar unidades o semántica) se despacha como un árbol
/api/v2/nuevo;/api/v1/sigue funcionando. index.jsontraeapi_version,generated_aty una cadenastability— leagenerated_atpara detectar un rebuild.
CORS y caché
Fijado en vercel.json para la ruta /api/v1/(.*):
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS
Cache-Control: public, max-age=3600, s-maxage=86400Así, la API de lectura es legible entre orígenes desde cualquier app de navegador (solo GET/OPTIONS — es de solo lectura), cacheada en el edge por un día. Aquí no hay superficie de escritura. Los endpoints dinámicos (/api/chat, /api/mcp, …) fijan sus propias cabeceras en código; de esos, solo /api/mcp es cross-origin (*, POST, OPTIONS) — el resto es de mismo origen.
Licencia y atribución
De index.json:
license_url:https://creativecommons.org/licenses/by/4.0/(CC BY 4.0)license(verbatim): "Datos redistribuidos de fuentes abiertas; cada observación conserva su fuente primaria (source_name,source_url,citation_id). Al reutilizar: cite la fuente primaria de cada observación y «Futuros» como agregador."
Es decir: los datos se redistribuyen desde fuentes abiertas; cada observación conserva su fuente primaria (nombre, URL profunda y citation_id). Al reutilizarlos, cite la fuente primaria de cada observación y acredite a Futuros como agregador. Nada se imputa ni se sintetiza — el contrato de honestidad del horneador es que cada valor en el cable lleva la fuente de la que proviene.
Estándares de interoperabilidad
index.json → standards declara los códigos que habla la API para que UNSD-SDG, World Bank Data360, OECD.AI y CEPALSTAT puedan ingerirla directamente:
| Estándar | Dónde |
|---|---|
REF_AREA = UN M49 numérico + ISO 3166-1 alfa-3 | cada geografía y observación |
| Marco global de indicadores ODS (A/RES/71/313) | bloque sdg por indicador + sdg-crosswalk.json |
| SDMX-CSV (ISO 17369) | observations.sdmx.csv |
| DCAT (project-open-data v1.1) | /data.json |
El manifiesto — index.json
Empiece aquí. Lista cada dataset, conteos en vivo, la licencia, la declaración de estabilidad y un mapa endpoints legible por máquina (plantillas de ruta para el resto).
curl -s https://futuros.xyz/api/v1/index.json{
"name": "Futuros — API de datos abiertos",
"api_version": "1",
"generated_at": "2026-08-29T04:17:28.143Z",
"docs": "/datos-abiertos",
"license": "Datos redistribuidos de fuentes abiertas; …",
"license_url": "https://creativecommons.org/licenses/by/4.0/",
"delta_basis": "latest value vs mean of the prior 3 years (smooths COVID-era volatility)",
"stability": "Shapes are additive-only within v1; breaking changes go to /api/v2/.",
"languages": ["es", "en", "pt"],
"label_fields": { "parameters": ["name_es", "name_en", "name_pt"],
"geographies": ["name_es", "name_en", "name_pt"],
"observations": ["label_es", "label_en", "label_pt"] },
"standards": {
"ref_area": "UN M49 numeric (REF_AREA) + ISO 3166-1 alpha-3",
"sdg": "UN SDG global indicator framework (A/RES/71/313); see /api/v1/sdg-crosswalk.json",
"sdmx": "SDMX-CSV (ISO 17369) at /api/v1/observations.sdmx.csv",
"catalog": "DCAT (project-open-data v1.1) at /data.json"
},
"counts": {
"parameters": 10, "geographies": 26, "cells": 260,
"observations": 397898, "sdg_tagged_indicator_instances": 2848,
"citations": 15098, "signal_files": 260, "governance_datasets": 3, "contributions": 0
},
"bulk": {
"observations_csv": { "path": "/api/v1/observations.csv", "bytes": 170533700, "rows": 397898 },
"observations_sdmx_csv": { "…": "…" }, "workbook_xlsx": { "…": "…" }
},
"endpoints": { "openapi": "/api/v1/openapi.json", "schemas": "/api/v1/schemas/",
"parameters": "/api/v1/parameters.json", "…": "…" }
}counts.geographies es 26 porque a los 25 países se suma el agregado regional LATAM (M49 419). Una celda es un parámetro × una geografía; una observación es una fila indicador-año.
El bloque bulk trae bytes + conteos de filas de cada volcado masivo — actualmente unos 171 MB (observations.csv), 111 MB (observations.sdmx.csv) y 91 MB (futuros-data.xlsx), ~398 mil filas cada uno — así que consúltelo antes de iniciar una descarga masiva. Estos tres volcados se generan en cada build (scripts/bake-api.ts, dentro de prebuild) y no se versionan en git: el observations.csv superó el límite duro de 100 MB por archivo de GitHub, de modo que el repositorio guarda los artefactos revisables (los archivos por celda de observations/, citations.json/.csv, index.json, freshness.json) y el sitio desplegado sirve los tres volcados igual que siempre. languages + label_fields (el reemplazo legible por máquina de la antigua nota en prosa language_coverage) declaran que cada parámetro y geografía lleva name_es/name_en/name_pt y cada observación label_es/label_en/label_pt. label_es es la etiqueta canónica del payload y nunca se renombra; sus hermanas EN/PT se unen desde public/data/metrics/registry.json por indicator_id y son null (JSON) / vacías (CSV) para los ids que el registro aún no cubre. Los conteos y tamaños de aquí son ilustrativos; el manifiesto en vivo es el autoritativo.
Contrato legible por máquina — openapi.json y schemas/
El propio contrato v1 se despacha como dato, listado primero en index.json → endpoints:
/api/v1/openapi.json— especificación OpenAPI 3.1.0 que cubre las 15 rutas de lectura núcleo (sdg-crosswalk.json,citations.csvy el catálogo DCAT/data.jsonaún no están en la spec)./api/v1/schemas/{manifest,parameter,geography,observation-cell,citation}.schema.json— JSON Schema de cada forma de respuesta.
Apunte una herramienta de codegen o validación a estos archivos en vez de transcribir formas a mano desde esta página.
Parámetros — parameters.json
Los 10 pilares de Futuros, en orden de presentación, cada uno con sus ODS primarios/secundarios.
curl -s https://futuros.xyz/api/v1/parameters.json[
{ "slug": "salud", "name_es": "Salud", "name_en": "Salud",
"display_order": 1, "sdg_primary": [3], "sdg_secondary": [2, 6] },
{ "slug": "educacion", "name_es": "Educación", "…": "…" }
]Use slug (p. ej. salud, educacion, cohesion-social-inclusion) como el segmento <param> en las rutas de observaciones.
Geografías — geographies.json
Los 25 países más la región LATAM, cada uno con ISO3 y M49.
curl -s https://futuros.xyz/api/v1/geographies.json[
{ "iso3": "LATAM", "m49": "419", "slug": "latam",
"name_es": "América Latina y el Caribe", "name_en": "Latin America and the Caribbean",
"level": "region", "sub_region": null, "population_2024": null },
{ "iso3": "ARG", "m49": "032", "slug": "arg", "level": "country", "…": "…" }
]Los slug de país son el ISO3 en minúsculas (arg, mex, bra; la región es latam). Use iso3 (p. ej. MEX, ARG, BRA) como el segmento <ISO> en las rutas de observaciones. Los códigos M49 son cadenas con ceros a la izquierda ("032", "484").
Observaciones — una celda
El dataset núcleo. Un archivo por parámetro × país, direccionado como observations/<param>__<ISO3>.json (note el separador de doble guion bajo).
JSON
curl -s https://futuros.xyz/api/v1/observations/salud__MEX.json{
"api_version": "1",
"generated_at": "2026-08-29T04:17:28.143Z",
"parameter": { "slug": "salud", "label_es": "Salud" },
"geography": { "iso3": "MEX", "m49": "484", "name_es": "México",
"name_en": "México", "level": "country" },
"confidence": "high",
"last_refreshed": "2026-06-15T16:20:52.364Z",
"indicators": [
{
"indicator_id": "sp_dyn_le00_in",
"label_es": "Esperanza de vida al nacer",
"label_en": "Life expectancy at birth",
"label_pt": "Expectativa de vida ao nascer",
"unit": "años",
"latest": { "year": 2024, "value": 75.264 },
"delta_pct": 3.199,
"delta_basis": "latest value vs mean of the prior 3 years (smooths COVID-era volatility)",
"good_direction": "up",
"coverage": "primary",
"benchmark": { "oecd": 80.39, "world": 73.48, "sea_peers": 73.87, "oecd_vintage": 2024 },
"sdg": { "code": "3", "level": "goal",
"indicator_name": "Ensure healthy lives and well-being …", "goal": 3 },
"source": {
"name": "World Bank Open Data",
"url": "https://data.worldbank.org/indicator/SP.DYN.LE00.IN?locations=MX",
"citation_id": "wb-sp-dyn-le00-in-mex-2024"
},
"series": [ { "year": 2000, "value": 72.562 }, { "year": 2001, "value": 72.912 }, "…" ]
}
]
}Cada indicador lleva latest (año + valor), la series completa hasta ~2000, su source (nombre + deep-link a la fuente primaria + citation_id), un código sdg oficial cuando existe (level es indicator, target o goal) y delta_pct (vea delta_basis: valor reciente vs. la media de los 3 años previos). sdg es null cuando no aplica ningún código ODS oficial — nunca se fabrica.
CSV
La misma celda aplanada a una fila por indicador-año:
curl -s https://futuros.xyz/api/v1/observations/salud__MEX.csvCabecera:
parameter,iso3,m49,indicator_id,label_es,unit,year,value,delta_pct_latest,
good_direction,coverage,vintage_year,sdg_code,sdg_level,source_name,source_url,citation_id,
label_en,label_ptlabel_en,label_pt van al final de la fila — el orden de columnas es parte del contrato CSV, así que las adiciones siempre se anexan, nunca se insertan.
Observaciones — masivo
Todas las celdas concatenadas en un solo CSV — el corpus completo en una sola descarga (~164 mil filas):
curl -s https://futuros.xyz/api/v1/observations.csv -o futuros-observations.csvMisma cabecera que el CSV por celda. Cárguelo directo:
import pandas as pd
df = pd.read_csv("https://futuros.xyz/api/v1/observations.csv")Observaciones — SDMX-CSV
El volcado masivo como SDMX-CSV (ISO 17369), con REF_AREA en UN M49 para alinearse con las convenciones de UNSD-SDG / Data360:
curl -s https://futuros.xyz/api/v1/observations.sdmx.csvDATAFLOW,FREQ,REF_AREA,INDICATOR,TIME_PERIOD,OBS_VALUE,UNIT_MEASURE,
SDG_INDICATOR,REF_AREA_ISO3,FUTUROS_PARAMETER,SOURCE,SOURCE_URL,CITATION_ID
FUTUROS:DF_OBSERVATIONS(1.0),A,032,si_pov_gini,2000,51,índice,10,ARG,
cohesion-social-inclusion,World Bank Open Data,https://data.worldbank.org/…,wb-si-pov-gini-arg-2024DATAFLOW es FUTUROS:DF_OBSERVATIONS(1.0), FREQ es A (anual).
Libro Excel — futuros-data.xlsx
Un libro con cinco hojas — README (licencia + base del delta_pct), parameters, geographies, observations (todas las filas) y citations:
curl -s https://futuros.xyz/api/v1/futuros-data.xlsx -o futuros-data.xlsxCitas — citations.json / citations.csv
El registro de fuentes completo contra el que resuelve cada citation_id.
curl -s https://futuros.xyz/api/v1/citations.json
curl -s https://futuros.xyz/api/v1/citations.csvColumnas CSV: id,source,title,url,year,retrieved_at. Una el citation_id de observations.* con el id de citations.* para adjuntar los metadatos completos de fuente a cualquier valor.
Libro de frescura — freshness.json
Cadencia de refresco por fuente — cuándo se extrajo cada indicador por última vez y cuándo le toca de nuevo. Un passthrough del refresh-meta.json del build. Claves de nivel superior: generated_at, total_indicators, indicators, más un bloque sources.
curl -s https://futuros.xyz/api/v1/freshness.json{
"generated_at": "2026-06-15T16:20:52.364Z",
"total_indicators": 48,
"indicators": {
"SI.POV.GINI": {
"source": "World Bank Open Data",
"source_url": "https://data.worldbank.org/indicator/SI.POV.GINI",
"cadence_months": 12,
"last_refreshed": "2026-06-15T16:20:52.364Z",
"next_refresh_due": "2027-08-15T20:11:16.364Z",
"countries_with_data": 0, "total_countries": 25
}
}
}Crosswalk ODS — sdg-crosswalk.json
El mapa verificado de pilares e indicadores a códigos ODS oficiales (goals, pillars, indicators). Una por indicator_id (join).
curl -s https://futuros.xyz/api/v1/sdg-crosswalk.jsonSeñales — signals/<param>__<ISO3>.json
Señales de «qué se está moviendo» por celda, extraídas por LLM y citadas (presentes porque la capa multi-fuente está horneada). Mismo direccionamiento <param>__<ISO3> que las observaciones, y desde el horneado del 2026-08-05 la cobertura es total: las 260 celdas tienen archivo (counts.signal_files en el manifiesto). Las 10 celdas del agregado regional (<param>__LATAM), que la cosecha de señales no escribe, se hornean como arreglo vacío []; antes no tenían archivo y respondían HTTP 404. Trate un [] como «esta celda no tiene señales», no como un error; un 404 bajo signals/ ahora sí indica una ruta mal escrita.
curl -s https://futuros.xyz/api/v1/signals/salud__ARG.jsonContribuciones — contributions.json
Datos aportados a través del Fideicomiso de Datos (Pilar 1), condicionados por licencia y publicados solo para posturas que permiten republicar valores. Actualmente count: 0.
curl -s https://futuros.xyz/api/v1/contributions.jsonMotor de Gobernanza — governance/*.json
Los datasets del Motor de Gobernanza (Pilar 3) forman parte del contrato versionado desde el horneado del 2026-08-05 (antes solo se accedían vía MCP o como archivo sin versionar). Cada uno es un passthrough del archivo que consume la UI, servido con el mismo contrato de estabilidad aditivo, CORS y caché que el resto de v1:
| Ruta | Qué es |
|---|---|
/api/v1/governance/resilience.json | Índice de Resiliencia Democrática: subíndices + compuesto, citado por país |
/api/v1/governance/regulation-index.json | Observatorio Regulatorio: instrumentos por país y pilar |
/api/v1/governance/scores.json | Índice compuesto goalpost por geografía (series 2010–2024) |
curl -s https://futuros.xyz/api/v1/governance/resilience.jsonLa vía MCP sigue disponible: get_dataset con dataset_id: "democracy-resilience" en el servidor MCP devuelve el mismo índice de resiliencia con cómputo determinista. El archivo sin versionar GET /data/democracy/resilience.json también sigue existiendo, pero sin contrato de estabilidad; prefiera las rutas /api/v1/governance/ de arriba.
Descubrimiento — catálogo DCAT en /data.json
Un catálogo DCAT / project-open-data v1.1 en la raíz del sitio (no bajo /api/v1/) para que portales de datos y crawlers descubran los datasets automáticamente:
curl -s https://futuros.xyz/data.jsonAnuncia los datasets de CSV masivo, SDMX-CSV, XLSX, citas, crosswalk ODS y frescura, cada uno con downloadURL, mediaType y licencia CC BY 4.0.
Feeds RSS · /feeds/
Las señales citadas también se publican como 267 feeds RSS 2.0 estáticos bajo /feeds/ (en la raíz del sitio, no bajo /api/v1/), pensados para lectores RSS y monitoreo institucional. Mismo régimen que la API de lectura: GET estático, sin auth, cacheable.
| Ruta | Qué es |
|---|---|
/feeds/futuros.xml | Maestro regional: todas las señales de la región en un solo feed |
/feeds/<ISO3>.xml | Por país: unión de sus 10 feeds de pilar, máximo 50 ítems |
/feeds/<param>__<ISO3>.xml | Por celda (pilar × país): señales citadas con deep-link a la fuente |
/feeds/index.json | Manifiesto: total_feeds, cells_written, cells_skipped_empty, countries_written, forecasts_included, forecasts_skipped_unverifiable, y feeds[] con título, ruta, tipo y conteo de ítems |
curl -s https://futuros.xyz/feeds/salud__BRA.xmlLos feeds ya no están congelados: scripts/bake-feeds.ts corre dentro de la cadena de prebuild (junto a bake-data-health, bake-source-ledger y bake-api, justo antes de check-api), de modo que se re-hornean en cada deploy desde el corpus de señales vigente. El manifiesto es explícito sobre lo que no publicó: cells_skipped_empty cuenta las celdas sin señal — no se emite un feed vacío para simular cobertura — y forecasts_skipped_unverifiable cuenta los pronósticos que se dejaron fuera por no ser verificables.
Backlog de vacíos de datos — GET /api/backlog
A diferencia de todo lo anterior, este no es un archivo horneado bajo /api/v1/: es una función en vivo (api/backlog.ts), porque lo que sirve se genera en tiempo de petición y no en el horneado. Se documenta aquí porque es público, sin autenticar y de sólo lectura, igual que el resto de esta página.
Cada vez que el asistente consulta el corpus y una herramienta del corpus vuelve vacía, el servidor abre un ticket estructurado en vez de dejar morir la señal. Este endpoint es cómo ese backlog se vuelve visible — para la SPA (/vacios), para la línea de ingesta que decide qué buscar a continuación, y para cualquiera que quiera comprobar que un "no tenemos ese dato" va seguido de algo.
Parámetros
| Parámetro | Por defecto | Qué hace |
|---|---|---|
limit | 100 (máx. 500) | Cuántos tickets devolver |
country | — | Filtra por ISO3 (se normaliza a mayúsculas) |
pillar | — | Filtra por slug de eje |
Límite de tasa: 60 peticiones por minuto y por IP; al excederlo responde 429 con Retry-After. Caché: s-maxage=60, stale-while-revalidate=300.
Respuesta
{
"ok": true,
"configured": true,
"count": 2,
"gaps": [
{
"id": "gap-ecu-medio-ambiente-clima-1a2b3c4d",
"created_at": "2026-08-16T09:12:44.031Z",
"lang": "es",
"country": "ECU",
"pillar": "medio-ambiente-clima",
"empty": ["get_dataset:regulation", "get_dataset:signals"],
"candidate_sources": [
{ "title": "SIMAS", "url": "https://ambiente.gob.ec/simas", "source": "ambiente.gob.ec", "date": "2026" }
],
"missing_indicators": ["Superficie con concesión minera"],
"hits": 7
}
]
}configured: falsesignifica que el almacén durable (Upstash) no está cableado en ese despliegue:gapses entonces el anillo en memoria de esa instancia, ni compartido ni persistente. Se declara en vez de fingir que existe un backlog común.hitses cuántas veces se ha levantado ese mismo hueco. El id deduplica por la forma del vacío (país + eje + herramientas vacías), no por el texto, así que la misma laguna preguntada de dos maneras incrementa un solo ticket. Ese orden por demanda es la cola de ingesta.candidate_sourcessólo contiene URLs que una herramienta externa devolvió en ese mismo turno. Nunca una fuente que el modelo nombró de memoria.
Sin texto del usuario, por diseño
Un ticket describe el hueco, no a quien lo encontró. Lleva el país, el eje, la ruta, las herramientas que volvieron vacías y los indicadores faltantes; todos esos campos salen de vocabulario controlado por la plataforma (códigos ISO3, slugs de eje, claves de herramienta, etiquetas horneadas de indicador).
El ticket no persiste ni sirve la pregunta del usuario. Se lee sólo para establecer que hubo un turno real y se descarta. Como este endpoint es público y sin autenticar, cualquier cosa que el ticket conservara sería algo que quien preguntó habría publicado sin proponérselo — y la postura de consentimiento del Fideicomiso de Datos tiene que valer para quien usa el chat, no sólo para las instituciones que contribuyen.
El texto sí se guarda, pero en otro sitio y bajo otras reglas: el registro privado de preguntas vive en una clave distinta (chat:questions:v1, nunca backlog:gaps:v1) y sólo se lee con Authorization: Bearer $QUESTION_LOG_TOKEN — sin token, GET /api/questions rechaza todo. El registro privado de turnos (chat:turns:v1, GET /api/chat-turns, mismo token) guarda además la respuesta, el modelo, la traza de herramientas y el resumen de verificación, para aprendizaje. Ningún camino público los alcanza: el backlog reconstruye cada ticket campo por campo, así que la garantía de este endpoint queda exactamente igual.
La garantía se aplica en lectura además de en escritura: los tickets escritos antes de esta regla siguen en el almacén, así que readGaps reconstruye cada fila campo por campo contra una lista de permitidos y descarta todo lo demás. Un campo nuevo tampoco puede llegar a la superficie pública por accidente: hay que añadirlo ahí a propósito.
No hay superficie de escritura. Los tickets los escribe el servidor de chat, en el mismo proceso que observó la recuperación vacía; no existe ruta de escritura alcanzable desde el navegador.
Referencia de endpoints
| Ruta | Qué es |
|---|---|
/api/v1/index.json | Manifiesto: datasets, conteos, tamaños masivos, licencia, estándares |
/api/v1/openapi.json | Spec OpenAPI 3.1 de la API de lectura (15 rutas núcleo) |
/api/v1/schemas/*.schema.json | JSON Schema: manifest, parameter, geography, observation-cell, citation |
/api/v1/parameters.json | 10 pilares + ODS primarios/secundarios |
/api/v1/geographies.json | 25 países + LATAM, ISO3 + M49 |
/api/v1/observations/<param>__<ISO3>.json | Una celda: indicadores, series, citas, ODS |
/api/v1/observations/<param>__<ISO3>.csv | La misma celda, una fila por indicador-año |
/api/v1/observations.csv | Masivo: todas las celdas concatenadas |
/api/v1/observations.sdmx.csv | Masivo, SDMX-CSV (ISO 17369), REF_AREA = M49 |
/api/v1/futuros-data.xlsx | Libro Excel (README/parameters/geographies/observations/citations) |
/api/v1/citations.json · .csv | Registro de citas completo |
/api/v1/freshness.json | Libro de refresco por fuente |
/api/v1/sdg-crosswalk.json | Pilar → ODS + indicador → código ODS oficial |
/api/v1/signals/<param>__<ISO3>.json | Señales citadas por celda (260/260 celdas; las 10 regionales __LATAM son []) |
/api/v1/governance/{resilience,regulation-index,scores}.json | Motor de Gobernanza: resiliencia democrática, observatorio regulatorio, índice compuesto |
/api/v1/contributions.json | Datos contribuidos condicionados por licencia |
/data.json | Catálogo DCAT (raíz del sitio) |
/feeds/futuros.xml · /feeds/<ISO3>.xml · /feeds/<param>__<ISO3>.xml | Feeds RSS 2.0 estáticos de señales, re-horneados en cada deploy por bake-feeds dentro de prebuild |
/feeds/index.json | Manifiesto de los 267 feeds RSS, con los conteos de lo omitido |
GET /api/backlog | Backlog de vacíos de datos (función en vivo, no horneada) — ver arriba |
GET /api/env-levers | Postura pública de las 15 palancas de producción que fallan cerrado: por palanca, su id, las variables que la componen, opens (qué habilita) y probe (cómo verificarla desde fuera), más un booleano set. Nunca devuelve valores de secretos; Cache-Control: no-store. Es la superficie legible por máquina detrás de /confianza — ver autoalojamiento |
Para acceso agéntico basado en herramientas al mismo corpus (con cómputo determinista y citas), vea el servidor MCP.
Política de rastreo (crawling)
Los datos son abiertos por diseño, así que el rastreo no está bloqueado — pero sí está declarado y observado:
robots.txt(https://futuros.xyz/robots.txt) permite todo salvo/admin/y una ruta-trampa interna. Los rastreadores de entrenamiento de IA (GPTBot, ClaudeBot, CCBot, Bytespider, etc.) están permitidos hoy, de forma explícita; la política por bot se cambia con una línea en ese archivo (la lista de tokens se mantiene enserver/scrape/known-agents.ts).- Raspar el HTML no sirve: cada ruta de la app devuelve el mismo shell de SPA sin contenido. Las superficies para máquinas son esta API, el servidor MCP y el catálogo DCAT (
/data.json) — úselas. - El acceso se observa, nunca se bloquea: un middleware de solo-observación (
middleware.ts+server/scrape/, runbook enserver/scrape/README.md) marca patrones de raspado masivo y de copia-por-IA como eventos internos. Ninguna petición se ralentiza ni se rechaza por esta capa.