Filas de análise da ANVISA como feeds Atom
Vitor Ayres (abre em nova aba)
A ANVISA publica a fila de análise de petições de cada área: a ordem em que os processos esperam análise. A consulta mostra a fila de hoje. Ela não avisa quando um processo entra, sai ou muda de posição, e não guarda a fila de ontem.
Este texto registra como transformamos essa consulta em um feed Atom por subfila, publicado todo dia sem nenhum servidor, e o que 32 dias de coleta mostraram. Os números são das coletas entre 6 de setembro e 8 de outubro de 2026.
Resumo
- São 319 subfilas, uma requisição cada, cerca de cinco minutos por dia.
- 32 snapshots diários entre 6 de setembro e 8 de outubro. Falta o dia 8 de setembro.
- Um feed Atom por subfila, com o que entrou, saiu ou mudou de posição em cada dia.
- Coleta, build e deploy são três jobs separados. Uma coleta que falha não apaga o site.
- O agendamento das 06:00 começou entre 09:36 e 14:51.
- 15 subfilas receberam 429 em três dias, mesmo com o cliente respeitando o limite.
- Nenhum servidor: o Git é o histórico e o GitHub Pages é a hospedagem.
O problema: a fila só existe hoje
A API Consultas Externas devolve a subfila inteira em uma chamada, sem paginação e sem histórico. O que ela mostra é o estado de agora. Para saber se um processo andou, é preciso ter guardado a fila de ontem.
Esse é o único pedaço do problema que uma API resolve. O resto, guardar, comparar e avisar, é o que o projeto faz. O comportamento da API está no post sobre a API da ANVISA.
Como a coleta funciona
Um GitHub Action roda uma vez por dia. Ele faz uma consulta por subfila, no ritmo que o gateway aceita, uma requisição por segundo. Às segundas-feiras, antes disso, ele percorre de novo o catálogo de áreas, grupos e subfilas, para notar uma subfila nova dentro de uma semana.
A coleta de 8 de outubro de 2026:
| Medida | Valor |
|---|---|
| Subfilas | 319 de 319 |
| Requisições | 319 |
| Duração | 299 s |
| Processos em fila | 9.364 |
| Falhas | 0 |
Nos dias de releitura do catálogo, são 383 requisições. Em 5 de outubro foram 393, porque o catálogo cresceu de 314 para 319 subfilas. A primeira coleta, em 6 de setembro, levou 413 segundos para 314 subfilas e 10.392 processos. Nos dias comuns, a duração ficou entre 292 e 476 segundos.
Cada coleta grava um arquivo JSON Lines comprimido, com uma linha por subfila, mais um arquivo de metadados com contagens, horários e as subfilas que falharam. O catálogo de nomes vai junto. Tudo isso é commitado no repositório.
Subfila vazia é um 404 sem corpo
Quando não há nada na fila, a API responde 404 com corpo vazio. A coleta grava a subfila como vazia, e isso não é erro. Em 6 de setembro, 88 das 314 subfilas estavam vazias. Em 8 de outubro, 87 das 319.
Uma subfila que falhou na coleta não produz evento nenhum naquele dia. Falta de dado não é mudança.
Do snapshot ao feed
O build compara cada subfila com o dia anterior. Um processo é identificado por número de processo mais expediente, porque um mesmo processo pode estar duas vezes na fila com duas petições. Daí saem três eventos: entrou, saiu e mudou de posição.
De 7 para 8 de outubro, nas 319 subfilas: 258 processos entraram, 269 saíram e 3.540 mudaram de posição. Quase toda saída empurra os processos atrás dela, então “mudou de posição” é o evento mais comum.
Cada subfila ganha um feed Atom com uma entrada por dia, com os eventos do dia e a fila como ficou. O feed guarda 30 dias. Ele carrega uma folha de estilo XSL, então o navegador mostra uma página legível em vez do XML. Cada subfila também tem uma página HTML e um JSON com a fila e os eventos, para quem prefere dados.
O site inteiro tem um feeds.opml para assinar todas as subfilas de uma vez, um sitemap, um bloco Dataset do schema.org e as tags de descoberta de feed, para o leitor achar o feed a partir da página.
O que a fila mostrou em 32 dias
O total em fila caiu de 10.392 processos, em 6 de setembro, para 9.364, em 8 de outubro. Por área, em 8 de outubro:
| Área | Processos em fila |
|---|---|
| Medicamento | 4.343 |
| Empresas | 1.749 |
| Dispositivos Médicos | 1.116 |
| Toxicologia | 862 |
| Saneantes | 438 |
| Cosmético | 404 |
| Dispositivos Médicos para Diagnóstico in Vitro | 298 |
| Alimento | 103 |
| Insumo Farmacêutico | 50 |
| Derivados do Tabaco | 1 |
A maior subfila é “Genéricos, Novos e Similares - Administrativo”, com 600 processos. A entrada mais antiga na fila é de 3 de setembro de 2009, e 103 processos entraram antes de 2020.
Sem servidor: três jobs e o que acontece quando falha
O workflow tem três jobs: coleta, build e deploy. A separação é de propósito. Uma falha ao gerar o site ou ao publicar nunca perde uma coleta. O snapshot também fica guardado como artefato do workflow por 90 dias, caso o push falhe, e o push tenta de novo depois de um rebase.
Se a coleta falha, o build roda mesmo assim, com o último snapshot bom, e põe um aviso em todas as páginas. O site fica desatualizado de forma visível, em vez de silenciosa. Um botão “Run workflow” com a opção build_only refaz o site sem gastar nenhuma requisição.
O CI separado, que roda lint, testes e build a cada push, ignora os commits de snapshot. Os testes rodam só com respostas gravadas, sem rede.
O que deu errado
Três coisas, em 32 dias.
O agendamento atrasa horas. O cron está marcado para 06:00 de Brasília. Os inícios registrados foram de 09:36 a 14:51, um atraso de 3,6 a 8,9 horas. O GitHub não garante o horário de um cron, e nos dias de maior carga o atraso cresce. Por isso cada página mostra a hora da última coleta, e não “hoje”.
Um timeout derrubou a coleta inteira. Em 8 de setembro, a leitura de uma resposta estourou o tempo e a exceção abortou o job. Não há snapshot desse dia. No mesmo dia, a coleta passou a tentar de novo cada subfila que falha por erro de transporte, três vezes, com esperas de 5 e 10 segundos, em vez de abortar.
O 429 apareceu mesmo com o limite respeitado. O cliente envia uma requisição por segundo, o ritmo que o gateway repõe. Ainda assim, uma subfila recebeu 429 em 14 de setembro, sete em 15 de setembro e sete em 20 de setembro. Nenhuma desde então. O limite é contado por endereço de origem, e os runners do GitHub Actions dividem endereços entre si. É a nossa hipótese, e não dá para confirmar pelos dados.
Como verificamos
Os números vêm dos 32 arquivos de metadados commitados, de 6 de setembro a 8 de outubro de 2026, e do histórico de execuções do workflow. As contagens por área, de subfilas vazias e de eventos foram feitas por nós em 9 de outubro, sobre os snapshots commitados. A coleta usa o cliente anvisa 0.5 em Python 3.12, no GitHub Actions.
O que não medimos: uma coleta por dia não vê um processo que entra e sai no mesmo dia. A posição é a que a API devolve, e não comparamos os dados com o portal da ANVISA. Em 9 de outubro o site mudou de domínio, e o identificador dos feeds mudou junto, então um leitor que já assinava pode ver entradas antigas de novo uma vez.
O projeto não tem vínculo com a ANVISA.
Onde assinar
Os feeds estão em anvisa.feeds.abelhaninja.de, e o código, em anvisa-feeds, com licença MIT. Para rodar a coleta na sua conta, bastam as credenciais do Portal de APIs da ANVISA:
uv sync
export ANVISA_CLIENT_ID=... ANVISA_CLIENT_SECRET=...
uv run anvisa-feeds crawl
uv run anvisa-feeds build
Os cadastros que a API não cobre, como alimentos e cosméticos, saem em CSV, e o post sobre os dados abertos da ANVISA mostra o que esses arquivos escondem. Se a sua empresa precisa acompanhar uma fonte pública e ser avisada quando algo muda, é esse o trabalho que fazemos em integrações.
Perguntas
- Como acompanhar a posição de um processo na fila de análise da ANVISA?
- Assine o feed Atom da subfila do processo e filtre pelo número dele no seu leitor. O arquivo feeds.opml assina todas as 319 subfilas de uma vez.
- Com que frequência os feeds são atualizados?
- Uma coleta por dia. Em 8 de outubro de 2026 foram 319 requisições em 299 segundos, com 9.364 processos em fila.
- Precisa de servidor para publicar os feeds?
- Não. Um GitHub Action faz a coleta, o Git guarda o histórico e o GitHub Pages serve os arquivos estáticos.
- O que acontece quando a subfila está vazia?
- A API responde 404 sem corpo, e a subfila é gravada como vazia. Em 8 de outubro de 2026, 87 das 319 subfilas estavam assim.
- Os feeds substituem a consulta oficial?
- Não. Os dados são da ANVISA, reproduzidos sem alteração, com a hora da última coleta em cada página. A consulta oficial continua sendo a referência.