1 Conceptos generales

1.1 ¿Qué es una API?

Una API (Application Programming Interface, interfaz de programación de aplicaciones) es una interfaz que define reglas y puntos de acceso mediante los cuales dos aplicaciones de software intercambian información y datos de forma programática, sin depender de pasos manuales (Ofoeda et al. 2019). En lugar de descargar manualmente hojas de cálculo desde un portal web, un cliente (por ejemplo, R) envía una solicitud a un servidor y recibe una respuesta estructurada, normalmente en formato JSON o XML, que puede procesarse de inmediato. Este modelo cliente–servidor, sin estado y basado en representaciones de recursos, es el que subyace a la arquitectura de la web moderna (Fielding 2000).

La analogía útil es la de un mesero en un restaurante: el comensal (nuestro programa) no entra a la cocina (la base de datos del Banco Mundial); le hace un pedido al mesero (la API) siguiendo un menú (la documentación), y este devuelve exactamente lo solicitado. El comensal no necesita saber cómo está organizada la cocina, solo cómo pedir.

1.2 Tipos de APIs

Las APIs pueden clasificarse según distintos criterios. Para efectos prácticos, conviene distinguir:

  • Por su arquitectura o protocolo: REST (el más difundido, basado en URLs y verbos HTTP) (Fielding 2000; Richardson y Ruby 2007), SOAP (más antiguo, basado en XML y muy estructurado), GraphQL (permite pedir exactamente los campos deseados en una sola consulta) (Hartig y Pérez 2018) y gRPC (orientado a alto rendimiento entre servicios).
  • Por su nivel de acceso: públicas o abiertas (disponibles para cualquier usuario, con o sin registro), de socios (requieren credenciales acordadas) e internas o privadas (uso exclusivo dentro de una organización) (Bauer et al. 2022).
  • Por lo que exponen: APIs de datos (consultan bases estadísticas, como la del Banco Mundial), APIs de servicios (envían correos, procesan pagos) y APIs de sistema operativo o de librerías (funciones dentro de un mismo entorno de programación).

La API del Banco Mundial es una API REST pública de datos: se consulta mediante URLs, no requiere clave de autenticación (API key) para la mayoría de sus series y devuelve resultados en formato JSON o XML.

1.3 Importancia de su uso

Para la investigación económica aplicada, el acceso vía API aporta ventajas que la descarga manual no ofrece (Bauer et al. 2022):

  • Reproducibilidad: el flujo de datos queda escrito en código; cualquier colega puede volver a ejecutar el análisis y obtener el mismo resultado. La reproducibilidad computacional se considera un estándar mínimo para evaluar hallazgos cuando la replicación independiente completa no es viable (Peng 2011).
  • Actualización automática: al volver a correr el script se obtienen los valores más recientes, sin repetir descargas a mano.
  • Escala y eficiencia: es igual de sencillo pedir un indicador para un país que para 200 economías y 60 años, en una sola instrucción.
  • Integración en el flujo de trabajo: los datos llegan ya en un formato tabular listo para limpiar, modelar y graficar dentro del mismo entorno (R), eliminando pasos intermedios propensos a error.

2 Muestra de datos: la API del Banco Mundial

La API principal del Banco Mundial es la API de Indicadores (Indicators API, versión 2), cuya URL base es (World Bank, s. f.-a, s. f.-c):

https://api.worldbank.org/v2/

La versión 2 reemplazó a la v1 ( que fue descontinuada en el año 2020) y exige incluir v2 en la ruta; entre sus mejoras figura la exposición de códigos ISO-2 e ISO-3, región, nivel de ingreso y tipo de préstamo en los metadatos de cada país (World Bank, s. f.-c). Su gran valor está en la desagregación: cada observación se identifica por varias dimensiones combinables (World Bank, s. f.-b). Las principales son:

Dimensión Descripción Ejemplo
País / economía ~217 economías, más agregados regionales y por ingreso SV (El Salvador), GT (Guatemala)
Indicador Código único de cada serie estadística NY.GDP.MKTP.CD (PIB, US$ corrientes)
Tiempo Año (algunas series: trimestre o mes) 2000:2024
Tema 20 temas: economía, salud, educación, pobreza… Economy & Growth
Fuente / base Más de 40 bases (WDI, deuda externa, etc.) World Development Indicators
Región / ingreso Agrupaciones geográficas y por nivel de ingreso Latin America & Caribbean, Upper middle income

El paquete wbstats incluye un catálogo precargado (wb_cachelist) que permite inspeccionar sin conexión toda esta estructura. Entre las más de cuarenta bases accesibles destaca World Development Indicators (WDI), la colección principal de indicadores de desarrollo del Banco Mundial, compilada a partir de fuentes internacionales oficialmente reconocidas (World Bank, s. f.-d). Los siguientes bloques muestran la información disponible desagregada por cada dimensión.

library(wbstats)
library(dplyr)

catalogo <- wb_cachelist

# ¿Cuántos elementos hay en cada dimensión del catálogo?
sapply(catalogo, NROW)
#>     countries    indicators       sources        topics       regions 
#>           295         29533            71            21            43 
#> income_levels lending_types     languages 
#>             7             4            23
# Países y economías de Centroamérica disponibles
catalogo$countries %>%
  filter(region == "Latin America & Caribbean ") %>%
  select(iso3c, iso2c, country, income_level, region) %>%
  filter(iso2c %in% c("SV", "GT", "HN", "NI", "CR", "PA", "BZ"))
# Los temas (topics) bajo los que se organizan los indicadores
catalogo$topics %>% select(topic_id, topic)
# Algunas de las bases de datos (sources) accesibles por la misma API
catalogo$sources %>% select(source_id, source) %>% head(10)

3 Ejemplos prácticos de acceso con R

Existen tres caminos, de mayor a menor nivel de abstracción, y a continuación se desarrolla un ejemplo completo de cada uno:

  1. WDI — el envoltorio (wrapper) más popular, ideal para empezar (Arel-Bundock 2025).
  2. wbstats — un wrapper más flexible (búsqueda por tema, salida en tibble, soporte multilingüe) (Piburn 2020).
  3. Acceso directo con httr + jsonlite — control total de la petición HTTP (Wickham 2023a; Ooms 2014).

Para R, el propio Banco Mundial ha señalado los paquetes WDI y wbstats como vías convenientes de acceso a sus datos; y para quienes no usan esas librerías, sugiere consumir directamente la API y procesar el JSON o XML devuelto, que es precisamente lo que hace la tercera vía (Khokhar 2013).

Antes de entrar en detalle, esta tabla resume en qué se diferencian y cuándo conviene cada una:

Vía Facilidad de uso Control sobre la petición Cuándo conviene
WDI Muy alta Bajo Empezar rápido; consultas simples de uno o varios indicadores.
wbstats Alta Medio Buscar por palabra clave o tema, columnas legibles y trabajo con paneles.
httr + jsonlite Baja Total Combinar series que ningún wrapper trae juntas y controlar paginación, campos y formato.

En la práctica, la mayoría del trabajo aplicado se resuelve con WDI o wbstats; el acceso directo se reserva para cuando se necesita algo que los wrappers no ofrecen.

3.1 Vía 1 — El paquete WDI

WDI es la manera más sencilla para acceder a los datos del Banco Mundial desde R. La idea es: no escribir direcciones web ni procesar respuestas; simplemente indicar qué país, qué indicador y qué años requiero, y el paquete se encarga de pedirlo y devolverlo en una tabla lista para usar. El trabajo se reparte en tres funciones:WDIcache(), WDIsearch() y WDI(), que corresponden a tres pasos naturales: poner al día el catálogo, encontrar el código del indicador y descargar los datos (Arel-Bundock 2025).

3.1.1 Paso 1 — Poner al día el catálogo de indicadores

Antes de buscar o descargar conviene tener la lista de indicadores actualizada. WDIcache() descarga desde los servidores del Banco Mundial el catálogo completo y vigente de indicadores y países. El paquete ya trae una copia local incorporada, pero esa copia puede quedar desfasada frente a los indicadores agregados más recientemente; WDIcache() baja la versión actual y devuelve un objeto (una lista con dos tablas, series y country—) que luego se pasa a WDIsearch() y WDI() mediante el argumento cache =. No recibe argumentos y la descarga tarda unos segundos porque trae todo el catálogo.

library(WDI)

# Descarga el catálogo vigente de indicadores desde el servidor del BM
mi_cache <- WDIcache()

# Estructura del objeto devuelto: dos elementos, 'series' y 'country'
str(mi_cache, max.level = 1) # Lee en una lista sencilla el contenido de 'mi_cache'
#> List of 2
#>  $ series :'data.frame': 29533 obs. of  5 variables:
#>  $ country:'data.frame': 295 obs. of  9 variables:

De aquí en adelante, pasar cache = mi_cache a las funciones de búsqueda y descarga garantiza que se trabaja sobre el listado más reciente disponible:

# Buscar sobre la caché recién descargada
WDIsearch("gdp", cache = mi_cache)[1:5, ]
# Descargar usando la caché; extra = TRUE añade región, nivel de ingreso, etc.
datos_pib <- WDI(
  country   = "US",
  indicator = "NY.GDP.PCAP.KD",
  extra     = TRUE,
  cache     = mi_cache
)

3.1.2 Paso 2 — Encontrar el código del indicador

Cada serie del Banco Mundial tiene un código (por ejemplo, NY.GDP.PCAP.KD para el PIB per cápita). Con más de mil indicadores, adivinar ese código es inviable. WDIsearch() resuelve el problema: recibe una palabra clave, la busca con grep sobre los nombres de los indicadores, (grep realiza la búsqueda ignorando mayúsculas y minúsculas) y devuelve una matriz con los códigos coincidentes y su descripción (Arel-Bundock 2025).

Por ejemplo, para el índice de Gini, la palabra aparece en decenas de indicadores (el resultado de la búsqueda será abrumadora, por lo que restingimos la sálida a solo 10 filas):

WDIsearch("gini")[1:10, ] 

Para reducir la dimensión del output y sabiendo que es un índice, se puede deducir:

WDIsearch("gini index")

Con el código en mano: SI.POV.GINI para el índice de Gini, ya se puede descargar.

3.1.3 Paso 3 — Descargar y graficar los datos

WDI() es la función central. Sus argumentos clave son:

  • country. Uno o varios códigos ISO-2/ISO-3, o "all")
  • indicator. Uno o varios códigos),
  • start y end – rango de años,
  • extra. Si es TRUE agrega columnas de región, ingreso, etc.) y
  • cache. La caché a utilizar.

Devuelve un data.frame en formato largo: una fila por país y año listo para graficar (Arel-Bundock 2025).

3.1.3.1 Ejemplo 1 — Índice de Gini en Centroamérica

El índice de Gini (SI.POV.GINI) mide cuánto se aparta la distribución del ingreso o, en algunos casos, del consumo de una economía respecto de una distribución perfectamente igualitaria: 0 equivale a igualdad perfecta y 100 a desigualdad máxima (World Bank, s. f.-d). Descargamos la serie para los seis países centroamericanos entre 2000 y 2023:

library(dplyr)
library(ggplot2)

datos <- WDI(
  country   = c("GTM", "SLV", "HND", "NIC", "CRI", "PAN"),
  indicator = "SI.POV.GINI",
  start     = 2000,
  end       = 2023,
  extra     = FALSE,
  cache     = mi_cache
)

ggplot(datos, aes(x = year, y = SI.POV.GINI, color = country)) +
  geom_line(linewidth = 1) +
  geom_point(size = 2) +
  labs(
    title = "Coeficiente de Gini en Centroamérica (2000-2023)",
    x = "Año", y = "Coeficiente de Gini", color = "País"
  ) +
  theme_minimal() +
  theme(legend.position = "right")

En el ggplot, aes() asigna el año al eje X, el valor del Gini al eje Y y el país al color; geom_line() traza la evolución y geom_point() marca cada observación. Como el Gini proviene de encuestas de hogares, la serie tiene huecos y geom_line() conecta solo los años con dato, de modo que las líneas no deben leerse como una medición continua.

3.1.3.2 Ejemplo 2 — Gasto público en salud

Para encontrar datos del gasto público en salud, volvemos a utilizar la función WDIsearch() combinada con palabras clave en inglés, como “health expenditure” o simplemente “health”, como lo queremos en proporción al PIB del país, agregamos a la consulta “gdp”:

WDIsearch("health.*expenditure.*gdp")[1:5, ]

La serie que nos interesa es SH.XPD.GHED.GD.ZS: Domestic general government health expenditure (% of GDP)

Cambiando un único argumento (el código del indicador) se accede a otro dominio. SH.XPD.GHED.GD.ZS mide el gasto público en salud de fuentes internas como porcentaje del PIB (World Bank, s. f.-d):

datos_salud <- WDI(
  country   = c("GTM", "SLV", "HND", "NIC", "CRI", "PAN"),
  indicator = "SH.XPD.GHED.GD.ZS",
  start     = 2000, end = 2023,
  extra     = FALSE, cache = mi_cache
)

ggplot(datos_salud, aes(x = year, y = SH.XPD.GHED.GD.ZS, color = country)) +
  geom_line(linewidth = 1) +
  geom_point() +
  labs(
    title = "Gasto público en salud (% del PIB) en Centroamérica, 2000-2023",
    x = "Año", y = "Porcentaje del PIB", color = "País"
  ) +
  theme_minimal()

3.2 Vía 2 — El paquete wbstats

wbstats hace lo mismo que WDI: traer datos sin escribir URLs, pero con una caja de herramientas más amplia: funciones con prefijo wb_, resultados en tibble, catálogo consultable por tema y soporte en español (Piburn 2020).

Lo usaremos para armar un pequeño panel fiscal de Centroamérica y presentarlo con tablas formateadas mediante kableExtra (Zhu 2024). El recorrido tiene cinco pasos: ver el catálogo, buscar el indicador (por palabra clave o tema), descargar los datos y graficarlos de dos maneras.

3.2.1 Paso 1 — Ver el catálogo completo

wb_indicators() devuelve todo el catálogo de indicadores como tibble, con código, nombre, descripción y tema; el argumento lang permite pedirlo en español. Es la forma de “hojear” qué hay disponible. Se muestran los diez primeros:

library(wbstats)
library(dplyr)
library(kableExtra)

indicadores <- wb_indicators(lang = "es")

indicadores |>
  head(10) |>
  kable(caption = "Indicadores disponibles en la API del Banco Mundial") |>
  kable_classic(html_font = "Helvetica", font_size = 14) |>
  add_footnote(label = "Tomado de la API del BM", notation = "symbol") |>
  kable_styling()
Indicadores disponibles en la API del Banco Mundial
indicator_id indicator unit indicator_desc source_org topics source_id source
1.0.HCount.1.90usd Tasa de Incidencia de la Pobreza ($1.90 al día) NA Tasa de Incidencia de la Pobreza mide la proporción de la población con ingreso per cápita diario (en PPA de 2011) por debajo de la línea de pobreza. Tabulaciones del LAC Equity Lab con datos de SEDLAC (CEDLAS y el Banco Mundial) 11 , Pobreza 37
1.0.HCount.2.5usd Tasa de Incidencia de la Pobreza ($2.50 al día) NA Tasa de Incidencia de la Pobreza mide la proporción de la población con ingreso per cápita diario (en PPA de 2005) por debajo de la línea de pobreza. Tabulaciones del LAC Equity Lab con datos de SEDLAC (CEDLAS y el Banco Mundial) 11 , Pobreza 37
1.0.HCount.Mid10to50 Tasa de Incidencia de la Clase Media ($10-50 al día) NA Tasa de Incidencia de la Pobreza mide la proporción de la población con ingreso per cápita diario (en PPA de 2005) por debajo de la línea de pobreza. Tabulaciones del LAC Equity Lab con datos de SEDLAC (CEDLAS y el Banco Mundial) 11 , Pobreza 37
1.0.HCount.Ofcl Tasa Oficial de la Pobreza Moderada-Nacional NA Tasa de Incidencia de la Pobreza mide la proporción de la población con ingreso per cápita diario por debajo de la línea de pobreza desarrollada por cada país. Tabulaciones del LAC Equity Lab de los datos de las Oficinas Nacionales de Estadística 11 , Pobreza 37
1.0.HCount.Poor4uds Tasa de Incidencia de la Pobreza ($4 al día) NA Tasa de Incidencia de la Pobreza mide la proporción de la población con ingreso per cápita diario (en PPA de 2005) por debajo de la línea de pobreza. Tabulaciones del LAC Equity Lab con datos de SEDLAC (CEDLAS y el Banco Mundial) 11 , Pobreza 37
1.0.HCount.Vul4to10 Tasa de incidencia de población Vulnerable ($4-10 al día) NA Tasa de Incidencia de la Pobreza mide la proporción de la población con ingreso per cápita diario (en PPA de 2005) por debajo de la línea de pobreza. Tabulaciones del LAC Equity Lab con datos de SEDLAC (CEDLAS y el Banco Mundial) 11 , Pobreza 37
1.0.PGap.1.90usd Brecha de Pobreza ($1.90 al día) NA La Brecha de Pobreza captura el déficit del ingreso o consumo promedio agregado relativo a la línea de pobreza a través de toda la población. Mide el total de recursos necesarios para traer a todos os pobres al nivel de la línea de pobreza (promediado sobre la población total). Tabulaciones del LAC Equity Lab con datos de SEDLAC (CEDLAS y el Banco Mundial) 11 , Pobreza 37
1.0.PGap.2.5usd Brecha de Pobreza ($2.50 al día) NA La Brecha de Pobreza captura el déficit del ingreso o consumo promedio agregado relativo a la línea de pobreza a través de toda la población. Mide el total de recursos necesarios para traer a todos os pobres al nivel de la línea de pobreza (promediado sobre la población total). Tabulaciones del LAC Equity Lab con datos de SEDLAC (CEDLAS y el Banco Mundial) 11 , Pobreza 37
1.0.PGap.Poor4uds Brecha de Pobreza ($4 al día) NA La Brecha de Pobreza captura el déficit del ingreso o consumo promedio agregado relativo a la línea de pobreza a través de toda la población. Mide el total de recursos necesarios para traer a todos os pobres al nivel de la línea de pobreza (promediado sobre la población total). Tabulaciones del LAC Equity Lab con datos de SEDLAC (CEDLAS y el Banco Mundial) 11 , Pobreza 37
1.0.PSev.1.90usd Severidad de la Pobreza ($1.90 al día) NA El índice de severidad de la pobreza combina información tanto de pobreza como de desigualdad entre los pobres, al promediar los cuadrados de las brechas de pobreza relativas a la línea de pobreza Tabulaciones del LAC Equity Lab con datos de SEDLAC (CEDLAS y el Banco Mundial) 11 , Pobreza 37
* Tomado de la API del BM

3.2.2 Paso 2 — Buscar el indicador: por palabra clave o por tema

Igual que WDI ofrece WDIsearch(), wbstats incluye su propia búsqueda tipo grep: wb_search(). Recibe un patrón, lo busca como expresión regular y sin distinguir mayúsculas de minúsculas sobre el código, el nombre y la descripción de los indicadores, y devuelve los que coinciden (Piburn 2020). Es el equivalente directo de WDIsearch() y suele ser la forma más rápida de dar con un código cuando se sabe qué se busca:

# Todos los indicadores relacionados con "*public expense" (gasto público), en proporción al
wb_search("expense.*gdp") |> head(10)

Cuando no se busca una palabra concreta sino un área temática, la alternativa es filtrar por la columna topics. Cada indicador está etiquetado con uno o varios temas; con grepl() y una expresión regular (la barra vertical | funciona como “o”) se conservan solo los de Public Sector: Government finance:

fiscales <- wb_indicators() |>
  filter(grepl("Public Sector", topics)) |>
  select(indicator_id, indicator)

fiscales |>
  head(10) |>
  kable(caption = "Indicadores económicos y fiscales", align = "c") |>
  kable_material(html_font = "sans-serif")
Indicadores económicos y fiscales
indicator_id indicator
GB.TAX.CMAR.ZS Highest marginal tax rate, corporate rate (%)
GC.AST.TOTL.CN Net acquisition of financial assets (current LCU)
GC.AST.TOTL.GD.ZS Net acquisition of financial assets (% of GDP)
GC.DOD.TOTL.CN Central government debt, total (current LCU)
GC.DOD.TOTL.GD.ZS Central government debt, total (% of GDP)
GC.LBL.TOTL.CN Net incurrence of liabilities, total (current LCU)
GC.LBL.TOTL.GD.ZS Net incurrence of liabilities, total (% of GDP)
GC.NFN.TOTL.CN Net investment in nonfinancial assets (current LCU)
GC.NFN.TOTL.GD.ZS Net investment in nonfinancial assets (% of GDP)
GC.NLD.TOTL.CN Net lending (+) / net borrowing (-) (current LCU)

3.2.3 Paso 3 — Descargar un panel fiscal

wb_data() es la función de descarga. Su rasgo más útil: admite en indicator un vector con nombres, donde la clave se usa como nombre de columna y el valor como código del indicador, de modo que el tibble resultante llega con columnas legibles en lugar de códigos crípticos.

Descargamos cuatro indicadores fiscales cuyas definiciones oficiales son (World Bank, s. f.-d):

  • Gasto del gobierno (GC.XPN.TOTL.GD.ZS, Expense, % del PIB),
  • Presión tributaria (GC.TAX.TOTL.GD.ZS, Tax revenue, % del PIB),
  • Deuda del gobierno central (GC.DOD.TOTL.GD.ZS, Central government debt, % del PIB) y
  • Remesas personales recibidas (BX.TRF.PWKR.DT.GD.ZS, Personal remittances, % del PIB).
mis_indicadores <- c(
  Gasto_Publico        = "GC.XPN.TOTL.GD.ZS",
  Ingresos_Tributarios = "GC.TAX.TOTL.GD.ZS",
  Deuda_gobierno       = "GC.DOD.TOTL.GD.ZS",
  Remesas_Personales   = "BX.TRF.PWKR.DT.GD.ZS"
)

datos_wb <- wb_data(
  indicator  = mis_indicadores,
  country    = c("SLV", "GTM", "HND", "CRI"),
  start_date = 2000,
  end_date   = 2020
)

datos_wb |>
  head(10) |>
  kable(format = "html", caption = "Panel fiscal de Centroamérica", align = "c") |>
  add_footnote(label = "Tomado de la API del BM", notation = "symbol") |>
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = TRUE, position = "center"
  )
Panel fiscal de Centroamérica
iso2c iso3c country date Remesas_Personales Deuda_gobierno Ingresos_Tributarios Gasto_Publico
CR CRI Costa Rica 2000 0.9056788 38.50185 12.83971 22.03412
CR CRI Costa Rica 2001 1.2421210 39.33511 13.36103 22.75867
CR CRI Costa Rica 2002 1.5118475 NA 13.36811 24.59492
CR CRI Costa Rica 2003 1.8578276 NA 13.58495 23.62190
CR CRI Costa Rica 2004 1.7170131 NA 13.35893 22.52539
CR CRI Costa Rica 2005 2.0974697 NA 13.63540 22.64636
CR CRI Costa Rica 2006 2.2590539 NA 13.84166 21.60123
CR CRI Costa Rica 2007 2.2984079 NA 14.85647 21.16081
CR CRI Costa Rica 2008 1.9634238 NA 15.23251 21.66894
CR CRI Costa Rica 2009 1.6688240 NA 13.18307 24.74499
* Tomado de la API del BM

El tibble devuelto trae una columna iso2c, iso3c`, el nombre del país, el año y una columna por cada indicador nombrado; esa estructura ancha es la que aprovechan la tabla y los gráficos de los pasos siguientes.

3.2.4 Paso 4 — Graficar la evolución de las remesas

Con los datos descargados, un ggplot de líneas muestra la trayectoria de las remesas personales (% del PIB), ampliando la consulta a los siete países del istmo:

library(ggplot2)

datos_wb2 <- wb_data(
  indicator  = mis_indicadores,
  country    = c("SLV", "GTM", "HND", "NIC", "CRI", "BLZ", "PAN"),
  start_date = 2000, end_date = 2020
)

ggplot(datos_wb2, aes(x = date, y = Remesas_Personales, color = country, group = country)) +
  geom_line(linewidth = 1) +
  scale_x_continuous(breaks = seq(2000, 2020, 5)) +
  labs(
    title = "Remesas personales recibidas (% del PIB) en Centroamérica",
    x = "Año", y = "Remesas personales (% del PIB)", color = "País"
  ) +
  theme_minimal()

3.3 Vía 3 — Acceso directo con httr + jsonlite: migración y remesas

Las dos vías anteriores usan paquetes que “esconden” la API detrás de una función cómoda. Aquí haremos lo contrario: hablarle a la API directamente, para entender qué ocurre por debajo y para poder combinar series que ningún paquete trae juntas.

El caso de estudio es la relación entre migración y remesas en América Latina: en buena parte de Centroamérica y el Caribe la migración funciona como una “válvula” económica y el dinero que envían los emigrantes (las remesas) sostiene el consumo interno, hasta volverse una fuente de ingreso tan resiliente como estructural (World Bank y KNOMAD 2024).

Usaremos cuatro series, con sus definiciones oficiales (World Bank, s. f.-d):

Código Variable Papel en el análisis
BX.TRF.PWKR.DT.GD.ZS Remesas personales recibidas (% del PIB) Indicador principal (“motor”)
BX.TRF.PWKR.CD.DT Remesas personales recibidas (US$ corrientes) Magnitud en dólares
SM.POP.NETM Migración neta (personas) Presión migratoria (estimación quinquenal)
SM.POP.TOTL.ZS Stock de migrantes internacionales (% de población) Acumulado histórico de migración

El recorrido tiene seis pasos: primero veremos cómo se le pide un solo dato a la API (Paso 0); luego automatizaremos ese pedido (Paso 1), uniremos las cuatro series (Paso 2), nos quedaremos con América Latina (Paso 3) y, por último, resumiremos los datos en una tabla (Paso 4) y dos gráficos (Pasos 5 a 6).

3.3.1 Paso 0 — ¿Cómo se le pide un dato a la API?

Antes de automatizar nada, conviene ver la operación más simple posible. Pedirle un dato a la API se parece a escribir una dirección web muy precisa: cada parte de esa dirección (la URL) le dice al servidor del Banco Mundial qué país, qué indicador y qué años queremos. La computadora del Banco Mundial recibe esa dirección y responde con el dato, no con una página web para leer.

library(httr)
library(jsonlite)

# Una sola pregunta a la API: remesas (% del PIB) de El Salvador, 2020-2023
url <- paste0(
  "https://api.worldbank.org/v2/",            # dirección base de la API (versión 2)
  "country/SLV/",                             # país: El Salvador (código ISO-3)
  "indicator/BX.TRF.PWKR.DT.GD.ZS",          # indicador: remesas (% del PIB)
  "?format=json&date=2020:2023&per_page=100"  # formato, rango de años y tamaño de página
)

respuesta <- GET(url)  # GET() envía la solicitud al servidor y guarda lo que responde

# content() extrae el texto de la respuesta y fromJSON() lo convierte en objetos de R
contenido <- fromJSON(content(respuesta, as = "text", encoding = "UTF-8"), flatten = TRUE)

# La respuesta llega SIEMPRE en dos partes:
contenido[[1]]$total  # (1) metadata: cuántos registros encontró
#> [1] 4
contenido[[2]][, c("country.value", "date", "value")]  # (2) los datos que pedimos

Lo esencial que hay que retener de este paso:

1. la URL se arma por piezas: país, indicador, formato, años—; 2. GET() hace la pregunta y fromJSON() traduce la respuesta a algo que R entiende; y 3. esa respuesta siempre trae dos partes: primero la metadata (cuántos datos hay y en cuántas “páginas” están repartidos) y después los datos en sí (World Bank, s. f.-b). Todo lo que sigue no es más que repetir esta misma idea de forma automática.

3.3.2 Paso 1 — Automatizar el pedido con una función

Escribir esa URL para cada país, indicador y año sería muy lento y quedaría propenso a errores. La siguiente función arma la URL automáticamente, hace la solicitud y devuelve una tabla ordenada. Añade además una protección: cuando hay muchos datos, la API los reparte en varias “páginas”, así que la función revisa cuántas páginas hay (pages) y, si es necesario, descarga y une las que falten con jsonlite::rbind_pages().

library(dplyr)

wb_indicador <- function(codigo, paises = "all", inicio = 1960, fin = 2026, per_page = 20000) {
  url_base <- paste0(
    "https://api.worldbank.org/v2/country/", paises, "/indicator/", codigo,
    "?format=json&date=", inicio, ":", fin, "&per_page=", per_page
  )

  resp <- GET(url_base)
  stop_for_status(resp)   # si la API falla, se detiene con un mensaje claro

  crudo         <- fromJSON(content(resp, as = "text", encoding = "UTF-8"), flatten = TRUE)
  metadata      <- crudo[[1]]         # la parte (1): metadata
  total_paginas <- metadata$pages     # ¿en cuántas páginas viene la respuesta?

  paginas <- list(crudo[[2]])         # la parte (2): datos de la primera página
  if (!is.null(total_paginas) && total_paginas > 1) {
    for (p in 2:total_paginas) {      # descarga las páginas restantes, si las hay
      resp_p  <- GET(paste0(url_base, "&page=", p))
      stop_for_status(resp_p)
      crudo_p <- fromJSON(content(resp_p, as = "text", encoding = "UTF-8"), flatten = TRUE)
      paginas[[p]] <- crudo_p[[2]]
    }
  }

  rbind_pages(paginas) |>             # pega todas las páginas en una sola tabla
    as_tibble() |>
    transmute(
      # coalesce por robustez: algunas bases del BM dejan vacío countryiso3code
      # y colocan el ISO-3 en country.id; coalesce toma el primer valor no vacío
      iso3c     = coalesce(na_if(countryiso3code, ""), country.id),
      anio      = as.integer(date),
      !!codigo := value               # la columna de valores toma el nombre del código
    )
}

Tres detalles clave: stop_for_status() interrumpe el proceso con un aviso legible si el servidor responde con error, en lugar de dejar que el problema aparezca más adelante disfrazado. La línea de la llave iso3c resuelve una inconsistencia real del Banco Mundial: según la base, el código de país (ISO-3) llega a veces en la columna countryiso3code y a veces en country.id; coalesce() simplemente toma “el que no esté vacío”, de modo que las uniones posteriores nunca fallen. Y !!codigo := value hace que la columna de valores se llame igual que el indicador, lo que evita choques de nombres al unir varias series.

3.3.3 Paso 2 — Descargar las cuatro series y unirlas

Aplicamos la función a los cuatro indicadores con purrr::map() (que repite la descarga para cada código) (Wickham y Henry 2023) y pegamos los resultados con reduce(full_join, ...). El full_join es una unión “generosa”: conserva toda combinación de país y año que aparezca en cualquiera de las series, aunque una tenga más años que otra.

library(purrr)

codigos <- c("BX.TRF.PWKR.DT.GD.ZS", "BX.TRF.PWKR.CD.DT", "SM.POP.NETM", "SM.POP.TOTL.ZS")

panel_migracion <- codigos |>
  map(wb_indicador) |>                           # una tabla por indicador
  reduce(full_join, by = c("iso3c", "anio")) |>  # las une por país y año
  rename(
    remesas_pib     = "BX.TRF.PWKR.DT.GD.ZS",
    remesas_usd     = "BX.TRF.PWKR.CD.DT",
    migracion_neta  = "SM.POP.NETM",
    stock_migrantes = "SM.POP.TOTL.ZS"
  )

3.3.4 Paso 3 — Quedarnos solo con América Latina

El panel anterior trae el mundo entero, e incluso “países” que en realidad son agregados (como “Mundo” o “Ingreso alto”). Para quedarnos con países latinoamericanos individuales, pedimos a la API su catálogo de países y lo filtramos por región (Wickham 2023b).

library(stringr)

meta_resp <- GET("https://api.worldbank.org/v2/country?format=json&per_page=400")
stop_for_status(meta_resp)
meta <- fromJSON(content(meta_resp, as = "text", encoding = "UTF-8"), flatten = TRUE)

paises_lac <- meta[[2]] |>
  as_tibble() |>
  transmute(
    iso3c   = id,
    pais    = name,
    region  = region.value,
    ingreso = incomeLevel.value
  ) |>
  filter(str_detect(region, "Latin America"))  # descarta agregados y otras regiones

Ahora un inner_join entre el panel mundial y ese catálogo hace dos cosas a la vez: añade el nombre del país y su región, y filtra, porque solo sobreviven las filas cuyo país aparece en el catálogo latinoamericano (los agregados y las demás regiones se quedan fuera automáticamente).

migracion_latam <- panel_migracion |>
  inner_join(paises_lac, by = "iso3c") |>
  relocate(pais, region, ingreso, .after = iso3c) |>
  arrange(pais, anio)

3.3.5 Paso 4 — Una tabla con el último dato disponible

Las cuatro series no se publican al mismo ritmo: las remesas son anuales, pero la migración neta es una estimación que el Banco Mundial reporta cada cinco años. Por eso buscamos el último dato de cada indicador por separado. La receta: estirar la tabla a formato “largo” con pivot_longer(), tomar el año más reciente de cada par país-indicador con slice_max(), y volver a “ancho” con pivot_wider() (Wickham et al. 2024). La presentación final usa kableExtra (Zhu 2024).

library(tidyr)
library(kableExtra)

migracion_largo <- migracion_latam |>
  pivot_longer(
    cols = c(remesas_pib, remesas_usd, migracion_neta, stock_migrantes),
    names_to = "codigo_indicador", values_to = "valor"
  ) |>
  filter(!is.na(valor)) |>
  mutate(indicador = recode(codigo_indicador,
    remesas_pib     = "Remesas recibidas (% del PIB)",
    remesas_usd     = "Remesas recibidas (US$ corrientes)",
    migracion_neta  = "Migración neta (personas)",
    stock_migrantes = "Stock de migrantes internacionales (% población)"
  ))

tabla_migracion <- migracion_largo |>
  group_by(pais, indicador) |>
  slice_max(anio, n = 1, with_ties = FALSE) |>   # el año más reciente con dato
  ungroup() |>
  mutate(valor_con_anio = paste0(round(valor, 2), " (", anio, ")")) |>
  select(pais, indicador, valor_con_anio) |>
  pivot_wider(names_from = indicador, values_from = valor_con_anio)

tabla_migracion |>
  kable(caption = "América Latina y el Caribe: migración y remesas, último dato disponible") |>
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE, html_font = "Helvetica", font_size = 11
  ) |>
  footnote(symbol = "Entre paréntesis, el año oficial más reciente disponible.")
América Latina y el Caribe: migración y remesas, último dato disponible
pais Migración neta (personas) Remesas recibidas (% del PIB) Remesas recibidas (US$ corrientes) Stock de migrantes internacionales (% población)
Antigua and Barbuda 12 (2025) 0.94 (2025) 22036892.59 (2025) 32.5 (2024)
Argentina 2859 (2025) 0.13 (2025) 916447215.5 (2025) 4.3 (2024)
Aruba 111 (2025) 1.04 (2023) 38964260.61 (2023) 68 (2024)
Bahamas, The 935 (2025) 0.42 (2024) 65929000 (2024) 16.8 (2024)
Barbados -87 (2025) 1.46 (2017) 85175892.5 (2017) 12.5 (2024)
Belize 360 (2025) 3.72 (2025) 123831546.94 (2025) 16.5 (2024)
Bolivia -3179 (2025) 1.96 (2025) 1267996895.51 (2025) 1.5 (2024)
Brazil -217283 (2025) 0.21 (2025) 4758109116.62 (2025) 0.7 (2024)
British Virgin Islands 153 (2025) NA NA 62.1 (2024)
Cayman Islands 888 (2025) 0.15 (2024) 11496340.14 (2024) 42.9 (2024)
Chile 51712 (2025) 0.03 (2025) 100289867.6 (2025) 7.8 (2024)
Colombia 129139 (2025) 2.87 (2025) 13129361534.85 (2025) 5.8 (2024)
Costa Rica 1000 (2025) 0.83 (2025) 853351002.16 (2025) 12.2 (2024)
Cuba -22244 (2025) NA NA 0 (2024)
Curacao 470 (2025) 5.38 (2023) 176607685.45 (2023) 43.1 (2024)
Dominica -242 (2025) 5.48 (2025) 39633245.56 (2025) 12.7 (2024)
Dominican Republic -35306 (2025) 9.74 (2025) 12411400000 (2025) 6.5 (2024)
Ecuador -17219 (2025) 5.93 (2025) 7734058771.59 (2025) 4.1 (2024)
El Salvador -23934 (2025) 27.51 (2025) 10098866981.46 (2025) 0.7 (2024)
Grenada -185 (2025) 3.35 (2025) 47508314.81 (2025) 6.3 (2024)
Guatemala -7959 (2025) 19.12 (2024) 21644521520 (2024) 0.5 (2024)
Guyana -5092 (2025) 3.22 (2023) 545579977.92 (2023) 6.5 (2024)
Haiti -32052 (2025) 16.95 (2024) 4111002565.95 (2024) 0.2 (2024)
Honduras -4778 (2025) 30.12 (2025) 11927362660.75 (2025) 0.4 (2024)
Jamaica -11053 (2025) 16.19 (2024) 3564377716.21 (2024) 0.8 (2024)
Mexico -108037 (2025) 3.51 (2025) 64391618338 (2025) 1.3 (2024)
Nicaragua -8371 (2025) 26.63 (2024) 5245600000 (2024) 0.6 (2024)
Panama 6151 (2025) 0.7 (2025) 634454278.24 (2025) 10.6 (2024)
Paraguay -12423 (2025) 3.1 (2025) 1525903924.32 (2025) 2.6 (2024)
Peru 11931 (2025) 1.58 (2025) 5299341957.05 (2025) 5.4 (2024)
Puerto Rico (US) 5495 (2025) NA NA 6.9 (2024)
Sint Maarten (Dutch part) 530 (2025) 2.85 (2023) 48022544.58 (2023) 69.5 (2024)
St. Kitts and Nevis 18 (2025) 3.36 (2025) 39716697.41 (2025) 17 (2024)
St. Lucia -23 (2025) 2.35 (2025) 62478904.44 (2025) 4.5 (2024)
St. Martin (French part) -1170 (2025) NA NA 0 (2024)
St. Vincent and the Grenadines -714 (2025) 7.04 (2025) 88324437.04 (2025) 4.8 (2024)
Suriname -967 (2025) 3.89 (2025) 176127338.79 (2025) 8.2 (2024)
Trinidad and Tobago 691 (2025) 0.77 (2025) 199557121.67 (2025) 7.5 (2024)
Turks and Caicos Islands 177 (2025) 0.57 (2018) 7481920 (2018) 61.1 (2024)
Uruguay -1338 (2025) 0.18 (2025) 149981548.74 (2025) 4.7 (2024)
Venezuela, RB -100467 (2025) 0.25 (2016) 2.79e+08 (2016) 4.4 (2024)
Virgin Islands (U.S.) -357 (2025) NA NA 66.9 (2024)
* Entre paréntesis, el año oficial más reciente disponible.

3.3.6 Paso 5 — ¿Quién depende más de las remesas hoy?

Un gráfico de barras ordena a los países por su nivel actual de remesas sobre el PIB. Fijamos una paleta de colores para seis economías clave, que reutilizaremos en el gráfico de dispersión del Paso 7.

library(ggplot2)

paises_clave <- c("El Salvador", "Honduras", "Guatemala", "Nicaragua", "Costa Rica", "Mexico")
paleta_paises <- c(
  "El Salvador" = "#e41a1c", "Honduras" = "#377eb8", "Guatemala" = "#4daf4a",
  "Nicaragua" = "#984ea3", "Costa Rica" = "#ff7f00", "Mexico" = "#a65628"
)

snapshot_remesas <- migracion_largo |>
  filter(codigo_indicador == "remesas_pib") |>
  group_by(pais) |>
  slice_max(anio, n = 1, with_ties = FALSE) |>
  ungroup() |>
  mutate(grupo = if_else(pais %in% paises_clave, pais, "Otros países de la región"))

ggplot(snapshot_remesas, aes(x = reorder(pais, valor), y = valor, fill = grupo)) +
  geom_col() +
  geom_text(aes(label = round(valor, 1)), hjust = -0.15, size = 3, color = "gray30") +
  coord_flip() +
  scale_fill_manual(values = c(paleta_paises, "Otros países de la región" = "grey80"), name = NULL) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.12))) +
  labs(
    title = "Remesas como motor económico en América Latina y el Caribe",
    subtitle = "Remesas personales recibidas (% del PIB), último año disponible por país",
    x = NULL, y = "% del PIB",
    caption = "Fuente: Banco Mundial (WDI) — BX.TRF.PWKR.DT.GD.ZS"
  ) +
  theme_minimal() +
  theme(legend.position = "bottom")

3.3.7 Paso 6 — La evolución en el tiempo (serie temporal interactiva)

Para ver cómo cambió la dependencia de las remesas usamos TSstudio::ts_plot(), que produce un gráfico de líneas (Krispin 2020).

library(TSstudio)

# 1) Remesas (% del PIB) de los países clave, en formato ancho: años en filas,
#    países en columnas, con la rejilla de años completa (NA donde no hay dato).
serie_wide <- migracion_largo |>
  filter(codigo_indicador == "remesas_pib", pais %in% paises_clave) |>
  select(pais, anio, valor) |>
  complete(pais, anio = full_seq(anio, 1)) |>   # rejilla anual completa (sin huecos)
  pivot_wider(names_from = pais, values_from = valor) |>
  arrange(anio)

# 2) Convertir a serie temporal ANUAL: frequency = 1 y start = primer año.
anio_inicial <- min(serie_wide$anio)
serie_ts <- ts(serie_wide[, -1], start = anio_inicial, frequency = 1)

# 3) Graficar de forma interactiva.
ts_plot(serie_ts,
        title  = "Evolución de las remesas recibidas (% del PIB)",
        Xtitle = "Año",
        Ytitle = "% del PIB")

El bloque hace tres cosas: reorganiza los datos a una tabla de años × países, rellena los años faltantes para que la serie no tenga saltos, y recién entonces la convierte en un objeto ts anual (frequency = 1) que ts_plot() puede dibujar. Esta es la corrección de la advertencia hecha en la Vía 2: la frecuencia debe reflejar la periodicidad real de los datos.

4 Buenas prácticas y cierre

  • Guardar en caché los resultados de la API (por ejemplo, con cache=TRUE en los chunks o guardando un .rds) para no golpear el servidor en cada compilación y trabajar sin conexión.
  • Documentar los códigos de indicador usados: NY.GDP.MKTP.CD es más reproducible que “PIB corriente”.
  • Verificar la cobertura temporal: no todos los indicadores cubren todos los años ni todos los países; conviene revisar los NA.
  • Citar la fuente y la fecha de descarga, ya que las series se revisan.

Los tres caminos consultan la misma API; la elección depende del control que se necesite. Para la mayoría del trabajo aplicado, WDI o wbstats bastan; el acceso directo con httr queda como recurso cuando se requiere ajustar la petición al detalle.

Todo el análisis es reproducible en el entorno estadístico R (R Core Team 2025) con los paquetes WDI (Arel-Bundock 2025), wbstats (Piburn 2020), httr (Wickham 2023a), jsonlite (Ooms 2014), dplyr (Wickham et al. 2023), tidyr (Wickham et al. 2024), purrr (Wickham y Henry 2023), stringr (Wickham 2023b), ggplot2 (Wickham 2016), kableExtra (Zhu 2024) y TSstudio (Krispin 2020).

Referencias

Arel-Bundock, Vincent. 2025. WDI: World Development Indicators and Other World Bank Data. https://vincentarelbundock.github.io/WDI/.
Bauer, Paul C., Camille Landesvatter, y Lion Behrens. 2022. APIs for Social Scientists: A Collaborative Review (V1.0). Online book. https://bookdown.org/paul/apis_for_social_scientists/.
Fielding, Roy Thomas. 2000. «Architectural Styles and the Design of Network-based Software Architectures». Tesis doctoral, University of California, Irvine. https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm.
Hartig, Olaf, y Jorge Pérez. 2018. «Semantics and Complexity of GraphQL». Proceedings of the 2018 World Wide Web Conference (WWW ’18) (Lyon, France), 1155-64. https://doi.org/10.1145/3178876.3186014.
Khokhar, Tariq. 2013. Accessing the World Bank Data APIs in Python, R, Ruby & Stata. World Bank Data Blog. https://blogs.worldbank.org/en/opendata/accessing-world-bank-data-apis-python-r-ruby-stata.
Krispin, Rami. 2020. TSstudio: Functions for Time Series Analysis and Forecasting. https://github.com/RamiKrispin/TSstudio.
Ofoeda, Joshua, Richard Boateng, y John Effah. 2019. «Application Programming Interface (API) Research: A Review of the Past to Inform the Future». International Journal of Enterprise Information Systems 15 (3): 76-95. https://doi.org/10.4018/IJEIS.2019070105.
Ooms, Jeroen. 2014. «The jsonlite Package: A Practical and Consistent Mapping Between JSON Data and R Objects». arXiv:1403.2805 [stat.CO]. https://arxiv.org/abs/1403.2805.
Peng, Roger D. 2011. «Reproducible Research in Computational Science». Science 334 (6060): 1226-27. https://doi.org/10.1126/science.1213847.
Piburn, Jesse. 2020. wbstats: Programmatic Access to Data and Statistics from the World Bank API. https://doi.org/10.11578/dc.20171025.1827.
R Core Team. 2025. R: A Language and Environment for Statistical Computing. R Foundation for Statistical Computing. https://www.R-project.org/.
Richardson, Leonard, y Sam Ruby. 2007. RESTful Web Services. O’Reilly Media.
Wickham, Hadley. 2016. ggplot2: Elegant Graphics for Data Analysis. Springer-Verlag New York. https://ggplot2.tidyverse.org.
Wickham, Hadley. 2023a. httr: Tools for Working with URLs and HTTP. https://httr.r-lib.org/.
Wickham, Hadley. 2023b. stringr: Simple, Consistent Wrappers for Common String Operations. https://stringr.tidyverse.org.
Wickham, Hadley, Romain François, Lionel Henry, Kirill Müller, y Davis Vaughan. 2023. dplyr: A Grammar of Data Manipulation. https://dplyr.tidyverse.org.
Wickham, Hadley, y Lionel Henry. 2023. purrr: Functional Programming Tools. https://purrr.tidyverse.org.
Wickham, Hadley, Davis Vaughan, y Maximilian Girlich. 2024. tidyr: Tidy Messy Data. https://tidyr.tidyverse.org.
World Bank. s. f.-a. About the Indicators API Documentation. World Bank Data Help Desk. Accedido 12 de septiembre de 2026. https://datahelpdesk.worldbank.org/knowledgebase/articles/889392-about-the-indicators-api-documentation.
World Bank. s. f.-b. API Basic Call Structures. World Bank Data Help Desk. Accedido 12 de septiembre de 2026. https://datahelpdesk.worldbank.org/knowledgebase/articles/898581-api-basic-call-structures.
World Bank. s. f.-c. New Features and Enhancements in the V2 API. World Bank Data Help Desk. Accedido 12 de septiembre de 2026. https://datahelpdesk.worldbank.org/knowledgebase/articles/1886674-new-features-and-enhancements-in-the-v2-api.
World Bank. s. f.-d. World Development Indicators. World Bank Data. Accedido 12 de septiembre de 2026. https://datatopics.worldbank.org/world-development-indicators/.
World Bank, y KNOMAD. 2024. Migration and Development Brief 40. World Bank; Global Knowledge Partnership on Migration; Development (KNOMAD). https://www.knomad.org/publication/migration-and-development-brief-40.
Zhu, Hao. 2024. kableExtra: Construct Complex Table with ’kable’ and Pipe Syntax. https://haozhu233.github.io/kableExtra/.