Skip to content

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ás public/data.json.tmp) y se intercambia sobre las rutas vivas con renameSync como 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/v1 a 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_pt van después de citation_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ónDeriva que bloquea
1index.json.counts.citations = longitud de public/data/citations.jsonmanifiesto desfasado del registro de citas
2cada celda de caché no vacía tiene su observations/<param>__<ISO3>.json horneadoceldas descartadas en silencio
3los CSV/SDMX/XLSX masivos existen, no están vacíos y parsean (CSV: cabecera + ≥1 fila; XLSX: abre)volcados masivos corruptos
4mtime del archivo index.json ≥ mtime más nuevo de public/data − 5 min de toleranciahorneado 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
5filas de observaciones < 1.000.000desbordar el techo de hoja única de XLSX (1.048.576 filas)
6cada referencia persona/caso del índice de búsqueda resuelve a un archivo en discohits de recuperación muertos (137 referencias de persona muertas se despacharon en julio de 2026)
7etiquetas 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.json trae api_version, generated_at y una cadena stability — lea generated_at para 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=86400

Así, 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ándarDónde
REF_AREA = UN M49 numérico + ISO 3166-1 alfa-3cada 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).

bash
curl -s https://futuros.xyz/api/v1/index.json
jsonc
{
  "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.csv y el catálogo DCAT /data.json aún no están en la spec).
  • /api/v1/schemas/{manifest,parameter,geography,observation-cell,citation}.schema.jsonJSON 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.

bash
curl -s https://futuros.xyz/api/v1/parameters.json
jsonc
[
  { "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.

bash
curl -s https://futuros.xyz/api/v1/geographies.json
jsonc
[
  { "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

bash
curl -s https://futuros.xyz/api/v1/observations/salud__MEX.json
jsonc
{
  "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:

bash
curl -s https://futuros.xyz/api/v1/observations/salud__MEX.csv

Cabecera:

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_pt

label_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):

bash
curl -s https://futuros.xyz/api/v1/observations.csv -o futuros-observations.csv

Misma cabecera que el CSV por celda. Cárguelo directo:

python
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:

bash
curl -s https://futuros.xyz/api/v1/observations.sdmx.csv
DATAFLOW,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-2024

DATAFLOW 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:

bash
curl -s https://futuros.xyz/api/v1/futuros-data.xlsx -o futuros-data.xlsx

Citas — citations.json / citations.csv

El registro de fuentes completo contra el que resuelve cada citation_id.

bash
curl -s https://futuros.xyz/api/v1/citations.json
curl -s https://futuros.xyz/api/v1/citations.csv

Columnas 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.

bash
curl -s https://futuros.xyz/api/v1/freshness.json
jsonc
{
  "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).

bash
curl -s https://futuros.xyz/api/v1/sdg-crosswalk.json

Señ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.

bash
curl -s https://futuros.xyz/api/v1/signals/salud__ARG.json

Contribuciones — 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.

bash
curl -s https://futuros.xyz/api/v1/contributions.json

Motor 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:

RutaQué es
/api/v1/governance/resilience.jsonÍndice de Resiliencia Democrática: subíndices + compuesto, citado por país
/api/v1/governance/regulation-index.jsonObservatorio Regulatorio: instrumentos por país y pilar
/api/v1/governance/scores.jsonÍndice compuesto goalpost por geografía (series 2010–2024)
bash
curl -s https://futuros.xyz/api/v1/governance/resilience.json

La 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:

bash
curl -s https://futuros.xyz/data.json

Anuncia 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.

RutaQué es
/feeds/futuros.xmlMaestro regional: todas las señales de la región en un solo feed
/feeds/<ISO3>.xmlPor país: unión de sus 10 feeds de pilar, máximo 50 ítems
/feeds/<param>__<ISO3>.xmlPor celda (pilar × país): señales citadas con deep-link a la fuente
/feeds/index.jsonManifiesto: 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
bash
curl -s https://futuros.xyz/feeds/salud__BRA.xml

Los 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ámetroPor defectoQué hace
limit100 (máx. 500)Cuántos tickets devolver
countryFiltra por ISO3 (se normaliza a mayúsculas)
pillarFiltra 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

json
{
  "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: false significa que el almacén durable (Upstash) no está cableado en ese despliegue: gaps es entonces el anillo en memoria de esa instancia, ni compartido ni persistente. Se declara en vez de fingir que existe un backlog común.
  • hits es 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_sources só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

RutaQué es
/api/v1/index.jsonManifiesto: datasets, conteos, tamaños masivos, licencia, estándares
/api/v1/openapi.jsonSpec OpenAPI 3.1 de la API de lectura (15 rutas núcleo)
/api/v1/schemas/*.schema.jsonJSON Schema: manifest, parameter, geography, observation-cell, citation
/api/v1/parameters.json10 pilares + ODS primarios/secundarios
/api/v1/geographies.json25 países + LATAM, ISO3 + M49
/api/v1/observations/<param>__<ISO3>.jsonUna celda: indicadores, series, citas, ODS
/api/v1/observations/<param>__<ISO3>.csvLa misma celda, una fila por indicador-año
/api/v1/observations.csvMasivo: todas las celdas concatenadas
/api/v1/observations.sdmx.csvMasivo, SDMX-CSV (ISO 17369), REF_AREA = M49
/api/v1/futuros-data.xlsxLibro Excel (README/parameters/geographies/observations/citations)
/api/v1/citations.json · .csvRegistro de citas completo
/api/v1/freshness.jsonLibro de refresco por fuente
/api/v1/sdg-crosswalk.jsonPilar → ODS + indicador → código ODS oficial
/api/v1/signals/<param>__<ISO3>.jsonSeñales citadas por celda (260/260 celdas; las 10 regionales __LATAM son [])
/api/v1/governance/{resilience,regulation-index,scores}.jsonMotor de Gobernanza: resiliencia democrática, observatorio regulatorio, índice compuesto
/api/v1/contributions.jsonDatos contribuidos condicionados por licencia
/data.jsonCatálogo DCAT (raíz del sitio)
/feeds/futuros.xml · /feeds/<ISO3>.xml · /feeds/<param>__<ISO3>.xmlFeeds RSS 2.0 estáticos de señales, re-horneados en cada deploy por bake-feeds dentro de prebuild
/feeds/index.jsonManifiesto de los 267 feeds RSS, con los conteos de lo omitido
GET /api/backlogBacklog de vacíos de datos (función en vivo, no horneada) — ver arriba
GET /api/env-leversPostura 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 en server/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 en server/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.

Cada cifra con su fuente — la trazabilidad es el contrato.