Tidyverse-friendly R interface to the MI Social API (Matriz de Informação Social) of the Brazilian Ministry of Development and Social Assistance (MDS/SAGI): https://aplicacoes.mds.gov.br/sagi/servicos/misocial.
The package follows the architecture of
ibger: a small English public API,
long-format tibble output, validation before any request, in-memory
per-session cache and offline tests with httptest2 fixtures. The sibling
Python package,
misocialpy,
mirrors the same functions,
name by name.
| Theme | Function | Content |
|---|---|---|
cadunico |
mds_cadunico() |
Cadastro Único: registered families and people, by income bracket |
bolsa_familia |
mds_bolsa_familia() |
Bolsa Família programme: beneficiary families, benefits and transfers |
bpc_suas |
mds_bpc_suas() |
BPC benefit and the social assistance network (CRAS, CREAS) |
suas_rede |
mds_suas_rede() |
SUAS network (CadSUAS): CRAS, CREAS, Centro POP, day centres, shelters and staff per unit |
suas_financiamento |
mds_suas_financiamento() |
FNAS transfers and balances of the municipal funds, and IGD-M |
pbf_condicionalidades |
mds_pbf_condicionalidades() |
Education and health conditionality monitoring (SICON) and sanctions |
seguranca_alimentar |
mds_seguranca_alimentar() |
PAA, Fomento Rural and cisterns |
The fields of each theme are in mds_fields(). Any other field of the API
(mds_api_fields()) can be requested by name in mds_data().
# install.packages("remotes")
remotes::install_github("StrategicProjects/misocial", subdir = "misocialr")
Requires R >= 4.1, httr2 >= 1.0.4, cli >= 3.6.1, dplyr >= 1.1.1 and purrr >= 1.0.0 (tests: testthat >= 3.2.1).
library(misocialr)
mds_bolsa_familia(
periods = c(202508, 202509),
localities = 2611606, # Recife (6 or 7 digits)
fields = c("qtd_familias_beneficiarias_bolsa_familia_i",
"valor_repassado_bolsa_familia_f")
)
#> # A tibble: 4 × 9 (selected columns)
#> ibge_code locality_name period field unit value
#> 1 2611606 RECIFE 202508 qtd_familias_beneficiarias_bolsa_familia_i Famílias 140350
#> 2 2611606 RECIFE 202509 qtd_familias_beneficiarias_bolsa_familia_i Famílias 139452
#> 3 2611606 RECIFE 202508 valor_repassado_bolsa_familia_f R$ 92124240
#> 4 2611606 RECIFE 202509 valor_repassado_bolsa_familia_f R$ 93354456
Accepted formats:
periods: NULL (last 12 months), -n (last n months), years
(2024) and/or YYYYMM months (202401).localities: "BR", states ("PE" or 26), municipalities with 6 or 7
digits, combined freely.Output columns: locality_id (6 digits, as in the API), ibge_code
(7 digits), locality_name, state, period, field, label, unit,
value.
The API answers 0, not "missing", for months that were not published
yet. mds_periods() shows this: n_nonzero counts only the municipalities
with a non-zero value.
mds_periods("bpc_ben_i", localities = "PE") |> tail(5)
#> period n_localities n_nonzero
#> 1 202608 185 185
#> 2 202609 185 0
#> 3 202610 185 0
#> 4 202611 185 0
#> 5 202612 185 0
mds_drop_unpublished() removes those zeros explicitly: future months and
months after the last month with a non-zero value. A zero in the middle of
the series, followed by a month with a value, is kept. The outputs above
come from queries made on 2026-10-19.
The "last month with a value" can come from two places:
reference = "selection" (default): the localities of the query itself.
No extra request, but in a small selection a legitimate trailing zero (a
municipality without a CREAS, say) is removed as well.reference = "brazil": the whole country, via mds_periods(). A month is
only removed when no municipality in Brazil has a value; legitimate zeros
of small selections are preserved.The query is checked before any data request, and the cases below are errors with a message, instead of an empty or silently different result:
character(0)), NA or empty string, which used to turn
into a query for the whole of Brazil;"BR" (c("BR", "Recifex"));2611600 instead of 2611606,
which used to query Recife);NA, non-integer (-1.9) period, or a negative period mixed
with others;regiao) or an identification field (municipio): the
package only returns numeric values;numFound,
a partial Solr result, a negative or non-integer numFound, missing
docs or facet_counts, a total that changes while paging, a document
read twice, a document without municipality or month, a field with more
than one value in the same document;Every response is checked before it is used or cached: a failure of the source does not become "no data" for the rest of the session.
Errors are classed, for programmatic handling: misocialr_error_validation
(input), misocialr_error_response (incomplete response),
misocialr_error_conflict (the source contradicts itself) and
misocialr_error_http (network, with status and the original error in
parent); all inherit from misocialr_error. A requested municipality
with no document raises the warning misocialr_warning_missing.
mds_drop_unpublished() never deletes a non-zero value. With
reference = "brazil", if the national reference says that a month with a
value was not published, it is queried again; if the contradiction remains,
the month is kept with a warning. To inspect before removing:
mds_drop_unpublished(x, action = "flag")
# adds publication_status ("published", "future", "unpublished",
# "missing"), publication_reason and publication_reference
This is an inference from the published values, not an official publication date.
options(misocialr.timeout = 300): maximum time per request, in seconds
(default 180).options(misocialr.quiet = TRUE): hides the progress messages.attr(x, "retrieved_at"): date and time of the query that produced x
(also in mds_periods() and mds_localities()).options(misocialr.cache_ttl = 3600): cache validity, in seconds (the
default is the whole session).refresh = TRUE in mds_periods(), mds_localities() and
mds_api_fields() queries a result again; mds_clear_cache() clears
everything. Equivalent selections (c("PE", "PB") and c("PB", "PE"))
share the result.mds_localities(period = ) picks the month of the municipality list.Retry-After capped at 30 s per attempt and 120 s in total;
options(misocialr.retry_backoff = 2) sets the wait between them.periods accepts years from 2000 to the year after the current one (the
API starts in 2003-01).mds_fields() tells the nature of each field (nature) and where that
classification comes from (nature_basis):
nature |
Sum across municipalities | Sum across months |
|---|---|---|
estoque (count in the month) |
yes | no |
fluxo (value of the month) |
yes | yes |
fluxo anual (one value per year, in December) |
yes | one value per year |
acumulado (running total) |
yes | no |
taxa, média, índice
|
no | no |
nature_basis = "medido" means the monthly series was inspected (a running
total only grows; a monthly value oscillates and restarts); the others are
inferences from the group or from the label and unit.
cursorMark, which walks by key rather than by position; the
package still flags a changing total and a repeated document. A long
query sees the source as it was at each page, not as a single snapshot.tests/testthat/paridade/casos.json), but parity is only guaranteed for
what those cases cover.storage_type says how the value is stored in the API (suffix _i,
_l, _f, _d, _s), not what it measures.mds_fields() are a
reading of the technical names; all 130 fields were checked against the
API (data-raw/check_dictionary.R). CRAS and CREAS appear in two themes
(bpc_suas and suas_rede).igdm_f is an index: do not sum across municipalities.pbf_qtd_familias_quilombolas_benef_i and
pbf_qtd_familias_sit_rua_benef_i have the same value in every
municipality and month (PE 2020–2026: 14,615 of 14,615 pairs; Brazil
2025-09: 5,570 of 5,570, summing 283,923), and Recife shows 1 homeless
family. They were left out of the dictionary until the source fixes them.mds_periods("cadun_qtd_familias_cadastradas_i") jumps from 202503 to
202505).pbf_qtd_familias_benef_i is not the total of Bolsa Família families
(Recife, 2024-02: 9,847, against 146,737 in
qtd_familias_beneficiarias_bolsa_familia_i). It is out of the
dictionary.Rscript -e 'testthat::test_local(".")' # tests, offline
Rscript data-raw/record_fixtures.R # re-records fixtures (network)
Rscript data-raw/check_dictionary.R # checks the dictionary against the API (network)