Meça a visibilidade da sua marca em respostas de IA com Python

Rodamos 45 prompts no Google AI Mode três vezes cada. Em 43 de 45, as marcas citadas mudaram entre execuções idênticas. Monitoramento de marca: método, números e script.

Para medir a visibilidade de uma marca em respostas de IA, rode um conjunto fixo de prompts na superfície de IA várias vezes, conte a parcela de respostas que citam a marca com uma regex de limite de palavra e informe um intervalo, não um número único. Uma única execução não é uma medição: em 19 de outubro de 2026 enviamos 45 prompts ao Google AI Mode três vezes cada (135 respostas), e em 43 dos 45 prompts o conjunto de marcas citadas foi diferente entre execuções idênticas. O script abaixo faz o trabalho todo em cerca de 70 linhas de Python, a 10 créditos por resposta.

Por que uma única execução de uma resposta de IA não é uma medição?

Porque o mesmo prompt devolve uma resposta diferente a cada vez. Nas 135 respostas, apenas 1 dos 45 prompts produziu três textos idênticos. Entre os pares (marca, prompt) em que a marca apareceu ao menos uma vez, 138 de 302 (46%) ficaram de fora em pelo menos uma das três execuções. As respostas são longas (mediana de 396 palavras) e citam cerca de cinco das marcas monitoradas cada uma, então pequenas mudanças de redação fazem marcas entrarem e saírem.

Se você confere sua marca uma vez por semana e a vê aparecer e depois sumir, isso é em grande parte esse ruído, não uma mudança na sua visibilidade. O post sobre o AI Mode cobre o formato da resposta; este trata do que fazer com muitas respostas.

Como isso foi medido?

Três categorias de produto que desenvolvedores pesquisam, 15 prompts em cada, escritos do jeito que as pessoas digitam: “best vector database”, “best vector database for rag”, “best async python web framework”, “web scraping api with a free tier”. Nenhum prompt cita uma marca. Cada prompt foi para /v1/ai com country=us e language=en, três vezes. 135 chamadas, 1.350 créditos, 6,9 segundos em média (duas chamadas de confirmação para api.fastaf.co no mesmo dia levaram 6,1 e 3,6 segundos, cada uma informando X-FastAF-Credits-Used: 10). Uma chamada retornou HTTP 200 com uma resposta vazia e foi cobrada em 10 créditos na época; depois dessa medição mudamos a regra, e uma resposta vazia do AI Mode agora é reembolsada. Tratada como dado, ela teria contado como “nenhuma marca citada” para todo mundo, então o script continua repetindo respostas vazias.

Escolhi 14 marcas por categoria de antemão. As menções são contadas apenas no texto da resposta (parágrafos, itens de lista e células de tabela), não nos títulos das páginas citadas. “Posição” é a ordem da primeira aparição entre as marcas monitoradas. Nada aqui é sentimento, recomendação ou ranking dos produtos: é o que uma superfície do Google disse em um dia.

O que os dados mostram?

A taxa de menção é a parcela das 45 respostas que citam a marca. O intervalo é um intervalo bootstrap de 95% que reamostra prompts, então inclui o efeito de quais 15 prompts você escolheu. As execuções 1 a 3 são as taxas que você teria informado com uma única passada.

Categoria Marca Taxa Intervalo de 95% Execução 1 / 2 / 3
Banco de dados vetorial Qdrant 96% 87-100% 93 / 93 / 100
Pinecone 84% 71-96% 80 / 80 / 93
Weaviate 73% 56-91% 67 / 73 / 80
Chroma 53% 31-73% 60 / 53 / 47
Framework Python Flask 91% 82-98% 80 / 93 / 100
Starlette 49% 31-67% 53 / 40 / 53
Litestar 38% 20-58% 40 / 27 / 47
API de scraping Firecrawl 71% 51-91% 80 / 67 / 67
ScraperAPI 47% 29-64% 53 / 40 / 47
Apify 42% 24-60% 33 / 40 / 53

Três coisas chamam a atenção. Primeiro, as marcas no meio de uma categoria oscilam de 10 a 20 pontos entre passadas únicas (Apify de 33% a 53%); a variação média das marcas acima de 20% foi de 11 a 14 pontos. Segundo, intervalos de 15 prompts ficam em mais ou menos 20 pontos no meio da faixa, então uma mudança de “+5 pontos” de uma semana para outra é invisível. Terceiro, os rankings por passada única diferem: na categoria de scraping, o ScraperAPI foi o terceiro na passada 1, e o Apify só substituiu a Oxylabs no top cinco na passada 3. Taxa e posição também são coisas diferentes: o Pinecone foi citado antes (posição média 2,2) que o Qdrant (2,9), apesar de uma taxa menor.

As respostas citam os sites das próprias marcas?

Na maioria das vezes, não. Dos hostnames distintos citados por resposta, a parcela que pertencia a uma das marcas monitoradas foi de 44% para APIs de scraping, 30% para bancos de dados vetoriais e 31% para frameworks Python. O resto eram terceiros: o reddit.com apareceu em 52 das 135 respostas (39%), depois github.com, YouTube e blogs de comparação. Quem escreve sobre você importa tanto quanto o que o seu próprio site diz.

As fontes também variam: duas execuções do mesmo prompt compartilharam em média 28 a 34% dos sites citados (sobreposição de Jaccard), então “quais páginas a resposta cita” também precisa de repetições.

Como rodar isso por conta própria em Python?

Defina FASTAF_API_KEY, preencha PROMPTS com 15 ou mais prompts e BRANDS com os nomes que importam, e rode. Cada chamada é acrescentada a runs.jsonl, então uma falha ou uma nova execução pula o que já está feito.

import json, os, random, re, time
from concurrent.futures import ThreadPoolExecutor

import requests

API = "https://api.fastaf.co/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['FASTAF_API_KEY']}"}
REPEATS = 3
PROMPTS = ["best vector database", "best vector database for rag", "best self hosted vector database"]  # use 15
BRANDS = {  # case-sensitive on purpose: "Falcon" is not "falcon"
    "Qdrant": r"\bQdrant\b", "pgvector": r"\bpgvector\b", "Milvus": r"\bMilvus\b",
    "Pinecone": r"\bPinecone\b", "Weaviate": r"\bWeaviate\b", "Chroma": r"\bChroma(?:DB)?\b",
}

def text_of(node):
    """Every visible string of the answer: paragraphs, list items, table cells."""
    if isinstance(node, str):
        return [node]
    if isinstance(node, list):
        return [s for v in node for s in text_of(v)]
    if isinstance(node, dict):
        return [s for k, v in node.items() if k in ("snippet", "text", "items", "table") for s in text_of(v)]
    return []

def ask(prompt):
    for attempt in range(5):
        r = requests.get(f"{API}/ai", headers=HEADERS, timeout=90,
                         params={"query": prompt, "country": "us", "language": "en"})
        if r.status_code == 200:
            text = "\n".join(text_of(r.json()["text_blocks"]))
            if text:  # an empty 200 is not "no brand mentioned": retry it
                return text
        time.sleep(3 * (attempt + 1))
    raise RuntimeError(f"gave up on {prompt!r}")

def collect(path="runs.jsonl"):
    done = {(r["prompt"], r["run"]) for r in map(json.loads, open(path))} if os.path.exists(path) else set()
    jobs = [(p, n) for p in PROMPTS for n in range(1, REPEATS + 1) if (p, n) not in done]
    def work(job):
        prompt, n = job
        row = {"prompt": prompt, "run": n, "text": ask(prompt)}
        with open(path, "a") as f:  # one line per finished call
            f.write(json.dumps(row) + "\n")
    with ThreadPoolExecutor(max_workers=5) as pool:
        list(pool.map(work, jobs))

def interval(per_prompt, n_boot=4000):
    """95% bootstrap interval resampling PROMPTS, keeping each prompt's runs together."""
    rng, k = random.Random(7), len(per_prompt)
    rates = sorted(sum(rng.choices(per_prompt, k=k)) / k for _ in range(n_boot))
    return rates[int(0.025 * n_boot)], rates[int(0.975 * n_boot)]

def report(path="runs.jsonl"):
    rows = [json.loads(line) for line in open(path)]
    print(f"{'brand':10}{'rate':>6}{'95% CI':>10}{'run1':>6}{'run2':>6}{'run3':>6}")
    table = []
    for brand, rx in BRANDS.items():
        hit = {(r["prompt"], r["run"]): bool(re.search(rx, r["text"])) for r in rows}
        per_prompt = [sum(hit[(p, n)] for n in range(1, REPEATS + 1)) / REPEATS for p in PROMPTS]
        by_run = [sum(hit[(p, n)] for p in PROMPTS) / len(PROMPTS) for n in range(1, REPEATS + 1)]
        table.append((sum(per_prompt) / len(PROMPTS), brand, interval(per_prompt), by_run))
    for rate, brand, (lo, hi), by_run in sorted(table, reverse=True):
        print(f"{brand:10}{rate:>6.0%}{f'{lo:.0%}-{hi:.0%}':>10}" + "".join(f"{x:>6.0%}" for x in by_run))

collect()
report()

Com a lista de três prompts acima, em 19 de outubro de 2026 ele imprimiu pgvector, Weaviate, Qdrant e Milvus a 100% em todas as execuções, Chroma a 78% (67 / 67 / 100) e Pinecone a 67%.

Quais erros de contagem mudam os números?

Maiúsculas e limites de palavra. Sem \b, “Jina” casa dentro de palavras maiores; sem diferenciar maiúsculas de minúsculas, “Bottle”, “Falcon” e “Reflex” casam com prosa comum, e “scraper API” em minúsculas conta como o produto ScraperAPI. No meu conjunto, a busca sem diferenciar maiúsculas mudou o ScraperAPI de 21 para 22 respostas e a Zyte de 4 para 5, pouco aqui, mas um “falcon” ou “bottle” em minúsculas é fácil de acertar com uma marca de palavra comum. Adicione os apelidos que você quiser (“ChromaDB”, “Bright Data” sem o espaço) e mantenha-os em uma lista revisada. Conte também só a resposta, não a consulta ecoada, ou um prompt que contém o nome da sua marca mede o seu prompt.

Para AI Overviews, a mesma extração funciona no objeto ai_overview dentro de uma resposta do /v1/serp, que custa 5 créditos em vez de 10; esse post cobre presença e fontes por palavra-chave.

O GEO muda o que medir?

O paper do GEO (Aggarwal et al., KDD 2024) relatou até 40% mais visibilidade em respostas de mecanismos generativos no seu benchmark GEO-bench, mas isso é um conjunto testado de edições de conteúdo, não uma garantia para o seu site. O guia do Google sobre recursos de IA generativa, atualizado pela última vez em 10 de julho de 2026 na data desta checagem, diz que “as boas práticas de SEO continuam relevantes” e que nenhuma marcação especial nem arquivo llms.txt é necessário para aparecer na Busca do Google. Então o trabalho continua sendo SEO comum; o que você acrescenta é uma forma de saber se algo se moveu, e a resposta acima é: só com repetições e um intervalo.

Quanto custa?

45 prompts x 3 execuções são 1.350 créditos, cerca de $0,68 no pacote Starter de $10 (20.000 créditos). Uma categoria de 15 prompts com 3 repetições são 450 créditos. Requisições que falham não são cobradas. Veja os preços e a documentação.

Resumindo

Conte as menções com regex de limite de palavra que diferencia maiúsculas de minúsculas, rode cada prompt ao menos três vezes e informe a taxa de menção com um intervalo que reamostra prompts. Nas nossas 135 respostas, 46% das aparições de marca por prompt sumiram em pelo menos uma repetição, e menos da metade das fontes citadas eram os sites das próprias marcas. Comece grátis com 500 créditos e rode o script na sua própria categoria.