'Tidyverse'-friendly interface to the 'MI Social' API of the Brazilian Ministry of Development and Social Assistance ('MDS', 'SAGI') < https://aplicacoes.mds.gov.br/sagi/servicos/misocial>. Retrieves monthly municipal series of the 'Cadastro Unico' (registered families and people by income bracket), the 'Bolsa Familia' programme (beneficiary families, benefits and transfers), the 'BPC' benefit, the social assistance network ('CRAS', 'CREAS' and other units), 'SUAS' funding, 'Bolsa Familia' conditionality monitoring and food security programmes as tidy long-format 'tibbles'.
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)