1. Planteamiento

En buena parte de América Latina —sobre todo en Centroamérica y el Caribe— la migración funciona como una especie de “válvula” económica: cuando la economía doméstica no genera suficiente empleo, una parte de la población emigra y el dinero que envía de vuelta (las remesas) termina sosteniendo el consumo interno. Para explorar esa relación con datos reales del Banco Mundial se necesitan cuatro series, dos del tema Remesas y dos del tema Migración:

Código Variable Papel en el análisis
BX.TRF.PWKR.DT.GD.ZS Remesas recibidas (% del PIB) Indicador principal (“motor”)
BX.TRF.PWKR.CD.DT Remesas 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 (% pob.) Acumulado histórico de migración

2. Función de descarga: httr + jsonlite, con paginación

La API de Indicadores del Banco Mundial devuelve una lista de dos elementos:

el primero ([[1]]) trae la metadata de la consulta —cuántos registros hay en total y en cuántas páginas están repartidos— y el segundo ([[2]]) trae los datos. Como aquí se piden todos los países del mundo (country/all) para 66 años de historia, el resultado puede no caber en una sola página, así que la función revisa el campo pages de la metadata y, si hace falta, descarga y combina las páginas adicionales con jsonlite::rbind_pages() antes de convertir el resultado en un tibble.

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) # corta la ejecución con un mensaje claro si la API falla

  crudo <- fromJSON(content(resp, as = "text", encoding = "UTF-8"), flatten = TRUE)
  metadata <- crudo[[1]]
  total_paginas <- metadata$pages

  paginas <- list(crudo[[2]])

  if (!is.null(total_paginas) && total_paginas > 1) {
    for (p in 2:total_paginas) {
      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) |>
    as_tibble() |>
    transmute(
      # coalesce por robustez: algunas bases del Banco Mundial dejan vacío
      # countryiso3code y ponen el ISO-3 en country.id
      iso3c = coalesce(na_if(countryiso3code, ""), country.id),
      anio  = as.integer(date),
      !!codigo := value # la columna toma el nombre del propio código
    )
}

3. Descarga y unión de las cuatro series

Cada llamada a wb_indicador() trae un tibble con tres columnas: iso3c, anio y el valor del indicador correspondiente. Recorremos los cuatro códigos con purrr::map() y fusionamos los resultados con reduce(full_join, ...), de modo que ninguna combinación país-año se pierda aunque una serie tenga más cobertura que otra.

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) |>
  reduce(full_join, by = c("iso3c", "anio")) |>
  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"
  )

4. Catálogo de países como filtro regional

El panel anterior trae el mundo entero, incluidos agregados regionales y grupos de ingreso. Para acotarlo a países individuales de América Latina y el Caribe, se descarga el catálogo de países de la API y se filtra por el campo region.

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

5. Filtrado a América Latina

Un inner_join entre el panel mundial y el catálogo filtrado cumple doble función: agrega el nombre del país y la región, y al mismo tiempo descarta todo lo que no sea un país latinoamericano individual.

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

6. Tabla comparativa: el último dato disponible

Las cuatro series no se actualizan en el mismo año: la migración neta es una estimación quinquenal, mientras que las remesas se publican cada año. Por eso el “último dato disponible” se calcula por separado para cada indicador, en vez de forzar una sola fila por país con un único año de referencia.

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) |>
  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 = "Times New Roman", font_size = 11
  ) |>
  footnote(symbol = "Entre paréntesis se indica 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 se indica el año oficial más reciente disponible.

7. ¿Quién depende más de las remesas hoy?

# Estos seis países y su paleta se reutilizan en las secciones 8 y 9 para que
# cada país conserve siempre el mismo color a lo largo de las tres gráficas.
paises_clave <- c("El Salvador", "Honduras", "Guatemala", "Nicaragua", "Jamaica", "Mexico")
paleta_paises <- c(
  "El Salvador" = "#e41a1c", "Honduras" = "#377eb8", "Guatemala" = "#4daf4a",
  "Nicaragua" = "#984ea3", "Jamaica" = "#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")

8. Evolución histórica en los países más dependientes

paises_clave <- c("El Salvador", "Honduras", "Guatemala", "Nicaragua", "Jamaica", "Mexico")

df_remesas_hist <- migracion_largo |>
  filter(codigo_indicador == "remesas_pib", pais %in% paises_clave)

ggplot(df_remesas_hist, aes(x = anio, y = valor, color = pais, group = pais)) +
  geom_line(linewidth = 1) +
  theme_minimal(base_size = 12) +
  labs(
    title = "Evolución histórica de las remesas (% del PIB)",
    subtitle = "Países con mayor dependencia de remesas en América Latina",
    x = "Año", y = "Remesas (% del PIB)", color = "País"
  ) +
  theme(legend.position = "bottom")

9. ¿La migración explica las remesas?

Si la migración es el motor de las remesas, los países con migración neta más negativa (más emigrantes que inmigrantes) deberían mostrar mayores remesas como porcentaje del PIB. El siguiente gráfico pone a prueba esa relación usando todos los país-año en los que ambas variables están disponibles al mismo tiempo.

df_relacion <- migracion_latam |>
  filter(!is.na(remesas_pib), !is.na(migracion_neta))

df_relacion_resto  <- df_relacion |> filter(!pais %in% paises_clave)
df_relacion_clave   <- df_relacion |> filter(pais %in% paises_clave)

ggplot() +
  geom_point(data = df_relacion_resto, aes(x = migracion_neta, y = remesas_pib),
             color = "grey80", alpha = 0.5) +
  geom_point(data = df_relacion_clave, aes(x = migracion_neta, y = remesas_pib, color = pais),
             size = 2, alpha = 0.85) +
  geom_smooth(data = df_relacion, aes(x = migracion_neta, y = remesas_pib),
              method = "lm", se = FALSE, color = "firebrick", linewidth = 0.8) +
  scale_color_manual(values = paleta_paises, name = "País destacado") +
  labs(
    title = "Migración neta y remesas recibidas en América Latina",
    subtitle = "Cada punto es un país en un año disponible (estimaciones quinquenales de migración)",
    x = "Migración neta (personas)", y = "Remesas recibidas (% del PIB)",
    caption = "Fuente: Banco Mundial (WDI) — SM.POP.NETM y BX.TRF.PWKR.DT.GD.ZS"
  ) +
  theme_minimal()

10. Conclusión

La tabla y las tres gráficas permiten leer la misma historia desde distintos ángulos: qué países dependen más de las remesas hoy (barras), cómo ha cambiado esa dependencia en el tiempo (líneas), y si existe una relación estadística entre la intensidad de la migración y el peso de las remesas en la economía (dispersión). En conjunto, sostienen la idea de que, para varias economías centroamericanas y caribeñas, la migración no es solo un fenómeno social si no un componente estructural del ingreso nacional.