SEO Programático · Capítulo 4 de 21 · 33 min
Escolher a fonte pública que sustenta o acervo
Bases do IBGE, da ANP e do Banco Central viram vantagem quando o recorte publicado não existe pronto em nenhum outro lugar.
Este capítulo faz parte do curso gratuito SEO Programático. Para marcar como concluído e salvar o progresso, abra este capítulo na página do curso.
A fonte de dado é a decisão que mais pesa no risco e no custo de um acervo programático: ela define se a empresa publica um ativo citável ou um passivo que a política de spam alcança. Nesta etapa aparece a saída fácil que arruína projeto: pegar um dump de cadastro de CNPJ, gerar uma página por empresa e publicar cinquenta mil URLs em uma tarde. O dado é público, a raspagem é trivial e o volume impressiona o cliente na reunião.
Essa é, palavra por palavra, a prática que a política de spam do Google descreve em scaled content abuse (abuso de conteúdo em escala, a fábrica de páginas feitas para o buscador e não para o leitor): muitas páginas geradas com a finalidade principal de manipular classificação, sem valor incremental para quem lê. Desde 15/05/2026 a mesma regra alcança variações criadas para manipular as respostas de IA da Pesquisa Google. O problema não é a origem do dado. É que reempacotar um cadastro não cria informação que alguém buscaria por si só.
Dado público não equivale a página útil. A pergunta que separa acervo de doorway continua sendo a de [Separar acervo de fachada antes de aprovar a verba](#gate-do-dado-proprio): existe nesta página um dado que só existe aqui e que alguém procuraria isoladamente? Um endereço de CNPJ falha no teste. O preço médio da gasolina em Sorocaba na semana passada, com série histórica, passa.
A legitimidade do uso comercial desse dado tem base escrita. A Lei de Acesso à Informação (Lei 12.527/2011) estabelece a publicidade como regra, e o Decreto 8.777/2016 institui a Política de Dados Abertos do Executivo federal, que é o que obriga órgão federal a publicar em formato aberto e reutilizável.
Duas consequências operacionais que ninguém pode pular
- Cada conjunto carrega a licença declarada no portal dele. Não existe licença única para "dado do governo". Você lê a licença do conjunto que baixou, e registra a atribuição que ela pede.
- A lei brasileira de proteção de dados pessoais continua valendo sobre qualquer campo que permita reidentificar pessoa natural, mesmo em base pública. Preço médio por município é dado agregado e não aciona esse risco. Nome de sócio, sim. O tratamento em profundidade fica em [Blindar o acervo contra risco jurídico e de marca](#risco-juridico-e-de-marca).
Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
O IBGE é o eixo central de qualquer projeto programático brasileiro por um motivo simples: ele define o vocabulário geográfico que todas as outras bases usam. Quatro endpoints resolvem quase tudo, todos verificados respondendo HTTP 200 em 19/07/2026:
https://servicodados.ibge.gov.br/api/v1/localidades/municipiosentrega os 5.570 municípios com UF, mesorregião e microrregião. É a espinha dorsal das rotas do Radar.https://servicodados.ibge.gov.br/api/v3/agregadosé o catálogo do SIDRA, com todas as tabelas disponíveis.https://apisidra.ibge.gov.br/values/t/6579/n6/all/v/9324/p/lasttraz população estimada por município, com série histórica. É o que permite dizer se o preço de um município grande destoa dos vizinhos de porte parecido.https://servicodados.ibge.gov.br/api/v2/censos/nomes/joaodevolve frequência de nomes por década e por UF. Fora do escopo do Radar, mas é o melhor exemplo público de cauda longa genuinamente informativa.
A documentação central em https://servicodados.ibge.gov.br/api/docs/ expõe 17 APIs.
// scripts/ingestao/ibge-municipios.mjs
// Node 22+, sem dependências. Roda com: node scripts/ingestao/ibge-municipios.mjs
import { mkdir, writeFile } from 'node:fs/promises';
const FONTE = 'https://servicodados.ibge.gov.br/api/v1/localidades/municipios';
const DESTINO = 'dados/brutos/ibge-municipios.json';
const resposta = await fetch(FONTE, {
headers: { 'User-Agent': 'radar-combustivel/1.0 (contato@seudominio.com.br)' },
});
if (!resposta.ok) throw new Error('IBGE respondeu ' + resposta.status);
const bruto = await resposta.json();
const registros = bruto.map((m) => {
// A API expõe a UF por dois caminhos e nem todo município traz os dois.
// Tentar os dois evita perder registro em silêncio.
const uf =
m.microrregiao?.mesorregiao?.UF ??
m['regiao-imediata']?.['regiao-intermediaria']?.UF;
if (!uf) throw new Error('sem UF para o município ' + m.id + ' (' + m.nome + ')');
return {
codigoIbge: String(m.id),
nome: m.nome,
uf: uf.sigla,
ufNome: uf.nome,
};
});
await mkdir('dados/brutos', { recursive: true });
await writeFile(
DESTINO,
JSON.stringify(
{
fonte: FONTE,
coletadoEm: new Date().toISOString(), // carimbo de coleta: sem isso, o dado não tem idade
totalRegistros: registros.length,
registros,
},
null,
2,
),
'utf8',
);
console.log(registros.length + ' municípios gravados em ' + DESTINO);
// Esperado hoje: 5570 municípios gravados em dados/brutos/ibge-municipios.jsonA API de Agregados do IBGE não tem SLA publicado e responde devagar em consultas amplas. Se a sua página chamar essa API no momento em que o visitante abre a URL, você acabou de acoplar o tempo de resposta do seu site a um serviço que não prometeu tempo de resposta nenhum. Com 5.570 municípios multiplicados por combustível, isso não degrada uma página: degrada o catálogo inteiro, inclusive para o rastreador. O padrão correto é ingestão em lote com cache local, sempre.
Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
A ANP é a fonte que dá ao Radar a razão de existir. A série histórica de preços de combustíveis fica em https://www.gov.br/anp/pt-br/centrais-de-conteudo/dados-abertos/serie-historica-de-precos-de-combustiveis, em CSV, com atualização semanal às sextas-feiras, trazendo preço médio por município, por combustível, por semana.
Olhe o que isso produz quando vira página
- O dado só existe ali. O preço médio praticado em um município específico, naquela semana, com o histórico anterior ao lado, não está em outra página da web nesse recorte.
- O dado muda, e muda em cadência conhecida. Isso é o que dá sentido real a
lastmod, a revalidação e a qualquer discussão de frescor. Um diretório de CNPJ é estático: nada nele justifica ser rastreado de novo. - A fonte é oficial, gratuita e citável. A página pode nomear a origem e a data de coleta, o que é exatamente o oposto de conteúdo raspado sem procedência.
- A cobertura é desigual. Nem todo município aparece em toda semana, e nem todo combustível aparece em todo município. Essa lacuna não é defeito: é o que vai obrigar você a decidir indexação contra o inventário, em [Gerar tudo e publicar só o que tem dado](#indexacao-seletiva-em-render).
// scripts/ingestao/anp-serie.mjs
// Node 22+, sem dependências.
// Uso: node scripts/ingestao/anp-serie.mjs dados/brutos/anp-semana.csv
// O link do arquivo muda a cada publicação, então o script recebe o caminho
// do CSV já baixado, em vez de fingir que a URL é estável.
import { readFile, writeFile } from 'node:fs/promises';
const caminho = process.argv[2];
if (!caminho) throw new Error('informe o caminho do CSV baixado da ANP');
// A ANP publica com separador ponto e vírgula e acentuação em windows-1252.
const texto = new TextDecoder('windows-1252').decode(await readFile(caminho));
const linhas = texto.split(/\r?\n/).filter((l) => l.trim() !== '');
// Os nomes das colunas variam entre publicações. Localizar pelo cabeçalho, e
// falhar alto mostrando o que veio, é melhor do que presumir posição fixa.
const cabecalho = linhas[0].split(';').map((c) => c.trim().toLowerCase());
const coluna = (trecho) => {
const i = cabecalho.findIndex((c) => c.includes(trecho));
if (i === -1) throw new Error('coluna "' + trecho + '" ausente. Cabeçalho: ' + cabecalho.join(' | '));
return i;
};
const iMunicipio = coluna('munic');
const iUf = coluna('estado');
const iProduto = coluna('produto');
const iPreco = coluna('médio');
const iFim = coluna('data final');
// Chave de junção: nome sem acento em caixa alta, mais a UF.
const chave = (nome, uf) =>
nome.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toUpperCase().trim() + '/' + uf.trim().toUpperCase();
const { registros: municipios } = JSON.parse(
await readFile('dados/brutos/ibge-municipios.json', 'utf8'),
);
const porChave = new Map(municipios.map((m) => [chave(m.nome, m.uf), m.codigoIbge]));
const cruzados = [];
const orfaos = new Set();
for (const linha of linhas.slice(1)) {
const campos = linha.split(';');
const k = chave(campos[iMunicipio], campos[iUf]);
const codigoIbge = porChave.get(k);
if (!codigoIbge) {
orfaos.add(k); // não descarte em silêncio: órfão é sinal de chave errada
continue;
}
cruzados.push({
codigoIbge,
combustivel: campos[iProduto].trim(),
precoMedio: Number(campos[iPreco].replace(',', '.')),
semanaFim: campos[iFim].trim(),
});
}
await writeFile(
'dados/brutos/anp-cruzado.json',
JSON.stringify(
{
fonte: 'ANP, série histórica de preços de combustíveis',
arquivoOrigem: caminho,
coletadoEm: new Date().toISOString(),
totalRegistros: cruzados.length,
registros: cruzados,
},
null,
2,
),
'utf8',
);
console.log('cruzados: ' + cruzados.length);
console.log('sem correspondência no IBGE: ' + orfaos.size);
if (orfaos.size > 0) console.log([...orfaos].slice(0, 10).join('\n'));Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
Duas fontes completam o inventário útil para este projeto.
O Banco Central expõe séries temporais pelo SGS, no padrão https://api.bcb.gov.br/dados/serie/bcdata.sgs.432/dados/ultimos/1?formato=json, e o PTAX pelo OData Olinda em https://olinda.bcb.gov.br/olinda/servico/PTAX/versao/v1/odata/Moedas. Serve para contextualizar variação de preço com câmbio ou com indicador macroeconômico.
A Base dos Dados (https://basedosdados.org/docs/access_data_bq, com SDK sob licença MIT em https://github.com/basedosdados/sdk) é um datalake público brasileiro hospedado no BigQuery, com tabelas já harmonizadas. Ela resolve o problema mais caro do SEO programático brasileiro, que é a normalização de códigos de município entre IBGE, DATASUS, INEP e Receita. O custo: você consulta com o seu próprio projeto do Google Cloud, e o primeiro 1 TiB processado por mês é gratuito.
Quantas bases diferentes você precisa cruzar?
|
+-- UMA base, uma chave (ex.: só IBGE Localidades)
| -> FONTE DIRETA. A API oficial é mais fresca e não
| depende de projeto no Google Cloud.
|
+-- DUAS OU MAIS bases de órgãos diferentes
(IBGE + DATASUS + INEP + Receita)
|
+-- as chaves de município batem entre elas?
|
+-- SIM, e você já testou -> fonte direta
|
+-- NÃO, ou você ainda não testou
-> BASE DOS DADOS. O trabalho de
harmonizar já foi feito e revisado.
Regra de bolso: o momento de mudar para a Base dos Dados é quando
você percebe que está escrevendo o seu terceiro de-para de código
de município na mão.Para cadastro e geografia complementar, o inventário verificado em 19/07/2026, todos respondendo HTTP 200 salvo indicação:
- BrasilAPI: CEP v2, CNPJ e municípios por UF, em
https://brasilapi.com.br/api/ibge/municipios/v1/SP. - ViaCEP:
https://viacep.com.br/ws/01001000/json/. - CNPJ.ws público:
https://publica.cnpj.ws/cnpj/19131243000197. - Minha Receita:
https://minhareceita.org/, que responde HTTP 302 e está vivo. - Portal da Transparência: OpenAPI em
https://api.portaldatransparencia.gov.br/v3/api-docs, com token obrigatório.
E um registro que evita meia hora de depuração: a API pública do dados.gov.br exige chave. O endpoint https://dados.gov.br/api/publico/conjuntos-dados retornou HTTP 401 na sonda de 19/07/2026. O portal de navegação humana continua aberto; a API, não.
Repare no que essa última lista tem em comum: são APIs de consulta pontual, feitas para responder sobre um CNPJ ou um CEP que o usuário já tem em mãos. Usá-las como matéria-prima para gerar milhares de páginas indexáveis de empresa é o caminho mais curto para a definição de scaled content abuse, e ainda coloca dado de pessoa natural (sócio, endereço residencial de MEI) dentro do alcance da lei brasileira de proteção de dados pessoais. Use essas APIs para enriquecer uma página que já tem razão de existir. Não para inventar a razão.
// dados/fontes.json
// Registro de proveniência. Escreva isto ANTES de baixar qualquer coisa:
// se você não consegue preencher o campo de licença, ainda não leu o portal.
{
"ibge-localidades": {
"orgao": "IBGE",
"endpoint": "https://servicodados.ibge.gov.br/api/v1/localidades/municipios",
"licencaDeclarada": "PREENCHER com a licença do conjunto, lida no portal",
"atribuicaoNaPagina": "Fonte: IBGE, API de Localidades",
"verificadoEm": "2026-07-19",
"cadencia": "mudança rara, revisão trimestral basta"
},
"anp-serie-historica": {
"orgao": "ANP",
"endpoint": "https://www.gov.br/anp/pt-br/centrais-de-conteudo/dados-abertos/serie-historica-de-precos-de-combustiveis",
"licencaDeclarada": "PREENCHER com a licença do conjunto, lida no portal",
"atribuicaoNaPagina": "Fonte: ANP, série histórica de preços de combustíveis",
"verificadoEm": "2026-07-19",
"cadencia": "semanal, às sextas-feiras"
}
}O campo verificadoEm do registro de proveniência não é burocracia: é o que permite a uma página exibir "preço coletado da ANP em 18/07/2026" em vez de um número sem idade. Data de coleta visível é, ao mesmo tempo, honestidade editorial e conteúdo exclusivo, porque o concorrente que raspou a sua página não tem como reproduzi-la de forma verdadeira.
Se a figura ultrapassar a área visível, deslize para os lados. Pelo teclado, foque a figura e use as setas.
Teste o inventário num pedido real. Alguém quer uma página que compare o preço do etanol de um município com o de municípios de porte parecido: escreva qual endpoint do IBGE dá a lista, qual dá o porte e por qual chave os dois se unem ao dado da ANP. Em seguida, decida se o cruzamento pede de-para feito à mão ou a Base dos Dados, usando o critério do fluxo acima. Se a resposta não couber em três linhas, a fonte ainda não está escolhida.
O guia a seguir serve a quem aprova o projeto e precisa nomear, antes da primeira linha de código, de onde vem o dado e quem responde por ele. Ele destrava a decisão de seguir com a fonte escolhida ou voltar à prancheta, com registro de licença e cadência que o jurídico consegue ler.
Guia de implementação: escolher e registrar a fonte do acervo
Cinco passos levam da lista de candidatas ao registro de proveniência aprovado.
Passo 1: Liste as fontes candidatas pelo teste do dado próprio
Para cada fonte, escreva o recorte que só a sua página vai publicar.
Deu certo quando: Cada candidata tem uma frase no formato "o dado exclusivo é X, e alguém buscaria X porque Y".
Erro comum: Começar pela base mais volumosa, como cadastro de CNPJ, em vez da mais informativa.
Passo 2: Confirme acesso, formato e cadência
Baixe uma amostra e registre formato, frequência de atualização e se a API exige chave.
Deu certo quando: A amostra abre no computador de quem vai construir e a cadência está anotada.
Erro comum: Confiar em endpoint citado em tutorial sem testar a resposta no dia.
Passo 3: Leia a licença e peça o aval do jurídico
Cada conjunto declara a própria licença; campos que identificam pessoa natural pedem análise à parte.
Deu certo quando: O registro de proveniência tem licença e atribuição preenchidas, com aval do jurídico.
Erro comum: Tratar "dado do governo" como licença única e pular a leitura do portal.
Passo 4: Teste o cruzamento das chaves
Rode a junção entre as bases e conte os registros sem correspondência.
Deu certo quando: A taxa de órfãos é conhecida e cada órfão tem causa anotada.
Erro comum: Descartar órfãos em silêncio e descobrir o buraco depois, página a página.
Passo 5: Nomeie o dono do dado
Uma pessoa responde pela ingestão semanal e pela correção quando a fonte muda de layout.
Deu certo quando: O nome do dono consta do registro de proveniência e do calendário da ingestão.
Erro comum: Deixar a ingestão sem dono e só notar a falha quando o preço envelhece no ar.
No fim você tem: Uma fonte escolhida por critério, com licença lida, cruzamento testado e dono nomeado dentro da empresa.
Quem faz, quanto custa, como conferir
| Etapa | Quem faz | Prazo e esforço | Como conferir |
|---|---|---|---|
| Teste do dado próprio | CMO com o analista de SEO | Meio dia | Uma frase de dado exclusivo por candidata |
| Acesso, formato e cadência | Engenheiro de dados | 1 dia; sem custo de licença nas fontes federais | Amostra baixada e cadência registrada |
| Licença e aval | Jurídico, com o engenheiro de dados | 2 a 3 dias corridos | Campos de licença e atribuição preenchidos |
| Cruzamento das chaves | Engenheiro de dados | 1 a 2 dias; BigQuery só se usar a Base dos Dados, dentro da franquia gratuita de 1 TiB por mês | Taxa de órfãos medida e explicada |
| Dono do dado | Diretoria que patrocina o projeto | 1 reunião | Nome no registro e no calendário de ingestão |
Peça ao time, antes de aprovar qualquer cronograma, o registro de proveniência com licença, cadência e dono preenchidos para cada fonte. Confira em duas semanas: nenhum campo com "PREENCHER", taxa de órfãos do cruzamento abaixo de 1% ou explicada linha a linha, e aval do jurídico anexado.
Perguntas frequentes deste capítulo
Posso simplesmente chamar a API do IBGE dentro da página e cachear a resposta por algumas horas?
Se o dado é público por lei, preciso mesmo registrar licença e atribuição?
O CSV da ANP veio com municípios que não casam com a lista do IBGE. O que fazer com eles?
Consultar a Base dos Dados no BigQuery vai me custar caro?
Por que o curso não usa API de ferramenta de SEO como fonte de dado para gerar páginas?
Seu caderno neste capítulo
Abrir o caderno completoSelecione um trecho do capítulo para destacar ou anotar. Nos vídeos e áudios, use Anotar este momento. No teclado, selecione com Shift e as setas e use Alt+Shift+D para destacar ou Alt+Shift+N para anotar.
Salvo neste navegador. Entre na sua conta para levar o caderno a outros aparelhos.
Entre na sua conta para compartilhar o que aprendeu e convidar alguém para estudar com você.
Conexões deste capítulo
Explore os conceitos e compare abordagens em outros cursos. As conexões indicam assuntos relacionados; a sequência de estudo continua no índice do curso.
Conceitos deste capítulo
O mesmo assunto em outros cursos
- Analytics de busca para decisão executivaAprovar a infraestrutura de dados com teto de custoExaminar conexões de Aprovar a infraestrutura de dados com teto de custo
- Gestão de Projetos GEOEscolher as perguntas e as condições da mediçãoExaminar conexões de Escolher as perguntas e as condições da medição
- Search Console para decisãoDeclare o que o dado esconde antes que alguém chame de erroExaminar conexões de Declare o que o dado esconde antes que alguém chame de erro
Voltar ao capítulo anterior: Auditar o concorrente em escala pelo que ele publica
Capítulos vizinhos em SEO Programático
- 02Conferir o número da agência antes de citá-lo
- 03Auditar o concorrente em escala pelo que ele publica
- 04Escolher a fonte pública que sustenta o acervo
- 05Definir o registro que fixa o custo do projeto
- 06Priorizar páginas sem depender do volume de busca