Download e FTP

baixar

caminho  = baixar(:sim, "PE"; ano = 2023)              # um arquivo
caminhos = baixar(:sim, "PE"; anos = 2013:2023)        # vários, em paralelo
caminhos = baixar(:sih, "PE"; anos = [2023], meses = 1:12)
  • Cache local via Scratch.jl: chamadas repetidas devolvem o caminho já baixado sem tocar na rede. forcar = true ignora o cache; quieto = true silencia os @info.
  • A forma plural baixa em paralelo (asyncmap, 4 conexões) e devolve os caminhos na ordem dos períodos.
  • Downloads interrompidos não poluem o cache (grava em .part + mv atômico).
  • MicroSUS.limpar_cache() apaga tudo.

Caminhos no FTP

Conferidos contra o microdatasus (jul/2026) — o FTP do DATASUS já mudou de estrutura outras vezes, e é a primeira coisa a checar quando um download falha com 550:

sistemapastaarquivo
:simSIM/CID10/DORES/DO{UF}{aaaa}.dbc
:sinascSINASC/1996_/Dados/DNRES/DN{UF}{aaaa}.dbc
:sihSIHSUS/200801_/Dados/RD{UF}{aamm}.dbc
:siaSIASUS/200801_/Dados/PA{UF}{aamm}.dbc
:cnesCNES/200508_/Dados/ST/ST{UF}{aamm}.dbc

As demais fontes de fontes seguem os mesmos padrões: os recortes nacionais do SIM em SIM/CID10/DOFET/ (DOFET{aa}, DOINF{aa}, DOEXT{aa}, DOMAT{aa}), os outros arquivos da AIH na pasta do RD (SP, RJ, ER) e cada tabela do CNES na sua pasta (CNES/200508_/Dados/LT/LT{UF}{aamm}.dbc e afins).

url_arquivo monta a URL sem baixar:

url_arquivo(:sinasc, "BA"; ano = 2022)
# "ftp://ftp.datasus.gov.br/dissemin/publicos/SINASC/1996_/Dados/DNRES/DNBA2022.dbc"

SINAN (agravos de notificação)

Dengue, chikungunya, zika, tuberculose, hanseníase e outros agravos vêm do SINAN, cujos arquivos são nacionais (um .dbc cobre o Brasil inteiro) — por isso a API é por agravo, não por UF. Filtre pela UF de residência no leitor.

caminho  = baixar_sinan(:dengue; ano = 2020)      # DENGBR20.dbc, nacional
caminhos = baixar_sinan(:zika; anos = 2016:2020)  # vários anos, em paralelo

# só Pernambuco, filtrando no leitor (SG_UF = UF de residência)
pe = DataFrame(ler(caminho; filtro = r -> strip(r[:SG_UF]) == "26"))

Os agravos disponíveis estão na tabela abaixo e em agravos_sinan(). O schema :sinan tipa o núcleo comum das fichas de notificação (datas, localidade, NU_IDADE_N → anos, CLASSI_FIN, CRITERIO, EVOLUCAO); os campos específicos de cada agravo caem na tipagem do DBF.

Como o SINAN fecha com atraso, baixar_sinan tenta FINAIS/ e cai automaticamente em PRELIM/ quando o consolidado não existe.

Agravos disponíveis

Cobertura verificada no FTP do DATASUS em 03/10/2026. "Consolidado até" é o último ano em FINAIS/; os seguintes só existem em PRELIM/ (ver Dados preliminares). Um ano que falta no meio da série é pulado por fetch_datasus com um aviso.

agravofetch_datasusarquivoanos no FTPobservação
:acidente_biologico — Acidente de trabalho com exposição a material biológico:SINAN_ACIDENTE_BIOLOGICOACBIBR{aa}2006–2025consolidado até 2022
:acidente_trabalho — Acidente de trabalho grave:SINAN_ACIDENTE_TRABALHOACGRBR{aa}2006–2025consolidado até 2022
:acidente_animais — Acidente por animais peçonhentos:SINAN_ACIDENTE_ANIMAISANIMBR{aa}2007–2025consolidado até 2022
:atendimento_antirrabico — Atendimento antirrábico:SINAN_ATENDIMENTO_ANTIRRABICOANTRBR{aa}2006–2025consolidado até 2024
:botulismo — Botulismo:SINAN_BOTULISMOBOTUBR{aa}2007–2025consolidado até 2024
:cancer_trabalho — Câncer relacionado ao trabalho:SINAN_CANCER_TRABALHOCANCBR{aa}2007–2025consolidado até 2022
:chagas — Doença de Chagas aguda:SINAN_CHAGASCHAGBR{aa}2000–2025consolidado até 2022
:chikungunya — Chikungunya:SINAN_CHIKUNGUNYACHIKBR{aa}2014–2025
:colera — Cólera:SINAN_COLERACOLEBR{aa}2007–2025sem 2023; consolidado até 2022
:coqueluche — Coqueluche:SINAN_COQUELUCHECOQUBR{aa}2007–2025consolidado até 2022
:dengue — Dengue:SINAN_DENGUEDENGBR{aa}2000–2025
:dermatoses_ocupacionais — Dermatoses ocupacionais:SINAN_DERMATOSES_OCUPACIONAISDERMBR{aa}2006–2025consolidado até 2022
:difteria — Difteria:SINAN_DIFTERIADIFTBR{aa}2007–2025consolidado até 2023
:esquistossomose — Esquistossomose:SINAN_ESQUISTOSSOMOSEESQUBR{aa}2007–2025consolidado até 2022
:exantematicas — Doenças exantemáticas (sarampo e rubéola):SINAN_EXANTEMATICASEXANBR{aa}2007–2025série inteira só em PRELIM/
:febre_maculosa — Febre maculosa:SINAN_FEBRE_MACULOSAFMACBR{aa}2007–2025consolidado até 2021
:febre_tifoide — Febre tifoide:SINAN_FEBRE_TIFOIDEFTIFBR{aa}2007–2025consolidado até 2024
:hanseniase — Hanseníase:SINAN_HANSENIASEHANSBR{aa}2001–2025consolidado até 2023
:hantavirose — Hantavirose:SINAN_HANTAVIROSEHANTBR{aa}2000–2025consolidado até 2024
:hepatites — Hepatites virais:SINAN_HEPATITESHEPABR{aa}2007–2023série inteira só em PRELIM/
:intoxicacao_exogena — Intoxicação exógena:SINAN_INTOXICACAO_EXOGENAIEXOBR{aa}2006–2025consolidado até 2022
:leishmaniose_visceral — Leishmaniose visceral:SINAN_LEISHMANIOSE_VISCERALLEIVBR{aa}2000–2025
:leptospirose — Leptospirose:SINAN_LEPTOSPIROSELEPTBR{aa}2000–2025consolidado até 2024
:ler_dort — LER/DORT:SINAN_LER_DORTLERDBR{aa}2006–2025consolidado até 2022
:leishmaniose_tegumentar — Leishmaniose tegumentar americana:SINAN_LEISHMANIOSE_TEGUMENTARLTANBR{aa}2000–2025
:malaria — Malária:SINAN_MALARIAMALABR{aa}2004–2025consolidado até 2023
:meningite — Meningite:SINAN_MENINGITEMENIBR{aa}2007–2024consolidado até 2022
:transtornos_mentais_trabalho — Transtornos mentais relacionados ao trabalho:SINAN_TRANSTORNOS_MENTAIS_TRABALHOMENTBR{aa}2006–2025consolidado até 2022
:tracoma — Tracoma (notificação):SINAN_TRACOMANTRABR{aa}2010–2025consolidado até 2024
:pair — Perda auditiva induzida por ruído relacionada ao trabalho:SINAN_PAIRPAIRBR{aa}2006–2025consolidado até 2022
:peste — Peste:SINAN_PESTEPESTBR{aa}2007–2025consolidado até 2024
:paralisia_flacida — Paralisia flácida aguda:SINAN_PARALISIA_FLACIDAPFANBR{aa}2007–2025consolidado até 2022
:pneumoconioses — Pneumoconioses relacionadas ao trabalho:SINAN_PNEUMOCONIOSESPNEUBR{aa}2006–2025consolidado até 2022
:raiva — Raiva humana:SINAN_RAIVARAIVBR{aa}2007–2025consolidado até 2024
:surto_dta — Surtos de doenças transmitidas por alimentos:SINAN_SURTO_DTASDTABR{aa}2007–2024sem 2014; consolidado até 2018
:sifilis_adquirida — Sífilis adquirida:SINAN_SIFILIS_ADQUIRIDASIFABR{aa}2010–2024série inteira só em PRELIM/
:sifilis_congenita — Sífilis congênita:SINAN_SIFILIS_CONGENITASIFCBR{aa}2007–2025série inteira só em PRELIM/
:sifilis_gestante — Sífilis em gestante:SINAN_SIFILIS_GESTANTESIFGBR{aa}2007–2025série inteira só em PRELIM/
:rubeola_congenita — Síndrome da rubéola congênita:SINAN_RUBEOLA_CONGENITASRCBR{aa}2007–2025série inteira só em PRELIM/
:tetano_acidental — Tétano acidental:SINAN_TETANO_ACIDENTALTETABR{aa}2007–2025consolidado até 2023
:tetano_neonatal — Tétano neonatal:SINAN_TETANO_NEONATALTETNBR{aa}2014–2025consolidado até 2021
:toxoplasmose_congenita — Toxoplasmose congênita:SINAN_TOXOPLASMOSE_CONGENITATOXCBR{aa}2019–2025consolidado até 2023
:toxoplasmose_gestacional — Toxoplasmose gestacional:SINAN_TOXOPLASMOSE_GESTACIONALTOXGBR{aa}2019–2025consolidado até 2023
:tracoma_inquerito — Tracoma (inquérito):SINAN_TRACOMA_INQUERITOTRACBR{aa}2009–2025consolidado até 2024
:tuberculose — Tuberculose:SINAN_TUBERCULOSETUBEBR{aa}2001–2025consolidado até 2019
:varicela — Varicela:SINAN_VARICELAVARCBR{aa}2007–2025sem 2020; série inteira só em PRELIM/
:violencia — Violência interpessoal/autoprovocada:SINAN_VIOLENCIAVIOLBR{aa}2009–2025consolidado até 2024
:zika — Zika:SINAN_ZIKAZIKABR{aa}2015–2025

O cache está em dia com o DATASUS?

O DATASUS republica bases retroativamente, sem aviso, e o cache não sabe disso: o arquivo baixado em julho continua sendo usado depois que o DATASUS o substitui. verificar_cache() compara cada arquivo do cache com o FTP, sem baixar nada, e diz o que fazer:

using DataFrames
v = DataFrame(verificar_cache())
filter(r -> r.situacao in (:mudou, :era_preliminar), v)
situaçãoo que éo que fazer
:atualizadoo FTP tem o mesmo arquivonada
:mudouo DATASUS republicouforcar = true / cache = false
:era_preliminarpreliminar guardado como definitivo por versões até a 0.3.1forcar = true
:consolidado_disponivelpreliminar no cache e consolidado já publicadoo próximo fetch_datasus troca sozinho
:ausente_no_ftpnenhuma URL candidata existe mais—
:sem_url / :sem_respostanome fora do catálogo / servidor não respondeu—

A comparação é por tamanho — o FTP não informa a data de modificação por essa via —, então uma republicação com exatamente o mesmo número de bytes passa como :atualizado. A consulta usa só o canal de controle do FTP, que responde mesmo onde o firewall bloqueia as transferências, e vai de dois em dois arquivos: mais que isso, e o servidor do DATASUS passa a deixar conexões sem resposta.

De onde veio este resultado?

Cada download grava, ao lado do arquivo, um registro .origem com a URL, a data, o tamanho e o SHA-256. O fetch_datasus anexa ao resultado a lista dos arquivos de que ele veio:

df = fetch_datasus(:SIM_DO; uf = "PE", anos = 2019:2023)
DataFrame(proveniencia(df))   # arquivo, url, baixado_em, bytes, sha256, preliminar, obtido_de

É o que uma nota de método precisa para que a análise possa ser refeita sobre os mesmos dados: a URL sozinha não identifica a versão de um arquivo que o DATASUS republica. A proveniência acompanha o DataFrame em cópias, filtros e agregações. Para arquivos baixados antes da 0.4.1, baixado_em é a data do arquivo no cache.

Travar os dados de uma análise

A proveniência diz de onde os dados vieram; travar_dados guarda isso num arquivo para refazer a análise sobre os mesmos bytes — o Manifest.toml dos dados. Versione-o junto com o script:

df = fetch_datasus(:SIM_DO; uf = "PE", anos = 2019:2023)
travar_dados("dados.toml", df)          # um ou mais resultados
[[arquivo]]
baixado_em = 2026-07-08T23:05:41.719
bytes = 5779997
nome = "DOPE2023.dbc"
preliminar = false
sha256 = "d5b5600d544cd82a7dfb13663b21c68ccfc004680d50c8dec72a26f97d219451"
url = "ftp://ftp.datasus.gov.br/dissemin/publicos/SIM/CID10/DORES/DOPE2023.dbc"

Meses depois, ou em outra máquina, restaurar_dados põe no cache exatamente esses arquivos, conferindo cada um pelo SHA-256, e ativa a trava na sessão — o mesmo script lê os mesmos dados:

restaurar_dados("dados.toml")
df = fetch_datasus(:SIM_DO; uf = "PE", anos = 2019:2023)   # os bytes travados

Com a trava ativa, um arquivo travado vem sempre do cache: nem o FTP, nem o consolidado que saiu depois de um preliminar travado, tomam o lugar dele. soltar_dados desativa.

O DATASUS não guarda versões antigas: se ele republicou um arquivo, a versão travada só volta de um espelho que a tenha guardado (MICROSUS_ESPELHOS, ver abaixo) — restaurar_dados procura em cada origem a que tem o mesmo hash. Se nenhuma tem, é erro: a análise não roda sobre outros dados sem que você decida (estrito = false segue com a versão atual e diz quais arquivos mudaram). Guardar o cache de uma análise publicada com exportar_espelho é o que garante que ela possa ser refeita.

O cache fica no diretório de Scratch do pacote; a variável de ambiente MICROSUS_CACHE o troca por outro (outro disco, um cache por projeto).

Falha de rede não é arquivo ausente

Um arquivo que não existe no FTP (ano ainda não publicado para uma UF, partição PA...b que não houve) é pulado com um @warn. Uma falha de rede — timeout, DNS, conexão recusada, canal de dados do FTP bloqueado por firewall — é outra coisa e interrompe com MicroSUS.ErroDeRede: seguir daria um resultado incompleto que parece completo.

try
    fetch_datasus(:SIM_DO; uf = :all, anos = 2023)
catch e
    e isa MicroSUS.ErroDeRede || rethrow()
    # sem acesso ao FTP: e.url diz qual arquivo, e.causa traz o erro original
end

Downloads interrompidos são retomados

O FTP do DATASUS derruba transferências no meio. Um download que cai depois de ter avançado continua de onde parou — até cinco vezes na mesma chamada —, e o que já veio fica no cache como ARQUIVO.dbc.parcial para a próxima chamada retomar. Antes de retomar, o tamanho do arquivo no servidor é conferido: se mudou (o DATASUS republicou), o parcial é descartado. Nada disso pede configuração; o arquivo só aparece no cache inteiro.

Espelhos: quando o FTP não responde

O DATASUS publica só por FTP, e há redes — e dias — em que ele não entrega nada. MICROSUS_ESPELHOS aponta para outras origens com a mesma árvore de pastas do FTP (<espelho>/SIM/CID10/DORES/DOPE2023.dbc), separadas por ;: uma pasta de rede (file://), um bucket, um servidor HTTP do grupo.

# quem tem os arquivos publica o seu cache na árvore do FTP
exportar_espelho("/mnt/grupo/datasus")

# quem não alcança o FTP aponta para lá
ENV["MICROSUS_ESPELHOS"] = "file:///mnt/grupo/datasus"
df = fetch_datasus(:SIM_DO; uf = "PE", anos = 2023)
proveniencia(df)    # obtido_de diz de qual espelho cada arquivo veio

Por padrão o espelho só entra quando o DATASUS falha por rede; com MICROSUS_ESPELHO_PRIMEIRO=true, é tentado antes dele (um espelho institucional mais rápido). Em qualquer caso o DATASUS decide se um arquivo existe: se ele responde que não, o espelho não é consultado — é disso que dependem as partições do SIA e o PRELIM. Um espelho não é conferido contra o DATASUS; o SHA-256 em proveniencia é o que permite verificar que os bytes são os mesmos.

A exceção é o preliminar já no cache: sem rede, ele é usado, com um aviso que diz que foi por falta de rede.

Dados preliminares

SIM e SINASC dos anos recentes ficam em PRELIM/ até a consolidação (historicamente ~18 meses). Se o consolidado não existir, baixar tenta automaticamente a pasta preliminar, com um @warn — um indicador calculado sobre dados preliminares merece um asterisco.

baixar(:sinasc, "PE"; ano = 2025)
# ┌ Warning: não achei o consolidado; tentando dados PRELIMINARES
# └   url = ".../SINASC/PRELIM/DNRES/DNPE2025.dbc"

url_arquivo(:sim, "PE"; ano = 2025, prelim = true)   # URL direta

Se as duas falharem, o erro relançado é o da URL principal (consolidada).

O preliminar fica no cache numa subpasta PRELIM/, separado do consolidado de mesmo nome. Duas consequências:

  • dá para saber de onde veio cada arquivo: eh_preliminar(caminho); o show de ler(caminho) avisa; e fetch_datasus acrescenta a coluna PRELIMINAR e lista, num @warn, os arquivos preliminares do resultado;
  • o consolidado substitui o preliminar quando sai: o consolidado é sempre tentado primeiro, mesmo com o preliminar em cache. O custo é uma tentativa de rede por chamada enquanto o ano não consolida; sem rede, o preliminar do cache é usado (com aviso).

O próprio preliminar também muda — o DATASUS o republica até consolidar —, e o do cache não é atualizado sozinho: o aviso diz quando ele foi baixado, e forcar = true (ou cache = false no fetch_datasus) o rebaixa.

c = baixar(:sim, "PE"; ano = 2025)
eh_preliminar(c)                                       # true

df = fetch_datasus(:SIM_DO; uf = "PE", anos = 2023:2025)
combine(groupby(df, :ANO_ARQUIVO), :PRELIMINAR => first)
Cache de versões anteriores

Até a 0.3.1 o preliminar era guardado no mesmo lugar do consolidado, com o mesmo nome — e, uma vez no cache, era devolvido para sempre como se fosse definitivo. Um cache montado por essas versões pode ter preliminares antigos na raiz; rebaixe os anos recentes com forcar = true (ou rode MicroSUS.limpar_cache()).

Limites de cobertura

  • SINASC: o helper cobre 1996+ (estrutura 1996_/Dados); 1994–1995 ficam em SINASC/1994_1995/, com outro padrão de nomes — monte a URL manualmente e use ler no arquivo baixado.
  • SIH/SIA: baixar e url_arquivo cobrem a estrutura pós-2008 (200801_); fetch_datasus alcança também as pastas de 1992–2007 / 1994–2007, cujos layouts têm diferenças próprias.
  • CNES: baixar(:cnes, ...) e url_arquivo(:cnes, ...) são do ST (estabelecimentos); as outras tabelas (:CNES_PF, :CNES_LT, :CNES_EQ, …) vêm por fetch_datasus.

Paralelizar a leitura entre arquivos

O DCL é sequencial por natureza (cada byte depende do histórico), então não há paralelismo dentro de um arquivo. O padrão é paralelizar entre arquivos:

caminhos = baixar(:sinasc, "PE"; anos = 2019:2023, quieto = true)
partes = asyncmap(caminhos; ntasks = Threads.nthreads()) do c
    materializar(ler(c; colunas = [:DTNASC, :CODMUNRES]))
end