Dados da Atenção Primária a Saúde

A Atenção Primária à Saúde (APS) é a principal porta de entrada do SUS e produz continuamente informações importantes sobre atendimentos, visitas, procedimentos, problemas avaliados e condições acompanhadas nos municípios brasileiros.

Esta base organiza dados do SISAB/SIAPS, obtidos nos relatórios de produção e por pedidos via LAI, em arquivos abertos, documentados e adequados para análises reprodutíveis. O objetivo é facilitar o uso de séries municipais mensais da APS, reduzindo a dependência de consultas manuais em relatórios web e reunindo dados complementares obtidos por transparência passiva.

Fontes de dados

Os dados combinam duas fontes relacionadas ao SISAB/SIAPS. A primeira fonte é o site de relatórios públicos do SISAB, em especial o relatório de Saúde: Atendimento/Visita. Esses relatórios permitem consultar informações agregadas de produção, procedimentos e problemas ou condições avaliadas por município e competência.

A segunda fonte são dados obtidos via Lei de Acesso à Informação (LAI). Esses pedidos são necessários porque as contagens de atendimentos por códigos CID-10 e CIAP-2 não estão disponíveis, atualmente, em transparência ativa no SISAB/SIAPS e são de difícil obtenção nos relatórios públicos do SISAB. As respostas recebidas via LAI são usadas para organizar séries mensais de atendimentos por município, competência e código diagnóstico ou de motivo de consulta.

Método

Os dados públicos do SISAB são extraídos com o repositório sisab_scrapper, que automatiza a coleta mensal dos relatórios de produção, procedimentos e problemas/condições avaliadas. Para cada competência, tipo de relatório, faixa etária e sexo, o fluxo baixa o relatório municipal do SISAB para todo o Brasil. Os resultados são combinados em arquivos tabulares anuais nos formatos CSV compactado e Parquet. As bases públicas têm as variáveis competencia, uf, ibge, municipio, faixa_etaria, sexo, uma variável de categoria do relatório e valor.

Os dados recebidos via LAI são processados com o repositório sisab_lai. O fluxo identifica arquivos válidos, resolve sobreposições entre pedidos, padroniza diferenças de esquema entre respostas, preserva informações de proveniência e exporta arquivos anuais em CSV compactado e Parquet. Esses arquivos contêm contagens mensais por município, tipo de código (CID ou CIAP), código e quantidade de atendimentos.

As atualizações serão mensais. Novas competências serão incorporadas conforme forem disponibilizadas no site do SISAB e novos pedidos mensais via LAI serão feitos até que o SISAB/SIAPS ofereça transparência ativa para os dados de atendimentos por CID-10 e CIAP-2.

Acesso aos dados

Os dados estão disponibilizados no Zenodo. Os depósitos estão organizados por ano ou período e contêm arquivos nos formatos CSV compactado e Parquet.

Período Depósito Zenodo
2014-2016 10.5281/zenodo.20597219
2017 10.5281/zenodo.20594700
2018 10.5281/zenodo.20595666
2019 10.5281/zenodo.20595800
2020 10.5281/zenodo.20595848
2021 10.5281/zenodo.20595931
2022 10.5281/zenodo.20596062
2023 10.5281/zenodo.20596123
2024 10.5281/zenodo.20597035
2025 10.5281/zenodo.20597086
2026-01 a 2026-04 10.5281/zenodo.20597157

Exemplo de análise em R

O exemplo abaixo usa os arquivos Parquet de produção da APS (sisab_saude_producao_YYYY.parquet). Eles são baixados com o pacote {zendown}, lidos com {arrow} e analisados com {dplyr}.

library(arrow)
library(brpop)
library(dplyr)
library(geobr)
library(ggplot2)
library(knitr)
library(purrr)
library(sf)
library(tidyr)
library(zendown)

depositos_producao <- tibble::tribble(
  ~ano , ~deposit_id ,
  2014 ,    20597219 ,
  2015 ,    20597219 ,
  2016 ,    20597219 ,
  2017 ,    20594700 ,
  2018 ,    20595666 ,
  2019 ,    20595800 ,
  2020 ,    20595848 ,
  2021 ,    20595931 ,
  2022 ,    20596062 ,
  2023 ,    20596123 ,
  2024 ,    20597035 ,
  2025 ,    20597086 ,
  2026 ,    20597157
) |>
  mutate(file_name = paste0("sisab_saude_producao_", ano, ".parquet"))

arquivos_producao <- depositos_producao |>
  mutate(
    path = map2_chr(
      deposit_id,
      file_name,
      ~ zen_file(deposit_id = .x, file_name = .y)
    )
  )

depositos_lai <- tibble::tribble(
  ~ano , ~deposit_id ,
  2017 ,    20594700 ,
  2018 ,    20595666 ,
  2019 ,    20595800 ,
  2020 ,    20595848 ,
  2021 ,    20595931 ,
  2022 ,    20596062 ,
  2023 ,    20596123 ,
  2024 ,    20597035 ,
  2025 ,    20597086 ,
  2026 ,    20597157
) |>
  mutate(file_name = paste0("sisab_saude_ciap_cid_", ano, ".parquet"))

arquivos_lai <- depositos_lai |>
  mutate(
    path = map2_chr(
      deposit_id,
      file_name,
      ~ zen_file(deposit_id = .x, file_name = .y)
    )
  )

Tabela

Esta primeira tabela resume os maiores tipos de produção registrados em 2025.

producao_2025 <- read_parquet(
  arquivos_producao$path[arquivos_producao$ano == 2025]
)

tabela_tipo_producao <- producao_2025 |>
  group_by(tipo_producao) |>
  summarise(total = sum(valor, na.rm = TRUE), .groups = "drop") |>
  arrange(desc(total)) |>
  slice_head(n = 10) |>
  mutate(total = format(total, big.mark = ".", decimal.mark = ","))

kable(
  tabela_tipo_producao,
  col.names = c("Tipo de produção", "Total em 2025")
)
Tipo de produção Total em 2025
Visita Domiciliar 777.683.286
Procedimento 703.182.832
Atendimento Individual 414.480.295
Atendimento Odontológico 56.863.372

Evolução temporal

A série temporal agrega todos os municípios, faixas etárias, sexos e tipos de produção por competência mensal.

serie_temporal <- arquivos_producao$path |>
  map_dfr(\(path) {
    read_parquet(path) |>
      group_by(competencia) |>
      summarise(total = sum(valor, na.rm = TRUE), .groups = "drop")
  }) |>
  mutate(
    competencia = as.character(competencia),
    data = as.Date(paste0(competencia, "01"), format = "%Y%m%d")
  ) |>
  arrange(data)

ggplot(serie_temporal, aes(x = data, y = total)) +
  geom_line(linewidth = 0.8, color = "#15616d") +
  scale_y_continuous(
    labels = scales::label_number(big.mark = ".", decimal.mark = ",")
  ) +
  labs(
    title = "Produção mensal da Atenção Primária à Saúde no Brasil",
    x = "Competência",
    y = "Total registrado"
  ) +
  theme_bw()

Atendimentos por dor de ouvidos em São Paulo

Os dados recebidos via LAI permitem analisar atendimentos por códigos específicos de CID-10 ou CIAP-2. O exemplo abaixo usa o código CIAP-2 H01, referente a dor de ouvidos, no município de São Paulo em 2025. Nos arquivos da LAI, a variável co_municipio_ibge usa o código municipal com 6 dígitos; por isso, São Paulo aparece como 355030.

arquivo_lai_2025 <- arquivos_lai$path[arquivos_lai$ano == 2025]

atendimentos_h01_sp_2025 <- open_dataset(arquivo_lai_2025) |>
  filter(
    co_municipio_ibge == "355030",
    tp_codigo == "CIAP",
    codigo == "H01"
  ) |>
  group_by(competencia, competencia_date) |>
  summarise(
    atendimentos = sum(qt_atendimentos, na.rm = TRUE),
    .groups = "drop"
  ) |>
  collect() |>
  complete(
    competencia = sprintf("2025%02d", 1:12),
    fill = list(atendimentos = 0)
  ) |>
  mutate(
    competencia_date = as.Date(paste0(competencia, "01"), format = "%Y%m%d")
  ) |>
  arrange(competencia)

resumo_h01_sp_2025 <- tibble::tibble(
  indicador = c(
    "Total de atendimentos em 2025",
    "Mês com maior quantidade",
    "Mês com menor quantidade"
  ),
  valor = c(
    format(
      sum(atendimentos_h01_sp_2025$atendimentos),
      big.mark = ".",
      decimal.mark = ","
    ),
    format(
      atendimentos_h01_sp_2025$atendimentos[
        which.max(atendimentos_h01_sp_2025$atendimentos)
      ],
      big.mark = ".",
      decimal.mark = ","
    ),
    format(
      atendimentos_h01_sp_2025$atendimentos[
        which.min(atendimentos_h01_sp_2025$atendimentos)
      ],
      big.mark = ".",
      decimal.mark = ","
    )
  ),
  competencia = c(
    "2025",
    atendimentos_h01_sp_2025$competencia[
      which.max(atendimentos_h01_sp_2025$atendimentos)
    ],
    atendimentos_h01_sp_2025$competencia[
      which.min(atendimentos_h01_sp_2025$atendimentos)
    ]
  )
)

kable(
  resumo_h01_sp_2025,
  col.names = c("Indicador", "Valor", "Competência")
)
Indicador Valor Competência
Total de atendimentos em 2025 27.509 2025
Mês com maior quantidade 2.792 202509
Mês com menor quantidade 1.557 202501
ggplot(atendimentos_h01_sp_2025, aes(x = competencia_date, y = atendimentos)) +
  geom_col(fill = "#7a4f9a") +
  geom_line(color = "#2f4858", linewidth = 0.8) +
  geom_point(color = "#2f4858", size = 2) +
  scale_x_date(date_breaks = "1 month", date_labels = "%b/%Y") +
  scale_y_continuous(
    labels = scales::label_number(big.mark = ".", decimal.mark = ",")
  ) +
  labs(
    title = "Atendimentos por dor de ouvidos (CIAP-2 H01) em São Paulo",
    subtitle = "Dados obtidos via LAI, 2025",
    x = "Competência",
    y = "Atendimentos"
  ) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Classificação da adesão ao e-SUS AB

A quantidade de atendimentos individuais registrada no relatório público de produção da APS também pode ser usada como um indicador indireto de adesão ou intensidade de registro no e-SUS AB. Nesta análise, os registros de Atendimento Individual em 2025 foram agregados por município e divididos pela população municipal de 2024 do pacote {brpop}. O resultado é uma taxa de atendimentos individuais por 100 habitantes. Essa medida não representa adesão formal ao sistema nem cobertura assistencial, mas ajuda a identificar municípios com registro proporcionalmente baixo, intermediário ou alto nos dados enviados ao SISAB.

A classificação abaixo usa quatro grupos: municípios sem atendimentos individuais registrados em 2025 são classificados como Sem registro; os demais são divididos em tercis da taxa de atendimentos individuais por 100 habitantes, formando as classes Baixa, Intermediária e Alta.

uf_lut <- tibble::tribble(
  ~uf_code , ~uf  ,
        11 , "RO" ,
        12 , "AC" ,
        13 , "AM" ,
        14 , "RR" ,
        15 , "PA" ,
        16 , "AP" ,
        17 , "TO" ,
        21 , "MA" ,
        22 , "PI" ,
        23 , "CE" ,
        24 , "RN" ,
        25 , "PB" ,
        26 , "PE" ,
        27 , "AL" ,
        28 , "SE" ,
        29 , "BA" ,
        31 , "MG" ,
        32 , "ES" ,
        33 , "RJ" ,
        35 , "SP" ,
        41 , "PR" ,
        42 , "SC" ,
        43 , "RS" ,
        50 , "MS" ,
        51 , "MT" ,
        52 , "GO" ,
        53 , "DF"
)

populacao_municipal_2024 <- mun_pop_totals(source = "datasus2024") |>
  filter(year == 2024) |>
  transmute(
    co_municipio_ibge = substr(sprintf("%07.0f", code_muni), 1, 6),
    populacao_2024 = as.numeric(pop)
  )

atendimentos_relatorio_municipio_2025 <- producao_2025 |>
  filter(tipo_producao == "Atendimento Individual") |>
  mutate(co_municipio_ibge = sprintf("%06.0f", as.numeric(ibge))) |>
  group_by(co_municipio_ibge) |>
  summarise(
    atendimentos_relatorio = sum(valor, na.rm = TRUE),
    .groups = "drop"
  )

adesao_esus_ab_2025 <- populacao_municipal_2024 |>
  left_join(atendimentos_relatorio_municipio_2025, by = "co_municipio_ibge") |>
  mutate(
    atendimentos_relatorio = replace_na(atendimentos_relatorio, 0L),
    atendimentos_100hab = 100 * atendimentos_relatorio / populacao_2024,
    uf_code = as.integer(substr(co_municipio_ibge, 1, 2))
  ) |>
  left_join(uf_lut, by = "uf_code")

cortes_adesao <- quantile(
  adesao_esus_ab_2025$atendimentos_100hab[
    adesao_esus_ab_2025$atendimentos_relatorio > 0
  ],
  probs = c(1 / 3, 2 / 3),
  na.rm = TRUE,
  names = FALSE
)

adesao_esus_ab_2025 <- adesao_esus_ab_2025 |>
  mutate(
    classificacao_adesao = case_when(
      atendimentos_relatorio == 0 ~ "Sem registro",
      atendimentos_100hab < cortes_adesao[1] ~ "Baixa",
      atendimentos_100hab < cortes_adesao[2] ~ "Intermediária",
      TRUE ~ "Alta"
    ),
    classificacao_adesao = factor(
      classificacao_adesao,
      levels = c("Sem registro", "Baixa", "Intermediária", "Alta")
    )
  )

resumo_adesao_nacional <- adesao_esus_ab_2025 |>
  count(classificacao_adesao, name = "municipios") |>
  mutate(
    percentual = 100 * municipios / sum(municipios),
    percentual = round(percentual, 1)
  )

kable(
  resumo_adesao_nacional,
  col.names = c("Classificação", "Municípios", "%")
)
Classificação Municípios %
Baixa 1857 33.3
Intermediária 1856 33.3
Alta 1857 33.3

Os pontos de corte usados para a classificação foram 215.6 e 365 atendimentos individuais por 100 habitantes. Em 2025, a taxa mediana municipal foi de 279 atendimentos individuais por 100 habitantes.

resumo_adesao_uf <- adesao_esus_ab_2025 |>
  group_by(uf) |>
  summarise(
    municipios = n(),
    mediana_atendimentos_100hab = median(atendimentos_100hab, na.rm = TRUE),
    percentual_alta = 100 * mean(classificacao_adesao == "Alta"),
    percentual_sem_registro = 100 *
      mean(classificacao_adesao == "Sem registro"),
    .groups = "drop"
  ) |>
  arrange(desc(mediana_atendimentos_100hab)) |>
  mutate(
    mediana_atendimentos_100hab = round(mediana_atendimentos_100hab, 1),
    percentual_alta = round(percentual_alta, 1),
    percentual_sem_registro = round(percentual_sem_registro, 1)
  )

kable(
  resumo_adesao_uf,
  col.names = c(
    "UF",
    "Municípios",
    "Mediana por 100 hab.",
    "% alta",
    "% sem registro"
  )
)
UF Municípios Mediana por 100 hab. % alta % sem registro
SC 295 510.6 73.2 0
RS 497 471.6 67.2 0
PR 399 377.1 52.6 0
SP 645 334.1 47.0 0
MG 853 333.9 44.1 0
GO 246 331.0 41.5 0
TO 139 329.9 39.6 0
RN 167 302.6 24.0 0
PB 223 294.4 26.5 0
MT 141 275.6 27.7 0
MS 79 274.2 20.3 0
CE 184 254.3 13.6 0
AL 102 239.2 13.7 0
PI 224 238.0 13.8 0
ES 78 234.1 14.1 0
PE 185 203.9 2.7 0
SE 75 192.9 8.0 0
AM 62 192.1 4.8 0
RJ 92 165.0 1.1 0
RO 52 163.9 0.0 0
BA 417 160.1 1.9 0
RR 15 153.8 0.0 0
MA 217 146.3 0.9 0
AC 22 139.9 0.0 0
AP 16 137.6 0.0 0
PA 144 124.6 0.7 0
DF 1 120.4 0.0 0
ggplot(
  resumo_adesao_uf,
  aes(
    x = reorder(uf, mediana_atendimentos_100hab),
    y = mediana_atendimentos_100hab
  )
) +
  geom_col(fill = "#2f4858") +
  coord_flip() +
  labs(
    title = "Mediana municipal de atendimentos individuais por 100 habitantes",
    subtitle = "Relatório de produção da APS 2025 e população municipal de 2024 do brpop",
    x = "UF",
    y = "Atendimentos individuais por 100 habitantes"
  ) +
  theme_bw()

A tabela abaixo mostra os municípios com maiores taxas proporcionais. Valores muito altos podem ocorrer em municípios pequenos e devem ser lidos como sinal de forte intensidade de registro no SISAB, necessidade de auditoria da série local ou possível concentração de registros, e não como medida direta de acesso individual à APS.

top_adesao_municipios <- adesao_esus_ab_2025 |>
  arrange(desc(atendimentos_100hab)) |>
  transmute(
    co_municipio_ibge,
    uf,
    populacao_2024 = format(
      round(populacao_2024),
      big.mark = ".",
      decimal.mark = ","
    ),
    atendimentos_relatorio = format(
      atendimentos_relatorio,
      big.mark = ".",
      decimal.mark = ","
    ),
    atendimentos_100hab = round(atendimentos_100hab, 1),
    classificacao_adesao
  ) |>
  slice_head(n = 10)

kable(
  top_adesao_municipios,
  col.names = c(
    "Município IBGE",
    "UF",
    "População 2024",
    "Atendimentos individuais 2025",
    "Atendimentos por 100 hab.",
    "Classificação"
  )
)
Município IBGE UF População 2024 Atendimentos individuais 2025 Atendimentos por 100 hab. Classificação
351650 SP 2.811 62.971 2240.2 Alta
351010 SP 2.951 62.236 2109.0 Alta
355310 SP 5.751 118.386 2058.5 Alta
355570 SP 1.628 31.251 1919.6 Alta
351565 SP 1.689 31.574 1869.4 Alta
421505 SC 2.432 45.168 1857.2 Alta
353100 SP 1.954 35.841 1834.2 Alta
310070 MG 2.165 39.641 1831.0 Alta
350720 SP 928 16.922 1823.5 Alta
354030 SP 2.415 43.385 1796.5 Alta

Limitações

As contagens são registros administrativos agregados e devem ser interpretadas considerando práticas de registro do SISAB, cobertura municipal, mudanças nos sistemas de informação e possíveis alterações nas classificações usadas ao longo do tempo.

Para os dados via LAI, as colunas de proveniência preservam o pedido e o arquivo-fonte selecionado para cada competência. Isso ajuda a auditar as escolhas feitas quando há arquivos sobrepostos ou respostas com diferenças de esquema.

De volta ao topo