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$ 93354456Accepted 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 0mds_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_referenceThis 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)