Como servir os 68 milhões de CNPJs da Receita com ~10ms de latência em Go

Todo dev brasileiro que já precisou consultar CNPJ conhece o dilema: ou você
usa uma API que faz proxy da Receita (3 a 10 segundos por consulta, quando não
cai), ou baixa o dump de dados abertos e monta a própria base — e descobre que
“baixar um CSV” e…


This content originally appeared on DEV Community and was authored by Messias Freitas

Todo dev brasileiro que já precisou consultar CNPJ conhece o dilema: ou você
usa uma API que faz proxy da Receita (3 a 10 segundos por consulta, quando não
cai), ou baixa o dump de dados abertos e monta a própria base — e descobre que
"baixar um CSV" era a parte fácil.

Eu montei a própria base. Este post é o diário honesto do que funcionou, do
que quebrou e dos números reais — 217 milhões de linhas servidas em ~10ms de
p50 dentro do datacenter, num Postgres de 1 vCPU.

A arquitetura em uma frase

Não consulte a Receita em tempo real. Ingira o dump mensal e sirva da sua
infra.
O resto é decorrência.

Receita (dump mensal, ~6GB zip) ──▶ ingestão Go (COPY) ──▶ Postgres ──▶ API (chi)
CGU (CEIS/CNEP, zip diário)     ──▶ job diário          ──┘

O dump da Receita: as pegadinhas que ninguém documenta

O layout oficial existe, mas o que quebra parser de verdade é o que está fora dele:

  1. Encoding latin1 (ISO-8859-1) — acento vira lixo se você ler como UTF-8. Em Go: charmap.ISO8859_1.NewDecoder() num transform.Reader streaming.
  2. Decimal com vírgula ("1000000,00") e datas YYYYMMDD onde 0 e 00000000 significam nulo.
  3. CNPJ quebrado em 3 colunas (básico 8 + ordem 4 + DV 2). A chave de junção entre empresas, estabelecimentos e sócios é o básico — errar isso custa um dia.
  4. As partições 0–9 não se alinham entre arquivos. O estabelecimento da partição 3 pode ser de uma empresa da partição 7. Foreign key rígida entre as tabelas = COPY quebrando no meio da carga. A solução: sem FK; a integridade vem da fonte.
  5. Bytes NUL (0x00) no meio dos dados. O Postgres rejeita NUL em text. Um strings.ReplaceAll(s, "\x00", "") no parser economizou três recargas.
  6. Desde jan/2026 o repositório é um Nextcloud do SERPRO+ com WebDAV público — dá pra listar meses com PROPFIND e baixar com o token do share como usuário. Adeus, scraping.

COPY ou morte

A diferença entre INSERT em lote e o protocolo COPY não é incremental — é
outra categoria. Com pgx.CopyFrom e lotes de 50k:

  • 28,1 milhões de empresas em 1m28s (~320k linhas/s) num MacBook
  • A base completa (68,6M empresas + 71,9M estabelecimentos + 27,8M sócios + 49M de registros do Simples) entra em cerca de uma hora numa VM modesta

O truque é não materializar nada: latin1→UTF-8→parse→COPY, tudo streaming,
memória constante.

O índice que paga o aluguel

A feature mais pedida por quem faz compliance é a busca reversa: de um nome
de pessoa para todas as empresas em que ela é sócia. Com 27,8 milhões de sócios:

CREATE INDEX idx_socios_nome_trgm ON socios USING gin (nome_socio gin_trgm_ops);

Lição aprendida: ORDER BY similarity(...) avalia todos os matches antes
do LIMIT. Com threshold 0.4 num nome comum ("JOSE..."), timeout; com 0.6,
~1s. O operador % respeita o set_limit() da sessão — alinhe o threshold do
índice com o do filtro ou o plano de query mente pra você.

Cruzar sanções com o QSA (o pulo do gato)

O CEIS/CNEP (empresas e pessoas punidas, CGU) publica CPF completo. O QSA
da Receita publica CPF mascarado: ***XXXXXX** — os 6 dígitos do meio.

Então o match sócio-sancionado é: substring(cpf_mascarado, 4, 6) =
substring(cpf_completo, 4, 6)
+ similaridade trigram de nome ≥ 0.5. Não é
prova — é sinal forte, e em compliance sinal forte com explicação vale mais que
caixa-preta. Numa base real: empresa limpa, sócio proibido de contratar com o
poder público até 2030. É esse cruzamento que consulta nenhuma te dá de graça.

Números de produção (Cloud Run + Cloud SQL 1 vCPU)

Operação Latência
Ficha completa (3 queries + JOINs de tradução) ~8ms no datacenter, ~134ms na minha casa
Checagem de sanções (empresa + sócios) 14–34ms
Score com sinais ~96ms
Busca reversa em 27,8M de nomes ~1s

Custo total da infra: ~R$ 300/mês. O concorrente que faz proxy da Receita
responde em 3.000–10.000ms e cai junto com ela.

Coisas que quebraram e viraram conhecimento

  • O Google Frontend reserva /healthz nos domínios *.run.app — o request nem chega no container. Use /health.
  • Cloud Run com scale-to-zero: primeiro request paga ~6s de cold start (pool do Postgres). Aceitável para o meu caso; min-instances=1 resolve por ~R$ 30/mês.
  • gcloud sql connect não funciona se seu ISP te dá IPv6. cloud-sql-proxy autentica via IAM e ignora o problema.
  • Payment Links do Stripe suportam assinatura de R$ 0 sem cartão (payment_method_collection: if_required) — free tier com identidade real, zero código de checkout.

O resultado

A API está no ar em fides.api.br — tem playground sem
cadastro na landing e um free tier de 50 consultas/mês que pede só email. Se
você só precisa de razão social e endereço, a BrasilAPI é grátis e resolve;
eu mantenho uma comparação honesta que
inclui quando não usar o meu produto.

O código do parser de latin1 e a estratégia de COPY descritos aqui são
genéricos — servem pra qualquer dataset dos dados abertos brasileiros.

Dúvidas sobre o pipeline? Comenta que eu respondo — a parte de infra rendeu
mais história do que coube aqui.


This content originally appeared on DEV Community and was authored by Messias Freitas


Print Share Comment Cite Upload Translate Updates
APA

Messias Freitas | Sciencx (2026-07-06T12:35:31+00:00) Como servir os 68 milhões de CNPJs da Receita com ~10ms de latência em Go. Retrieved from https://www.scien.cx/2026/07/06/como-servir-os-68-milhoes-de-cnpjs-da-receita-com-10ms-de-latencia-em-go/

MLA
" » Como servir os 68 milhões de CNPJs da Receita com ~10ms de latência em Go." Messias Freitas | Sciencx - Monday July 6, 2026, https://www.scien.cx/2026/07/06/como-servir-os-68-milhoes-de-cnpjs-da-receita-com-10ms-de-latencia-em-go/
HARVARD
Messias Freitas | Sciencx Monday July 6, 2026 » Como servir os 68 milhões de CNPJs da Receita com ~10ms de latência em Go., viewed ,<https://www.scien.cx/2026/07/06/como-servir-os-68-milhoes-de-cnpjs-da-receita-com-10ms-de-latencia-em-go/>
VANCOUVER
Messias Freitas | Sciencx - » Como servir os 68 milhões de CNPJs da Receita com ~10ms de latência em Go. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2026/07/06/como-servir-os-68-milhoes-de-cnpjs-da-receita-com-10ms-de-latencia-em-go/
CHICAGO
" » Como servir os 68 milhões de CNPJs da Receita com ~10ms de latência em Go." Messias Freitas | Sciencx - Accessed . https://www.scien.cx/2026/07/06/como-servir-os-68-milhoes-de-cnpjs-da-receita-com-10ms-de-latencia-em-go/
IEEE
" » Como servir os 68 milhões de CNPJs da Receita com ~10ms de latência em Go." Messias Freitas | Sciencx [Online]. Available: https://www.scien.cx/2026/07/06/como-servir-os-68-milhoes-de-cnpjs-da-receita-com-10ms-de-latencia-em-go/. [Accessed: ]
rf:citation
» Como servir os 68 milhões de CNPJs da Receita com ~10ms de latência em Go | Messias Freitas | Sciencx | https://www.scien.cx/2026/07/06/como-servir-os-68-milhoes-de-cnpjs-da-receita-com-10ms-de-latencia-em-go/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.