Filas de análise da ANVISA como feeds Atom

Vitor Ayres (abre em nova aba)

Desenho de plantas gerado a partir do primeiro commit do repositório anvisa-feeds.
Plantas geradas pelo primeiro commit de anvisa-feeds (6aed086).

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.

Como podemos ajudar?

Oi (abre em nova aba)