Saltar al contenido

Seguimiento de crawlers de IA

Actualizado el 18 de octubre de 2026

Los crawlers de IA no ejecutan JavaScript, por lo que el script de Statsdot nunca los detecta. Para saber cuáles de ellos leen tu sitio, tu servidor debe reportar sus peticiones a Statsdot. A continuación, verás en el informe del sitio cada crawler con su número de peticiones y las páginas que más leen.

Solo se almacenan las peticiones cuyo user agent coincide con un crawler del directorio de crawlers de IA, junto con el crawler, la ruta sin cadena de consulta y el código de estado. Las peticiones de personas se ignoran incluso si tu servidor las envía, y no se almacena ninguna dirección IP.

1. Actívalo

En los ajustes del sitio, bajo Seguimiento de crawlers de IA, elige Activar y crear una clave. Copia la clave (empieza por sdb_). La clave solo puede reportar peticiones de crawlers; si se filtra, genera una nueva en el mismo lugar.

2. Conecta tu sitio

WordPress

En Ajustes > Statsdot del plugin, pega la clave en Seguimiento de crawlers de IA y guarda. Eso es todo.

Laravel

Añade este middleware al grupo web. Reporta después de que la respuesta haya sido enviada, por lo que los crawlers no sufren ralentizaciones:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Symfony\Component\HttpFoundation\Response;

class ReportAiCrawlers
{
    private const TOKENS = ['gptbot', 'oai-searchbot', 'chatgpt-user', 'claudebot', 'claude-user', 'claude-searchbot', 'perplexitybot', 'perplexity-user', 'google-extended', 'googlebot', 'bingbot', 'applebot', 'meta-externalagent', 'bytespider', 'ccbot', 'amazonbot', 'duckassistbot', 'mistralai-user'];

    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);
    }

    public function terminate(Request $request, Response $response): void
    {
        $agent = strtolower((string) $request->userAgent());

        foreach (self::TOKENS as $token) {
            if (str_contains($agent, $token)) {
                Http::timeout(2)->withHeaders(['X-Statsdot-Bot-Key' => config('services.statsdot.bot_key')])
                    ->post('https://statsdot.com/api/bots', [
                        'user_agent' => $request->userAgent(),
                        'url' => $request->fullUrl(),
                        'status' => $response->getStatusCode(),
                    ]);

                return;
            }
        }
    }
}

Pon la clave en .env como STATSDOT_BOT_KEY y en config/services.php como 'statsdot' => ['bot_key' => env('STATSDOT_BOT_KEY')]. Statsdot reconoce más crawlers que la lista corta anterior; amplíala con los tokens del directorio que te interesen.

PHP plano

Al final de tu controlador frontal (por ejemplo, index.php), después de que la página se haya enviado:

$agent = $_SERVER['HTTP_USER_AGENT'] ?? '';

if (preg_match('/gptbot|oai-searchbot|chatgpt-user|claudebot|claude-user|claude-searchbot|perplexitybot|perplexity-user|googlebot|bingbot|applebot|meta-externalagent|bytespider|ccbot|amazonbot/i', $agent)) {
    if (function_exists('fastcgi_finish_request')) {
        fastcgi_finish_request();
    }

    $context = stream_context_create(['http' => [
        'method' => 'POST',
        'timeout' => 2,
        'header' => "Content-Type: application/json\r\nX-Statsdot-Bot-Key: sdb_your_key\r\n",
        'content' => json_encode([
            'user_agent' => $agent,
            'url' => 'https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'],
            'status' => http_response_code(),
        ]),
    ]]);

    @file_get_contents('https://statsdot.com/api/bots', false, $context);
}

Node.js y Express

const CRAWLERS = /gptbot|oai-searchbot|chatgpt-user|claudebot|claude-user|claude-searchbot|perplexitybot|perplexity-user|googlebot|bingbot|applebot|meta-externalagent|bytespider|ccbot|amazonbot/i;

app.use((req, res, next) => {
  const agent = req.get('user-agent') || '';
  if (CRAWLERS.test(agent)) {
    res.on('finish', () => {
      fetch('https://statsdot.com/api/bots', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'X-Statsdot-Bot-Key': process.env.STATSDOT_BOT_KEY },
        body: JSON.stringify({ user_agent: agent, url: `${req.protocol}://${req.get('host')}${req.originalUrl}`, status: res.statusCode }),
      }).catch(() => {});
    });
  }
  next();
});

Next.js

En middleware.ts, reporta sin retrasar la respuesta:

import { NextResponse, type NextRequest, type NextFetchEvent } from 'next/server';

const CRAWLERS = /gptbot|oai-searchbot|chatgpt-user|claudebot|claude-user|claude-searchbot|perplexitybot|perplexity-user|googlebot|bingbot|applebot|meta-externalagent|bytespider|ccbot|amazonbot/i;

export function middleware(request: NextRequest, event: NextFetchEvent) {
  const agent = request.headers.get('user-agent') ?? '';
  if (CRAWLERS.test(agent)) {
    event.waitUntil(fetch('https://statsdot.com/api/bots', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'X-Statsdot-Bot-Key': process.env.STATSDOT_BOT_KEY! },
      body: JSON.stringify({ user_agent: agent, url: request.url }),
    }).catch(() => {}));
  }
  return NextResponse.next();
}

El endpoint

POST https://statsdot.com/api/bots con la cabecera X-Statsdot-Bot-Key y un cuerpo JSON {"user_agent": "...", "url": "...", "status": 200}, o {"events": [...]} con hasta 100 peticiones. Responde con 202 y el número registrado. Hasta 600 peticiones por minuto por clave.

Límites

Cada cuenta registra hasta 100.000 peticiones de crawlers al mes en todos sus sitios; una vez superado este límite, no se registrarán más hasta el mes siguiente. Las peticiones de crawlers no cuentan para los eventos de tu plan. Se conservan durante el mismo tiempo que tus visitas (24 meses).