Powrót na blog
Klasyfikacja Ruchu Botów

Which AI agents request your content? A guide to reading page-level crawler logs

autor: travatar-admin·
Which AI agents request your content? A guide to reading page-level crawler logs

How to inspect AI agent requests by page and content type

A page-level log audit shows which URLs suspected AI agents requested, what the server returned, and where a closer investigation is useful. Start with server or CDN request records, group them by agent and URL, then compare response patterns with content type and conversion data. Treat a matching User-Agent as a lead, not proof of who made the request.

How to see which pages AI agents request

Export server or CDN logs, filter requests against a maintained list of AI crawler User-Agent strings, and aggregate hits, status codes, response sizes and timestamps by URL. That produces a practical list of pages to investigate while keeping the distinction between an agent *claiming* an identity and an agent whose identity has been verified.

Which records should the audit use?

Use request-level logs from the system that receives the traffic. Depending on the site, that may be a web server such as Nginx, a CDN export, or logs stored for analysis in S3 access logs or BigQuery. A CDN dashboard can help spot a pattern, but request records are needed to inspect the URLs and responses behind it.

For each request, extract the timestamp, client IP, User-Agent, requested URL, response status and response size. Retain the response Content-Type if it is available. Field names and logging formats vary, so establish a consistent set of names before combining exports. Keep the original records: an aggregated dashboard cannot settle a later question about a particular request.

The first filter should be deliberately narrow. Maintain a list of User-Agent strings taken from the relevant operators' current documentation, then search for those strings in the log's User-Agent field. AI traffic can include crawlers fetching content for different purposes and fetches associated with a live user interaction. Do not group those activities into a single measure of demand.

How can a team run a quick page-level check?

The following examples assume a normalized JSON Lines file called `requests.jsonl`. Each line is one request with keys named `timestamp`, `client_ip`, `user_agent`, `url`, `status`, `bytes` and, where recorded, `content_type`. These are example keys for the normalized file, not promised field names in any server or CDN export.

For an ad-hoc inspection, set a User-Agent pattern based on the agents being investigated and print matching request details:

“`sh

UA_PATTERN='replace-with-agent-pattern'

jq -r –arg pattern "$UA_PATTERN" '

select((.user_agent // "" | ascii_downcase) |

contains($pattern | ascii_downcase)) |

[.timestamp, .client_ip, .user_agent, .url, .status, .bytes] |

@tsv

' requests.jsonl

“`

A `grep` check is useful when inspecting a familiar raw-log format, but it searches text rather than a reliably parsed User-Agent field:

“`sh

grep -i — "$UA_PATTERN" access.log

“`

For a quick URL count from the normalized file, `jq` can produce a URL column for `awk` to tally:

“`sh

jq -r –arg pattern "$UA_PATTERN" '

select((.user_agent // "" | ascii_downcase) |

contains($pattern | ascii_downcase)) |

.url

' requests.jsonl |

awk '{hits[$0]++} END {for (url in hits) print hits[url], url}' |

sort -nr

“`

These commands identify candidates. The pattern must be replaced with a User-Agent value relevant to the audit, and raw-log parsing must follow the actual export format. For repeated reporting, use a small `jq` or Python pipeline that groups by both agent and URL rather than relying on a text search.

Which URLs deserve investigation first?

Build a view with one row per claimed agent and URL. Show request count, status-code distribution, typical and unusual response sizes, first and latest request times, and a short time series of hits. This makes a top-requested-page list more useful than a count alone.

Use the following as triage rules, not as evidence that a page influenced an AI answer:

  • Investigate frequently requested URLs to understand what an agent could access and whether the returned page contains the intended information.
  • Review pages with repeated successful responses alongside weak human conversion signals. Compare traffic and conversion data carefully; a crawler request is not a human visit or a conversion opportunity.
  • Inspect unusually small or large responses against the page's normal response size. The request record may point to an error page, redirect, incomplete response or another delivery issue.
  • Review recurring error statuses and the timestamps around them. A time-series chart can reveal a burst that a total hit count conceals.

When identity matters, escalate beyond the User-Agent. Compare the client IP with a range published by the claimed operator, and use reverse-DNS as an additional investigative signal where appropriate. An IP or hostname result should be assessed against that operator's current guidance; neither a User-Agent string nor a reverse-DNS result alone proves attribution.

How to analyze AI crawler requests by content type

Classify each candidate request using its URL path, recorded response Content-Type and file extension, then summarize hits, statuses, response sizes and timing by agent and content type. This separates requests for HTML pages from requests for PDFs, API/JSON responses and images without assuming that a particular agent prefers any one format.

How should request records be classified?

Start with the response Content-Type when it is recorded and represents the response actually served. Use the URL path and extension as supporting clues. For example, a path ending in `.pdf` suggests a PDF, while a response labeled `application/pdf` provides stronger evidence about what was returned. A `/api/` path may suggest an endpoint, but its response Content-Type determines whether it returned JSON, HTML or something else.

The script below reads the normalized JSON Lines file used above. It assigns a practical content label and summarizes each User-Agent, content-type and URL combination. The matching rule is intentionally simple: supply a pattern that fits the current investigation, then review the matched records before treating the label as an agent identity.

“`python

import json

import sys

from collections import Counter, defaultdict

from statistics import median

from urllib.parse import urlparse

pattern = sys.argv[1].lower()

groups = defaultdict(lambda: {

"statuses": Counter(), "sizes": [], "times": []

})

def content_kind(record):

content_type = (record.get("content_type") or "").lower()

path = urlparse(record.get("url") or "").path.lower()

if "text/html" in content_type:

return "HTML"

if "application/pdf" in content_type:

return "PDF"

if "application/json" in content_type or "+json" in content_type:

return "API/JSON"

if content_type.startswith("image/"):

return "image"

if path.endswith((".html", ".htm")):

return "HTML (extension)"

if path.endswith(".pdf"):

return "PDF (extension)"

if path.endswith(".json"):

return "API/JSON (extension)"

if path.endswith((".png", ".jpg", ".jpeg", ".webp", ".svg")):

return "image (extension)"

return "unclassified"

with open("requests.jsonl", encoding="utf-8") as source:

for line in source:

record = json.loads(line)

agent = record.get("user_agent") or ""

if pattern not in agent.lower():

continue

key = (agent, content_kind(record), record.get("url") or "")

group = groups[key]

group["statuses"][str(record.get("status"))] += 1

if record.get("bytes") is not None:

group["sizes"].append(int(record["bytes"]))

if record.get("timestamp"):

group["times"].append(record["timestamp"])

for (agent, kind, url), group in sorted(

groups.items(), key=lambda item: -sum(item[1]["statuses"].values())

):

sizes = group["sizes"]

times = sorted(group["times"])

print(json.dumps({

"agent": agent,

"content_type": kind,

"url": url,

"hits": sum(group["statuses"].values()),

"statuses": dict(group["statuses"]),

"median_bytes": median(sizes) if sizes else None,

"first_seen": times[0] if times else None,

"last_seen": times[-1] if times else None

}))

“`

Run it with `python audit.py 'agent-pattern'`. A large export may call for a scheduled pipeline or query tool instead, but the classification logic should remain inspectable. In particular, extension-based labels need review: a URL may have no extension, and an error response may have a different Content-Type from the requested document.

How does content type change the next action?

For HTML pages, inspect the returned status and rendered content before changing copy or page structure. A successful request shows that the server returned a response; it does not show that an AI system used, cited or recommended the page. For PDFs, inspect whether the document and its response are suitable for the intended audience. For API/JSON paths, establish whether the request is expected and authorized before treating its volume as a content opportunity. Images and unclassified responses warrant separate review rather than being folded into a page count.

Travatar is a SaaS platform for AI visibility analytics and web-traffic intelligence. We monitor brand visibility in AI Search, classify bot traffic and help teams assess its impact on conversion. When a manual audit produces more candidates than a team can review, move toward a workflow that connects traffic classification, page-level investigation and conversion analysis. Evaluate any platform against the log sources and reporting detail the team actually needs; do not assume that a dashboard identifies every agent or proves that a fetch caused an AI mention.

The immediate next step is modest: export a request sample, normalize its fields, produce the agent-by-URL list, and inspect the highest-priority records before changing content or crawler access rules.

FAQ

How do I get server or CDN logs if I only use a hosted CMS?

Ask the hosting provider or CDN which request-level exports are available and whether they include URL, timestamp, User-Agent, status, response size and client IP. If request logs are unavailable, use the available traffic dashboard for an initial view, but do not treat an aggregate chart as a page-level audit.

What are the most reliable signals to prove an AI agent fetched a page?

A request record links a claimed User-Agent and client IP to a URL, timestamp and server response. To strengthen attribution, compare the IP with the claimed operator's published guidance and investigate DNS information; a User-Agent string by itself is not proof of identity.

Can I automate content-type mapping for large logs, or is manual parsing required?

A parser can assign initial labels from response Content-Type, URL paths and extensions. Review exceptions and a sample of classified records, especially where Content-Type is absent, the URL has no extension, or the server returned an error.

What quick checks detect User-Agent spoofing?

Compare the request's client IP with ranges published by the claimed operator and investigate its reverse-DNS result against that operator's guidance. Treat a mismatch as a reason to question the attribution, not as a complete classification of the visitor.

When should I move from scripts to a vendor platform for this work?

Consider a platform when recurring log exports, agent classification and comparison with conversion data consume more effort than the team can sustain manually. Define the required data sources, page-level views and validation process before evaluating a vendor.