**TL;DR.** IndexNow é uma API padronizada e gratuita que permite notificar instantaneamente o Bing, Yandex, Seznam e Naver sobre mudanças de URL. Para um site de ecommerce, isso significa que um novo produto aparece no Bing em minutos, em vez de dias. Implemente como uma fila drenada por um trabalho em segundo plano, não chamadas síncronas.

## O que é o IndexNow

IndexNow é um protocolo aberto anunciado pela Microsoft e Yandex em 2021. Os motores de busca que participam aceitam um simples HTTP POST listando URLs que mudaram; eles rastreiam essas URLs com prioridade.

Motores participantes:

- Bing
- Yandex
- Seznam (Tcheco)
- Naver (Coreano)

Notavelmente ausente: Google. O Google não participa do IndexNow; para o Google, você depende de rastreamentos regulares ou da API de Indexação (que é limitada a anúncios de emprego, transmissões ao vivo e alguns outros tipos de conteúdo).

## Como funciona

1. Você gera uma chave de API (qualquer string aleatória de 32 caracteres ou mais).
2. Você hospeda a chave em `https://seusite.com/{key}.txt` — o corpo contém apenas a chave. Isso verifica que você controla o domínio.
3. Você POST para `https://api.indexnow.org/indexnow` (ou qualquer endpoint IndexNow de um motor participante) com:

```json
{
  "host": "seusite.com",
  "key": "sua-chave-api",
  "keyLocation": "https://seusite.com/sua-chave-api.txt",
  "urlList": [
    "https://seusite.com/produtos/bolsa-de-couro",
    "https://seusite.com/produtos/casaco-de-lã"
  ]
}
```

4. O motor coloca as URLs na fila para rastreamento.

É isso. Sem autenticação além da verificação de propriedade do arquivo-chave, sem sistema de token de limite de taxa.

## Quando notificar

Notifique o IndexNow sempre que uma URL publicamente indexável mudar de forma significativa:

| Evento                                | Notificação                                   |
| ------------------------------------- | --------------------------------------------- |
| Produto criado                        | Notifique a nova URL do produto               |
| Produto atualizado (título, descrição) | Notifique a URL do produto                    |
| Slug do produto alterado              | Notifique AMBAS as URLs antiga e nova        |
| Produto despublicado ou excluído      | Notifique a URL (o motor verá 404/410 e descartará) |
| Categoria criada                      | Notifique a URL da categoria                  |
| Categoria atualizada                  | Notifique a URL da categoria                  |
| Marca ou página criada/atualizada     | Notifique a URL                              |
| Sitemap regenerado                   | Opcionalmente notifique a URL do sitemap      |

Não notifique para:

- Atualizações de inventário que não mudam o conteúdo.
- Mudanças apenas internas (registro de auditoria de preços, ações administrativas).
- Importações em massa — agrupe a notificação pós-importação.

## Fila, não chame de forma síncrona

Uma implementação ingênua:

```ts
// RUIM: chamada IndexNow síncrona dentro da salvaguarda do produto
async function saveProduct(product: Product) {
  await db.update(...);
  await fetch('https://api.indexnow.org/indexnow', { method: 'POST', body: ... });
  // ↑ adiciona 100–500ms a cada salvaguarda
}
```

Melhor:

```ts
// BOM: enfileirar, drenar assíncrono
async function saveProduct(product: Product) {
  await db.update(...);
  await enqueueIndexNow(product.url);
  // retorna imediatamente
}

// Tarefa em segundo plano, executa a cada 1–5 minutos
export const indexNowDrainTask = task({
  id: 'indexnow-drain',
  cron: '*/1 * * * *',
  run: async () => {
    const urls = await fetchPendingUrls(MAX_BATCH);
    if (urls.length === 0) return;
    await postToIndexNow(urls);
    await markAsDrained(urls);
  },
});
```

Benefícios da enfileiração:

- As mutações permanecem rápidas.
- Falhas não quebram fluxos voltados para o usuário.
- Fácil de agrupar.
- Fácil de registrar por tentativa.
- Sobrevive a períodos de inatividade da plataforma (a fila persiste, drena quando a API está de volta).

## Lidando com mudanças de slug

Um caso sutil comum: um slug de produto muda de `/produtos/antigo` para `/produtos/novo`. Enfileire AMBAS as URLs:

- A nova URL precisa ser rastreada para que o motor a adicione ao índice.
- A URL antiga precisa ser rastreada para que o motor a recupere novamente, veja o 301 (ou 410) e a descarte.

```ts
async function updateProductSlug(productId: string, oldSlug: string, newSlug: string) {
  await db.products.update({ id: productId, slug: newSlug });
  await writeRedirect({ from: `/produtos/${oldSlug}`, to: `/produtos/${newSlug}`, status: 301 });
  await enqueueIndexNow([`/produtos/${oldSlug}`, `/produtos/${newSlug}`]);
}
```

Para exclusões, escreva um redirecionamento 410 (tabela de caminhos excluídos da Ordiko) e notifique a URL. O motor vê 410 e remove a URL de seu índice permanentemente.

## Agrupamento

A API aceita até 10.000 URLs por POST. Padrão comum de ecommerce: drenar 100–500 URLs por minuto, dependendo do volume de mutações.

```ts
const MAX_BATCH = 500;
const BATCH_INTERVAL_MS = 60_000;

export const indexNowDrainTask = task({
  id: 'indexnow-drain',
  cron: '*/1 * * * *',
  run: async () => {
    const urls = await fetchPendingUrls(MAX_BATCH);
    if (urls.length === 0) return;

    try {
      const res = await fetch('https://api.indexnow.org/indexnow', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          host: 'seusite.com',
          key: process.env.INDEXNOW_KEY,
          keyLocation: `https://seusite.com/${process.env.INDEXNOW_KEY}.txt`,
          urlList: urls,
        }),
      });

      if (res.status === 200 || res.status === 202) {
        await markAsDrained(urls, 'ok');
      } else if (res.status === 429) {
        await markAsDrained(urls, 'retry'); // re-enfileirar para o próximo ciclo de drenagem
      } else {
        await markAsDrained(urls, 'failed', `${res.status} ${await res.text()}`);
      }
    } catch (err) {
      await markAsDrained(urls, 'failed', String(err));
    }
  },
});
```

## Registro

Cada notificação (sucesso ou falha) deve ser registrada. Esquema de tabela de auditoria útil:

```sql
CREATE TABLE seo_revalidation_events (
  id SERIAL PRIMARY KEY,
  store_id UUID NOT NULL,
  url TEXT NOT NULL,
  step TEXT NOT NULL, -- 'indexnow' | 'revalidate_tag' | 'sitemap'
  status TEXT NOT NULL, -- 'ok' | 'failed' | 'retry'
  error TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
```

Isso permite que você responda "por que este produto não está aparecendo no Bing?" consultando a tabela pela URL do produto.

## Verificando

O Bing Webmaster Tools tem um painel dedicado ao IndexNow:

1. Faça login em [bing.com/webmasters](https://www.bing.com/webmasters).
2. Adicione seu site.
3. Navegue até **IndexNow** na barra lateral.
4. Veja as submissões ao longo do tempo, contagens de sucesso/falha.

O Yandex Webmaster tem relatórios equivalentes sob **Indexação**.

## Como a Ordiko implementa o IndexNow

- A coluna `stores.indexNowApiKey` armazena a chave por loja.
- O arquivo-chave é servido em `/{key}.txt` automaticamente.
- Cada serviço de entidade (`product.service.ts`, `category.service.ts`, etc.) chama `notifyIndexNowOnChange(url)` em mutações.
- Fila: tabela `pending_indexnow` com `(storeId, url, enqueuedAt)`.
- A tarefa cron do Trigger.dev `indexnow-drain.task.ts` é executada a cada minuto.
- Cada drenagem registra em `seo_revalidation_events` com `step: "indexnow", status: ok|failed|retry`.

## FAQ

**O IndexNow funciona para o Google?**
Não. O Google não participa do IndexNow. Para o Google, envie seu sitemap XML e dependa de ciclos de rastreamento regulares, ou use a própria API de Indexação do Google para tipos de conteúdo limitados (anúncios de emprego, eventos ao vivo). Para Bing, Yandex, Seznam e Naver, o IndexNow é a maneira mais rápida de sinalizar atualizações.

**Quantas URLs posso enviar por chamada?**
Até 10.000 URLs por POST. A API retorna 200-OK para submissões aceitas. Para volumes maiores, agrupe e limite a taxa — a taxa segura típica é de 1–10 lotes por minuto. O trabalho de drenagem na Ordiko envia um lote por minuto por padrão.

**O que acontece se eu enviar de forma muito agressiva?**
Você recebe 429 Too Many Requests. A API não o bane — diminua, tente novamente com retrocesso exponencial. A submissão sustentada de URLs inalteradas em alto volume pode levar à despriorização, mas não ao bloqueio.

**Como a Ordiko implementa o IndexNow?**
Cada serviço de entidade (produto, categoria, marca, página) chama notifyIndexNowOnChange na criação/atualização/exclusão/despublicação. A tabela de fila pending_indexnow mantém entradas; uma tarefa cron do Trigger.dev indexnow-drain.task.ts a drena em um cronograma. Cada notificação é registrada em seo_revalidation_events para auditoria.