Até pouco tempo atrás, quem publica conteúdo ou gerencia uma aplicação web vivia à mercê do cronograma dos crawlers: você subia um artigo ou criava uma rota nova, atualizava o sitemap.xml e ficava esperando dias (às vezes semanas) até o Google ou o Bing decidirem bater na sua porta. Para complicar, o Google desativou de vez o endpoint público de ping de sitemaps no final de 2023, reforçando que o modelo passivo de descoberta tem limites claros.
O protocolo IndexNow inverte essa lógica e adota uma arquitetura push. Em vez de esperar que o crawler descubra por conta própria o que mudou no servidor, a minha própria aplicação envia um aviso HTTP direto aos motores de busca: "esta URL acabou de ser publicada ou alterada, pode rastrear".
Neste artigo, detalho como o protocolo opera nos bastidores, a lógica da chave de validação de domínio e como montei implementações diretas em Node.js, PHP, Python e rotinas de CI/CD.
1. O que é o IndexNow e quem realmente usa?#
Criado por uma iniciativa conjunta da Microsoft (Bing) e da Yandex sob licença Creative Commons, o IndexNow é um protocolo aberto e minimalista. Ele resolve dois problemas centrais da infraestrutura de busca na web:
- Latência de descoberta de conteúdo: O intervalo entre disparar a publicação de um artigo ou produto e a visita do crawler cai de dias para poucos segundos ou minutos.
- Desperdício de recursos de rede e CPU: Robôs tradicionais gastam largura de banda e ciclos de processamento rastreando repetidamente centenas de páginas idênticas só para conferir se houve alteração. Com o modelo push, o bot só bate onde houve novidade real.
Além de resolver esses dois problemas, o protocolo tem um diferencial de arquitetura: a distribuição em malha (Mesh Sharing) — ao submeter URLs a qualquer endpoint participante (como api.indexnow.org ou www.bing.com), o mecanismo que recebe a notificação compartilha automaticamente a requisição com todos os motores de busca parceiros cadastrados no protocolo (Yandex, Seznam, Naver).
E onde entra a Inteligência Artificial nisso? Ferramentas que sintetizam respostas em tempo real (como o Microsoft Copilot e serviços que consultam a API do Bing) consomem essa malha de dados. Se o Bing indexou sua página minutos após a subida, a IA já tem acesso ao seu conteúdo fresco.
2. Autenticação por chave de host: simples e sem tokens opacos#
O IndexNow não exige credenciais OAuth, contas de desenvolvedor ou tokens complexos por cliente. Ele valida a posse do domínio pelo próprio protocolo HTTP, através de uma Host-Matching Key:
- Gere uma chave: Uma sequência alfanumérica em hexadecimal de 8 a 128 caracteres (o padrão da indústria é usar 32 caracteres minúsculos).
- Hospede o arquivo de validação: Crie um arquivo de texto plano chamado
{chave}.txtna raiz pública do seu domínio (exemplo:https://meudominio.com.br/2cd1cb62283949fbb51a9f02e642cb7d.txt). - Conteúdo do arquivo: O interior do arquivo deve conter apenas o valor da chave, sem espaços, linhas em branco ou tags HTML.
- Validação do buscador: Ao receber o ping com as URLs, o crawler do IndexNow faz uma checagem rápida no seu domínio buscando esse arquivo. Se o arquivo existir e o conteúdo for idêntico à chave enviada no payload, a titularidade está comprovada e as URLs entram na fila prioritária de rastreamento.
Para gerar a chave e o arquivo de validação direto no terminal Linux:
# Gera uma chave hexadecimal limpa de 32 caracteres
openssl rand -hex 16
# Exemplo de saída: 2cd1cb62283949fbb51a9f02e642cb7d
# Salva o arquivo na raiz pública (document root) da aplicação
echo -n "2cd1cb62283949fbb51a9f02e642cb7d" > 2cd1cb62283949fbb51a9f02e642cb7d.txt
3. Os dois formatos de envio da API#
O endpoint oficial do protocolo aceita dois métodos de comunicação:
Método 1: URL Única via HTTP GET#
Perfeito para testes manuais no terminal com curl ou disparos pontuais:
https://api.indexnow.org/indexnow?url=https://meudominio.com.br/novo-artigo.html&key=SUA_CHAVE_AQUI
Método 2: Envio em Lote via HTTP POST (JSON)#
É o padrão que eu utilizo em código de produção e pipelines. Permite empacotar de 1 até 10.000 URLs em uma única requisição:
POST /indexnow HTTP/1.1
Host: api.indexnow.org
Content-Type: application/json; charset=utf-8
{
"host": "meudominio.com.br",
"key": "2cd1cb62283949fbb51a9f02e642cb7d",
"keyLocation": "https://meudominio.com.br/2cd1cb62283949fbb51a9f02e642cb7d.txt",
"urlList": [
"https://meudominio.com.br/blog/artigo-1.html",
"https://meudominio.com.br/blog/artigo-2.html",
"https://meudominio.com.br/planos.html"
]
}
O campo keyLocation é opcional caso o arquivo {chave}.txt esteja na raiz padrão do host, mas eu sempre declaro a URL completa para evitar dúvidas de resolução em ambientes com múltiplos subdomínios ou proxies reversos.
4. Implementação em Node.js / JavaScript#
Em projetos com Node.js (seja uma API Express/Fastify, rota serverless no Next.js ou script de pós-build), basta utilizar a API nativa de fetch:
// indexnow-submit.js
const INDEXNOW_API = "https://api.indexnow.org/indexnow";
const HOST = "meudominio.com.br";
const KEY = "2cd1cb62283949fbb51a9f02e642cb7d";
/**
* Envia uma lista de URLs para o IndexNow
* @param {string[]} urls - Lista de URLs completas
*/
export async function submitToIndexNow(urls) {
if (!urls || urls.length === 0) {
return { ok: false, error: "Nenhuma URL fornecida." };
}
// O protocolo aceita no máximo 10.000 URLs por lote
const batch = urls.slice(0, 10000);
const payload = {
host: HOST,
key: KEY,
keyLocation: `https://${HOST}/${KEY}.txt`,
urlList: batch
};
try {
const response = await fetch(INDEXNOW_API, {
method: "POST",
headers: {
"Content-Type": "application/json; charset=utf-8",
"User-Agent": "IndexNow-Client/1.0"
},
body: JSON.stringify(payload)
});
// 200 = processado imediatamente; 202 = enfileirado com sucesso
if (response.status === 200 || response.status === 202) {
console.log(`[IndexNow] ${batch.length} URLs submetidas com sucesso (HTTP ${response.status}).`);
return { ok: true, status: response.status, count: batch.length };
}
const errorBody = await response.text();
console.error(`[IndexNow] Erro HTTP ${response.status}:`, errorBody);
return { ok: false, status: response.status, error: errorBody };
} catch (err) {
console.error("[IndexNow] Erro de conexão:", err.message);
return { ok: false, error: err.message };
}
}
// Exemplo de chamada:
// submitToIndexNow(["https://meudominio.com.br/blog/meu-novo-post.html"]);
5. Implementação em PHP puro#
Se você desenvolve em PHP (legado, moderno ou Laravel) sem depender de bibliotecas pesadas de terceiros, uma classe enxuta com cURL nativo resolve a notificação:
<?php
// IndexNowNotifier.php
class IndexNowNotifier
{
private string $host;
private string $key;
private string $endpoint;
public function __construct(string $host, string $key, string $endpoint = 'https://api.indexnow.org/indexnow')
{
$this->host = $host;
$this->key = $key;
$this->endpoint = $endpoint;
}
public function notify(array $urls): array
{
if (empty($urls)) {
return ['ok' => false, 'error' => 'A lista de URLs não pode estar vazia.'];
}
$payload = [
'host' => $this->host,
'key' => $this->key,
'keyLocation' => "https://{$this->host}/{$this->key}.txt",
'urlList' => array_values(array_slice($urls, 0, 10000))
];
$jsonPayload = json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$ch = curl_init($this->endpoint);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $jsonPayload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json; charset=utf-8',
'Content-Length: ' . strlen($jsonPayload),
'User-Agent: IndexNow-PHP/1.0'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError !== '') {
return ['ok' => false, 'error' => $curlError];
}
if ($httpCode === 200 || $httpCode === 202) {
return ['ok' => true, 'code' => $httpCode, 'count' => count($payload['urlList'])];
}
return [
'ok' => false,
'code' => $httpCode,
'error' => $response ?: "HTTP Error {$httpCode}"
];
}
}
// Exemplo de uso:
// $client = new IndexNowNotifier('meudominio.com.br', '2cd1cb62283949fbb51a9f02e642cb7d');
// $status = $client->notify(['https://meudominio.com.br/novo-artigo.html']);
6. WordPress: Como ativar no Rank Math ou com o Plugin Oficial#
Se você gerencia sites, portais ou lojas virtuais no WordPress, não precisa escrever código manual. Existem dois caminhos bem consolidados para colocar o IndexNow em produção:
Opção A: Rank Math SEO (Módulo Instant Indexing)#
Para quem já utiliza a suíte do Rank Math, a integração com o IndexNow é nativa:
- Ative o módulo: No painel do WordPress, vá em
Rank Math > Painel Gerale ligue a chave Indexação Instantânea (Instant Indexing). - Chave de API automática: Ao abrir as configurações da aba Indexação Instantânea, o Rank Math gera e gerencia a chave de host de forma dinâmica, sem você precisar subir arquivos
.txtna mão via FTP ou SSH. - Escolha os tipos de post: Marque quais post types devem disparar o ping automático ao publicar ou salvar (Posts, Páginas, Produtos ou Custom Post Types).
- Envio manual ou em lote: O módulo inclui uma aba de console onde você pode colar até 100 URLs de uma vez só para forçar uma indexação imediata, além de um botão de envio direto na listagem padrão de posts.
- Log de auditoria: O plugin exibe o histórico com data, URL enviada e o código HTTP retornado pelos buscadores.
Opção B: Plugin Oficial da Microsoft (IndexNow Plugin)#
Se você não usa o Rank Math ou prefere manter a instalação limpa, sem suítes volumosas de SEO:
- Instale o plugin oficial IndexNow fornecido pela Microsoft no diretório do WordPress (
wordpress.org/plugins/indexnow/). - Ao ativar, ele gera a chave de validação silenciosamente e se conecta aos hooks do core (
save_post,transition_post_statuse exclusões). - Sempre que você publica, atualiza ou exclui um post (ou cria um redirecionamento 301), o plugin dispara a notificação em segundo plano para a API do IndexNow.
7. Implementação em Python#
Para serviços em FastAPI, Django ou scripts em lote de scraping e migração de banco:
# indexnow.py
import requests
def notify_indexnow(host: str, key: str, urls: list[str]) -> dict:
if not urls:
return {"ok": False, "error": "A lista de URLs está vazia"}
endpoint = "https://api.indexnow.org/indexnow"
batch = urls[:10000]
payload = {
"host": host,
"key": key,
"keyLocation": f"https://{host}/{key}.txt",
"urlList": batch
}
try:
response = requests.post(
endpoint,
json=payload,
headers={"Content-Type": "application/json; charset=utf-8"},
timeout=10
)
if response.status_code in (200, 202):
return {"ok": True, "status_code": response.status_code, "count": len(batch)}
return {
"ok": False,
"status_code": response.status_code,
"error": response.text
}
except requests.RequestException as e:
return {"ok": False, "error": str(e)}
# Exemplo de uso:
# res = notify_indexnow("meudominio.com.br", "2cd1cb62283949fbb51a9f02e642cb7d", [
# "https://meudominio.com.br/artigo-tecnico.html"
# ])
# print(res)
8. Automação em CI/CD com GitHub Actions#
Em sites estáticos (gerados com scripts próprios, Astro, 11ty ou Hugo) hospedados no GitHub Pages, Cloudflare Pages ou Vercel, o melhor momento para notificar os motores de busca é no final do pipeline de deploy:
# .github/workflows/deploy-and-index.yml
name: Deploy & Notify IndexNow
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build do site
run: npm run build
- name: Submeter URLs atualizadas ao IndexNow
env:
INDEXNOW_KEY: ${{ secrets.INDEXNOW_KEY }}
run: |
curl -s -X POST "https://api.indexnow.org/indexnow" \
-H "Content-Type: application/json; charset=utf-8" \
-d '{
"host": "meudominio.com.br",
"key": "'"$INDEXNOW_KEY"'",
"keyLocation": "https://meudominio.com.br/'"$INDEXNOW_KEY"'.txt",
"urlList": [
"https://meudominio.com.br/"
]
}'
9. Tabela de códigos de status HTTP do IndexNow#
Quando você estiver depurando logs ou verificando o retorno da chamada, estes são os status emitidos pelo endpoint:
| Status HTTP | O que significa | Ação recomendada |
|---|---|---|
| 200 OK | URLs validadas e processadas na hora. | Nenhuma. Operação concluída. |
| 202 Accepted | URLs recebidas e enfileiradas para processamento posterior. | Comportamento normal em lotes maiores ou picos de carga. |
| 400 Bad Request | JSON inválido, falta de campo obrigatório ou URL sem https://. | Valide a sintaxe do payload e garanta URLs absolutas. |
| 403 Forbidden | Chave não bate com o arquivo {chave}.txt ou o arquivo está inacessível. | Verifique se a rota pública do arquivo responde 200 sem bloqueio de WAF/Cloudflare. |
| 422 Unprocessable Entity | As URLs enviadas não pertencem ao host indicado no JSON. | Verifique se o domínio do host coincide estritamente com as URLs da lista. |
| 429 Too Many Requests | Limite de requisições atingido. | Agrupe os envios em lotes periódicos de 5 a 10 minutos. |
10. Boas práticas para o ambiente de produção#
- Submeta apenas URLs canônicas limpas: Nunca envie links com
noindex, redirecionamentos 301 provisórios ou rotas bloqueadas norobots.txt. O IndexNow deve receber apenas o destino canônico final. - Remova parâmetros de campanha: Elimine tags de tracking como
?utm_ou?fbclid=antes de montar o payload. - Agrupe em lotes quando o volume for alto: Se o seu banco recebe dezenas de atualizações por minuto, não dispare um HTTP POST por linha inserida. Acumule os paths em uma fila (como Redis) e dispare um lote consolidado a cada 5 ou 10 minutos.
- Avise sobre conteúdos removidos (404/410): Se você aposentou uma página e ela passou a retornar 404 ou 410, submeta essa URL ao IndexNow. Os motores atualizam o índice e removem o resultado mais rápido, diminuindo o tráfego desperdiçado com visitantes caindo em telas de erro.
Conclusão: indexação ativa e sem fricção#
Neste guia compartilhei o caminho completo do IndexNow — da chave de host ao pipeline de CI/CD. O custo de implementação é baixo e o ganho de descoberta é imediato: vale para qualquer site, SaaS ou API que precise que o conteúdo novo seja encontrado em minutos, não em dias.
O IndexNow não faz milagres de ranqueamento sozinho nem substitui a qualidade técnica do conteúdo, mas zera a frustração do ciclo passivo de rastreamento: em vez de esperar dias pelo crawler, o seu servidor assume o controle e avisa aos motores de busca exatamente quando o conteúdo está pronto.
Este artigo foi útil?
Deixe uma reação rápida para apoiar o conteúdo:
Este post está licenciado sob CC BY-NC.



Comentários
Participe da discussão abaixo.
0 comentários