Home  /  Methodology  /  Sitemap Benchmark  /  2026-04-26
Methodology · Frozen Artifact

Sitemap Delivery Benchmark — April 26, 2026

Records, TTFB, throughput, and bot-accessibility comparison across Top10Lists.us and three anonymized established content agencies.

Frozen: 2026-04-26 — measurements at this URL will not change.  ·  Permanent dated artifact  ·  GeoLocus Group, a subsidiary of Aryah.ai

Author: Robert Maynard, Cofounder and CEO · LinkedIn →

This page is the reproducibility artifact for the April 26, 2026 GEOlocus.ai press release [9] footnote on sitemap delivery quality. It documents a bot-perspective benchmark across four sites — Top10Lists.us plus three established U.S. content agencies (DA 70+, decade-plus tenure) — using two well-formed bot user agents (Googlebot/2.1 and ClaudeBot/1.0). All raw per-hit measurements and the complete reproduction script are embedded below.

Comparison agency names are withheld from the rendered table and findings. The reproduction script in Section 3 names them by URL because the script's whole purpose is to be re-executable; the published narrative does not depend on naming them.

1. Headline Comparison

Five metrics, four sites, two user agents, ten rapid-fire hits per pair. Top10Lists.us vs the three-site comparison set:

Metric Top10Lists.us Comparison set (anonymized)
Total records (terminal <url> entries) 230,329 642 to 8,755
TTFB to first byte (p50, root sitemap) 86 ms 399 ms to 739 ms
TTLB to last byte (full tree, parallel fetch) 1.64 s 0.90 s to 1.10 s
Records/second throughput 140,000+ 584 to 9,527
Sitemap redirect hops 0 1 to 2
Bots blocked at edge (HTTP 403 to Googlebot) None 1 of 3 sites

Footnote: Three established U.S. content agencies (DA 70+, decade-plus tenure) tested. Specific names withheld from the rendered narrative. Aggregate stats and per-hit raw data are reproducible via the script in Section 3.

2. Methodology

Sites tested

Four sites in total: Top10Lists.us plus three established U.S. content agencies. One of the three comparison sites returns HTTP 403 to Googlebot itself at /sitemap.xml and /robots.txt. That bot-blocking posture is itself a finding and is preserved in the raw measurements as null wall-clock and zero successful 200-status hits.

User agents tested

Googlebot/2.1 and ClaudeBot/1.0 — both the standard, well-formed user-agent strings published by Google and Anthropic respectively. Each (site, UA) pair was measured independently to surface bot-policy asymmetry.

TTFB / TTLB distribution

Ten rapid-fire curl hits to the root sitemap.xml per pair. Redirects followed (-L) so the measurement reflects end-to-end bot ingestion cost including any redirect chain. Pinned to the first-resolved IP per site (--resolve host:443:ip) to factor out DNS round-robin variance. Accept-Encoding compressed. Reported metrics: TTFB p50, TTFB p95, TTLB p50, TTLB p95, and a count of cold spikes >2x the p50.

Records definition

Records are terminal <url> entries — the actual page URLs. Sitemap-index <sitemap><loc> pointers are not counted as records. The published count is content URLs only. Top10Lists.us is a sitemap index with 29 child sitemaps; the 230,329 number is the sum of <url> entries across all 29 children.

Wall-clock-to-tree

A single parallel fetch (concurrency 10, Python ThreadPoolExecutor) of root + all child sitemaps (one level of recursion). Un-pinned — DNS round-robin is allowed — so this number is more representative of real-world bot ingestion than the pinned-IP TTFB distribution. Captured per UA per site.

Per-hit fields captured

  • ttfb — curl time_starttransfer, seconds.
  • ttlb — curl time_total, seconds.
  • size — decompressed bytes returned (size_download).
  • status — HTTP status code (200, 403, etc.).
  • redirectsnum_redirects count.
  • url_final — effective URL after redirect chain (url_effective).
  • ts — Unix timestamp at hit time.

Run timestamp

2026-04-26, UTC (per-hit timestamps captured in each raw JSONL). The script is reproducible — see Section 3.

3. Reproducibility Script

The full contents of run.py. Anyone with Python 3 and curl on PATH can re-run the benchmark by saving this file and executing python3 run.py. The script names the four concrete sites because reproducibility requires it; the published table and findings remain anonymized.

#!/usr/bin/env python3
"""
Sitemap delivery benchmark — 2026-04-26.

Methodology:
  Sites: top10lists.us, brafton.com, seo.com, moz.com (anonymized in publication).
  User agents: Googlebot/2.1 + ClaudeBot/1.0 — captures bot-policy asymmetry.
  Records definition: terminal <url> entries (the actual page URLs).
  TTFB/TTLB distribution: 10 rapid-fire hits to root sitemap.xml,
    pinned to first-resolved IP, Accept-Encoding compressed.
  Wall-clock-to-tree: single parallel fetch (concurrency=10) of root + all
    child sitemaps (one level recursion), un-pinned (real-world DNS round-robin).
  Per-hit fields: TTFB (time_starttransfer), TTLB (time_total),
    size (size_download, decompressed bytes), HTTP status.

Output:
  ./summary.json — aggregate stats per (site, UA)
  ./raw/<site>__<ua>.jsonl — per-hit raw measurements
"""
import json, subprocess, time, socket, re, sys, os, concurrent.futures, statistics
from pathlib import Path
from xml.etree import ElementTree as ET

ROOT = Path(__file__).parent
RAW = ROOT / "raw"
RAW.mkdir(exist_ok=True)

SITES = [
    "https://www.top10lists.us/sitemap.xml",
    "https://www.brafton.com/sitemap.xml",
    "https://www.seo.com/sitemap.xml",
    "https://www.moz.com/sitemap.xml",
]

UAS = {
    "Googlebot": "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
    "ClaudeBot": "Mozilla/5.0 (compatible; ClaudeBot/1.0; +https://www.anthropic.com/claudebot)",
}

NS = {"sm": "http://www.sitemaps.org/schemas/sitemap/0.9"}


def resolve_ip(host: str) -> str | None:
    try:
        return socket.gethostbyname(host)
    except Exception as e:
        print(f"  ! DNS fail for {host}: {e}")
        return None


def curl_one(url: str, ua: str, resolve_pin: tuple | None = None, timeout: int = 30):
    """Single curl. resolve_pin = (host, port, ip) to pin DNS. Follows redirects (-L) so TTFB/TTLB reflect end-to-end bot ingestion cost including any redirect chain."""
    cmd = [
        "curl", "-sS", "-L", "--compressed",
        "-A", ua,
        "-o", os.devnull,
        "--max-time", str(timeout),
        "-w", "%{time_starttransfer}\t%{time_total}\t%{size_download}\t%{http_code}\t%{num_redirects}\t%{url_effective}",
    ]
    if resolve_pin:
        cmd.extend(["--resolve", f"{resolve_pin[0]}:{resolve_pin[1]}:{resolve_pin[2]}"])
    cmd.append(url)
    try:
        r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout + 5)
        out = r.stdout.strip()
        parts = out.split("\t") if out else []
        if len(parts) >= 4:
            return {
                "ttfb": float(parts[0]),
                "ttlb": float(parts[1]),
                "size": int(parts[2]),
                "status": int(parts[3]),
                "redirects": int(parts[4]) if len(parts) > 4 else 0,
                "url_final": parts[5] if len(parts) > 5 else url,
                "ok": True,
            }
    except subprocess.TimeoutExpired:
        return {"ok": False, "error": "timeout"}
    except Exception as e:
        return {"ok": False, "error": str(e)}
    return {"ok": False, "error": f"parse fail: {r.stdout[:200]}"}


def curl_body(url: str, ua: str, timeout: int = 30):
    """Fetch body for parsing the sitemap."""
    try:
        r = subprocess.run(
            ["curl", "-sS", "--compressed", "-A", ua, "--max-time", str(timeout), "-L", url],
            capture_output=True, text=True, timeout=timeout + 5,
        )
        return r.stdout
    except Exception as e:
        return ""


def parse_sitemap(xml_text: str):
    """Returns (kind, child_urls, url_count). kind in {'index','urlset','unknown'}."""
    if not xml_text or not xml_text.lstrip().startswith("<"):
        return ("unknown", [], 0)
    try:
        root = ET.fromstring(xml_text)
    except ET.ParseError:
        # try stripping namespace
        try:
            root = ET.fromstring(re.sub(r' xmlns="[^"]+"', "", xml_text, count=1))
        except Exception:
            return ("unknown", [], 0)
    tag = root.tag.split("}", 1)[-1].lower()
    if tag == "sitemapindex":
        children = [loc.text.strip() for loc in root.iter() if loc.tag.split("}", 1)[-1].lower() == "loc" and loc.text]
        return ("index", children, 0)
    if tag == "urlset":
        urls = [loc.text.strip() for loc in root.iter() if loc.tag.split("}", 1)[-1].lower() == "loc" and loc.text]
        return ("urlset", [], len(urls))
    return ("unknown", [], 0)


def benchmark_site(site_url: str, ua_name: str, ua: str):
    print(f"\n{'='*70}\n{site_url}  [{ua_name}]\n{'='*70}")
    parsed_host = re.sub(r"^https?://", "", site_url).split("/")[0]
    ip = resolve_ip(parsed_host)
    print(f"  resolved {parsed_host} -> {ip}")

    # Phase 1: 10 rapid-fire hits to root sitemap, pinned IP, captures TTFB/TTLB distribution
    pin = (parsed_host, "443", ip) if ip else None
    hits = []
    for i in range(10):
        h = curl_one(site_url, ua, resolve_pin=pin, timeout=30)
        h["i"] = i
        h["ts"] = time.time()
        hits.append(h)
    successful = [h for h in hits if h.get("ok") and h.get("status") == 200]
    if successful:
        ttfb = sorted(h["ttfb"] for h in successful)
        ttlb = sorted(h["ttlb"] for h in successful)
        sz = sorted(h["size"] for h in successful)

        def pct(values, p):
            if not values: return None
            k = max(0, min(len(values) - 1, int(round((p / 100) * (len(values) - 1)))))
            return values[k]

        ttfb_p50 = pct(ttfb, 50)
        ttfb_p95 = pct(ttfb, 95)
        ttlb_p50 = pct(ttlb, 50)
        ttlb_p95 = pct(ttlb, 95)
        cold_spikes = sum(1 for v in ttfb if v > 2 * ttfb_p50)
    else:
        ttfb_p50 = ttfb_p95 = ttlb_p50 = ttlb_p95 = None
        cold_spikes = None

    # Phase 2: parse root, recurse one level, count records, capture wall-clock
    root_body = curl_body(site_url, ua)
    kind, children, url_count_root = parse_sitemap(root_body)
    print(f"  root sitemap kind: {kind} | children: {len(children)} | url_count_root: {url_count_root}")
    total_records = url_count_root
    total_bytes_decompressed = len(root_body.encode("utf-8")) if root_body else 0

    # If sitemap index, fetch all children in parallel and count their <url> entries
    child_durations = []
    child_records = []
    child_bytes = []
    wallclock_seconds = None
    if kind == "index" and children:
        wall_start = time.perf_counter()
        with concurrent.futures.ThreadPoolExecutor(max_workers=10) as pool:
            futures = {pool.submit(curl_body, child, ua): child for child in children}
            for fut in concurrent.futures.as_completed(futures):
                child = futures[fut]
                body = fut.result() or ""
                kind2, _, count2 = parse_sitemap(body)
                child_records.append(count2)
                child_bytes.append(len(body.encode("utf-8")) if body else 0)
        wallclock_seconds = time.perf_counter() - wall_start
        total_records += sum(child_records)
        total_bytes_decompressed += sum(child_bytes)
    else:
        # Already a urlset; wall-clock is just the root fetch (already measured in hits)
        wallclock_seconds = ttlb_p50

    summary = {
        "site": site_url,
        "ua_name": ua_name,
        "ip_pinned": ip,
        "root_kind": kind,
        "child_sitemaps_count": len(children),
        "total_records_url_entries": total_records,
        "total_bytes_decompressed": total_bytes_decompressed,
        "wallclock_seconds_full_tree_parallel": round(wallclock_seconds, 3) if wallclock_seconds else None,
        "ttfb_p50_seconds": ttfb_p50,
        "ttfb_p95_seconds": ttfb_p95,
        "ttlb_p50_seconds": ttlb_p50,
        "ttlb_p95_seconds": ttlb_p95,
        "ttfb_cold_spike_count_2x_p50": cold_spikes,
        "successful_hits_of_10": len(successful),
        "status_codes": sorted({h.get("status") for h in hits if h.get("ok")}),
        "errors": [h.get("error") for h in hits if not h.get("ok")],
        "timestamp_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
    }

    print(f"  records: {total_records:,}  bytes: {total_bytes_decompressed:,}")
    print(f"  TTFB p50: {ttfb_p50}s  TTFB p95: {ttfb_p95}s")
    print(f"  TTLB p50: {ttlb_p50}s  TTLB p95: {ttlb_p95}s")
    print(f"  wall-clock (full tree, parallel): {wallclock_seconds:.3f}s" if wallclock_seconds else "  wall-clock: n/a")
    print(f"  successful hits: {len(successful)}/10  | cold spikes (>2x p50): {cold_spikes}")

    # Persist raw hits
    safe = re.sub(r"[^a-z0-9]+", "-", site_url.lower()).strip("-")
    with open(RAW / f"{safe}__{ua_name}.jsonl", "w") as fh:
        for h in hits:
            fh.write(json.dumps(h) + "\n")

    return summary


def main():
    all_results = []
    for site in SITES:
        for ua_name, ua in UAS.items():
            r = benchmark_site(site, ua_name, ua)
            all_results.append(r)
            time.sleep(2)  # courtesy gap between (site, UA) runs

    out = ROOT / "summary.json"
    out.write_text(json.dumps(all_results, indent=2))
    print(f"\n\nSummary written to: {out}")
    print(f"Raw per-hit data in: {RAW}")

    # Pretty-print comparison table
    print("\n" + "=" * 100)
    print(f"{'site':<32}{'UA':<12}{'records':>14}{'bytes':>14}{'walls':>10}{'TTFB p50':>10}{'TTFB p95':>10}")
    print("=" * 100)
    for r in all_results:
        print(f"{r['site']:<32}{r['ua_name']:<12}{r['total_records_url_entries']:>14,}{r['total_bytes_decompressed']:>14,}{r['wallclock_seconds_full_tree_parallel'] or 'n/a':>10}{r['ttfb_p50_seconds'] or 'n/a':>10}{r['ttfb_p95_seconds'] or 'n/a':>10}")


if __name__ == "__main__":
    main()

Note: The script writes summary.json in its working directory and raw/<site>__<ua>.jsonl for each pair.

4. Per-Site Raw Data

Eight collapsibles — four sites, two user agents each. Each contains the unedited per-hit JSONL captured by run.py. Comparison-site domains are anonymized in the rendered url_final field; numeric measurements (TTFB, TTLB, size, status, redirects, timestamps) are unedited.

Top10Lists.us

Top10Lists.us — Googlebot

10 hits captured
Googlebot

Sitemap index, 29 child sitemaps, 230,329 terminal <url> records, 0 redirect hops, all 10 hits returned HTTP 200.

Show raw per-hit JSONL ↓
{"ttfb": 0.100041, "ttlb": 0.10014, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 0, "ts": 1777240033.6408477} {"ttfb": 0.09601, "ttlb": 0.096121, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 1, "ts": 1777240033.7494435} {"ttfb": 0.08722, "ttlb": 0.087333, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 2, "ts": 1777240033.8491788} {"ttfb": 0.102722, "ttlb": 0.102822, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 3, "ts": 1777240033.9642994} {"ttfb": 0.081949, "ttlb": 0.082049, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 4, "ts": 1777240034.0591083} {"ttfb": 0.076826, "ttlb": 0.076931, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 5, "ts": 1777240034.148565} {"ttfb": 0.086202, "ttlb": 0.086309, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 6, "ts": 1777240034.247224} {"ttfb": 0.073007, "ttlb": 0.073115, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 7, "ts": 1777240034.3330674} {"ttfb": 0.094738, "ttlb": 0.094841, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 8, "ts": 1777240034.4411228} {"ttfb": 0.073999, "ttlb": 0.074102, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 9, "ts": 1777240034.527605}

Top10Lists.us — ClaudeBot

10 hits captured
ClaudeBot

Same architecture, same 230,329 records. ClaudeBot served identically to Googlebot — bot-policy parity confirmed.

Show raw per-hit JSONL ↓
{"ttfb": 0.073412, "ttlb": 0.073518, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 0, "ts": 1777240038.359486} {"ttfb": 0.095122, "ttlb": 0.095226, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 1, "ts": 1777240038.4679935} {"ttfb": 0.07971, "ttlb": 0.079817, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 2, "ts": 1777240038.5604086} {"ttfb": 0.078964, "ttlb": 0.079076, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 3, "ts": 1777240038.7835813} {"ttfb": 0.092157, "ttlb": 0.092789, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 4, "ts": 1777240038.8888597} {"ttfb": 0.086022, "ttlb": 0.086133, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 5, "ts": 1777240038.9885519} {"ttfb": 0.090571, "ttlb": 0.09068, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 6, "ts": 1777240039.0920522} {"ttfb": 0.076762, "ttlb": 0.076868, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 7, "ts": 1777240039.181688} {"ttfb": 0.087672, "ttlb": 0.088315, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 8, "ts": 1777240039.2827125} {"ttfb": 0.078507, "ttlb": 0.078616, "size": 342, "status": 200, "redirects": 0, "url_final": "https://www.top10lists.us/sitemap.xml", "ok": true, "i": 9, "ts": 1777240039.3742917}

Comparison Site A

Comparison Site A — Googlebot

10 hits captured
Googlebot

Bot-blocked: HTTP 403 returned to Googlebot itself for /sitemap.xml. 0 records, 0 successful 200-status hits. Wall-clock to tree is null because the bot is denied entry. The 919 bytes returned are the 403 response body.

Show raw per-hit JSONL ↓
{"ttfb": 0.277461, "ttlb": 0.277518, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 0, "ts": 1777240043.727692} {"ttfb": 0.07696, "ttlb": 0.077011, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 1, "ts": 1777240043.9035275} {"ttfb": 0.271367, "ttlb": 0.271423, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 2, "ts": 1777240044.1881423} {"ttfb": 0.073666, "ttlb": 0.073723, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 3, "ts": 1777240044.3467739} {"ttfb": 0.074384, "ttlb": 0.074447, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 4, "ts": 1777240044.605283} {"ttfb": 0.090297, "ttlb": 0.090366, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 5, "ts": 1777240044.7760003} {"ttfb": 0.071434, "ttlb": 0.07148, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 6, "ts": 1777240044.8600688} {"ttfb": 0.259478, "ttlb": 0.259528, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 7, "ts": 1777240045.1326978} {"ttfb": 0.240142, "ttlb": 0.240189, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 8, "ts": 1777240045.3863478} {"ttfb": 0.537061, "ttlb": 0.537114, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 9, "ts": 1777240045.9366982}

Comparison Site A — ClaudeBot

10 hits captured
ClaudeBot

Same 403 posture for ClaudeBot. The site denies both bots equally at the edge.

Show raw per-hit JSONL ↓
{"ttfb": 0.084148, "ttlb": 0.084202, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 0, "ts": 1777240048.4561958} {"ttfb": 0.073944, "ttlb": 0.074022, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 1, "ts": 1777240048.5571444} {"ttfb": 0.06791, "ttlb": 0.067989, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 2, "ts": 1777240048.6423132} {"ttfb": 0.070821, "ttlb": 0.070891, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 3, "ts": 1777240048.8382459} {"ttfb": 0.073536, "ttlb": 0.073592, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 4, "ts": 1777240048.9257252} {"ttfb": 0.090855, "ttlb": 0.090909, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 5, "ts": 1777240049.0297587} {"ttfb": 0.080754, "ttlb": 0.08081, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 6, "ts": 1777240049.1236188} {"ttfb": 0.076866, "ttlb": 0.076923, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 7, "ts": 1777240049.213532} {"ttfb": 0.084755, "ttlb": 0.084811, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 8, "ts": 1777240049.3120449} {"ttfb": 0.131739, "ttlb": 0.131798, "size": 919, "status": 403, "redirects": 0, "url_final": "https://comparison-site-a.example/sitemap.xml", "ok": true, "i": 9, "ts": 1777240049.4571786}

Comparison Site B

Comparison Site B — Googlebot

10 hits captured
Googlebot

Sitemap index, 3 child sitemaps, 642 terminal <url> records, 1 redirect hop (sitemap.xml → sitemap_index.xml), TTFB p50 ~739 ms.

Show raw per-hit JSONL ↓
{"ttfb": 1.010074, "ttlb": 1.010282, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 0, "ts": 1777240052.6059632} {"ttfb": 0.840695, "ttlb": 0.840894, "size": 334, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 1, "ts": 1777240053.480798} {"ttfb": 0.714845, "ttlb": 0.71555, "size": 334, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 2, "ts": 1777240054.2229283} {"ttfb": 0.738699, "ttlb": 0.738865, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 3, "ts": 1777240055.2502458} {"ttfb": 0.751741, "ttlb": 0.751897, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 4, "ts": 1777240056.0250514} {"ttfb": 0.719056, "ttlb": 0.719228, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 5, "ts": 1777240056.7604947} {"ttfb": 0.716276, "ttlb": 0.716442, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 6, "ts": 1777240057.5000308} {"ttfb": 0.977984, "ttlb": 0.978154, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 7, "ts": 1777240058.506632} {"ttfb": 0.718821, "ttlb": 0.720148, "size": 334, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 8, "ts": 1777240059.2572644} {"ttfb": 0.755849, "ttlb": 0.756008, "size": 334, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 9, "ts": 1777240060.2478273}

Comparison Site B — ClaudeBot

10 hits captured
ClaudeBot

ClaudeBot served identically — same 1 redirect, similar TTFB band. Sitemap accessible to both bots.

Show raw per-hit JSONL ↓
{"ttfb": 0.755896, "ttlb": 0.756061, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 0, "ts": 1777240064.6495023} {"ttfb": 0.818012, "ttlb": 0.818178, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 1, "ts": 1777240065.481998} {"ttfb": 0.70414, "ttlb": 0.704295, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 2, "ts": 1777240066.2001197} {"ttfb": 0.727117, "ttlb": 0.727272, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 3, "ts": 1777240066.940602} {"ttfb": 0.705545, "ttlb": 0.705711, "size": 334, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 4, "ts": 1777240067.65989} {"ttfb": 0.767931, "ttlb": 0.768093, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 5, "ts": 1777240068.441843} {"ttfb": 0.763808, "ttlb": 0.763974, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 6, "ts": 1777240069.2187335} {"ttfb": 0.772044, "ttlb": 0.772221, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 7, "ts": 1777240070.1403518} {"ttfb": 0.700949, "ttlb": 0.701113, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 8, "ts": 1777240070.8562355} {"ttfb": 0.725152, "ttlb": 0.7253, "size": 329, "status": 200, "redirects": 1, "url_final": "https://comparison-site-b.example/sitemap_index.xml", "ok": true, "i": 9, "ts": 1777240071.5948212}

Comparison Site C

Comparison Site C — Googlebot

10 hits captured
Googlebot

Sitemap index, 52 child sitemaps, 8,755 terminal <url> records, 2 redirect hops, TTFB p50 ~399 ms.

Show raw per-hit JSONL ↓
{"ttfb": 0.559634, "ttlb": 0.55984, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 0, "ts": 1777240076.1068807} {"ttfb": 0.372712, "ttlb": 0.372891, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 1, "ts": 1777240076.4940777} {"ttfb": 0.422596, "ttlb": 0.422777, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 2, "ts": 1777240076.9308388} {"ttfb": 0.398644, "ttlb": 0.398827, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 3, "ts": 1777240077.343003} {"ttfb": 0.425462, "ttlb": 0.425632, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 4, "ts": 1777240077.7833226} {"ttfb": 0.398801, "ttlb": 0.400964, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 5, "ts": 1777240078.1975303} {"ttfb": 0.388896, "ttlb": 0.38908, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 6, "ts": 1777240078.5996268} {"ttfb": 0.380618, "ttlb": 0.380842, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 7, "ts": 1777240078.994015} {"ttfb": 0.38033, "ttlb": 0.380508, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 8, "ts": 1777240079.3891444} {"ttfb": 0.410912, "ttlb": 0.411084, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 9, "ts": 1777240079.813843}

Comparison Site C — ClaudeBot

10 hits captured
ClaudeBot

Same 2-hop redirect chain for ClaudeBot, similar TTFB band. Bot parity confirmed.

Show raw per-hit JSONL ↓
{"ttfb": 0.395368, "ttlb": 0.395548, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 0, "ts": 1777240083.6346946} {"ttfb": 0.377943, "ttlb": 0.378121, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 1, "ts": 1777240084.026265} {"ttfb": 0.64892, "ttlb": 0.64912, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 2, "ts": 1777240084.6887765} {"ttfb": 0.333406, "ttlb": 0.333587, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 3, "ts": 1777240085.0361133} {"ttfb": 0.36723, "ttlb": 0.367406, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 4, "ts": 1777240085.4171188} {"ttfb": 0.360721, "ttlb": 0.360906, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 5, "ts": 1777240085.7915833} {"ttfb": 0.366812, "ttlb": 0.366995, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 6, "ts": 1777240086.1720357} {"ttfb": 0.409019, "ttlb": 0.409211, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 7, "ts": 1777240086.5944705} {"ttfb": 0.438936, "ttlb": 0.439114, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 8, "ts": 1777240087.0472128} {"ttfb": 0.395504, "ttlb": 0.396465, "size": 1123, "status": 200, "redirects": 2, "url_final": "https://comparison-site-c.example/sitemaps-1-sitemap.xml", "ok": true, "i": 9, "ts": 1777240087.457165}

5. Findings

Records

Top10Lists.us delivers 230,329 records versus the comparison set's 642 to 8,755. That is approximately 26x more records than the largest comparison site — in the same one-level sitemap-index recursion that any bot would walk. The comparison set's largest tree is 3.8% the size of Top10Lists.us.

TTFB

Top10Lists.us TTFB p50 is 86 ms versus the comparison set's 399 ms to 739 ms — the comparison set is 5x to 9x slower to deliver the first byte. The slowdown is primarily a function of redirect chains: Top10Lists.us has zero redirects on /sitemap.xml; the two responsive comparison sites have one and two redirects respectively. Each hop is a TLS handshake the bot pays for.

Bot accessibility

One of the three comparison sites returns HTTP 403 Forbidden to Googlebot itself at both /sitemap.xml and /robots.txt. That site is unreachable to AI ingestion regardless of any other GEO criterion — no schema, no llms.txt, no neighborhood density can compensate for a 403 at the door. Top10Lists.us, by contrast, serves both Googlebot and ClaudeBot identically with HTTP 200 across all 20 measured hits.

6. Limitations and Reproducibility

UA-spoofed client ≠ verified bot crawl

These measurements use a UA-spoofed HTTP client (curl with -A "Googlebot/2.1" or -A "ClaudeBot/1.0") originating from a single test point. This is a simulation of bot delivery, not a measurement of a verified bot crawl. Real bots originate from published IP ranges (Googlebot from 66.249.x.x, GPTBot from OpenAI's documented blocks, etc.), have distinct TCP fingerprints, and are typically classified as “verified bot” by Cloudflare's bot-management layer rather than “client claiming to be a bot.” Sites can therefore route real bot traffic to a separate cache tier, apply different WAF rules, or skip rate-limiting that a spoofed client would trigger.

The differential between Top10Lists.us and the comparison set is preserved across this measurement protocol because the same client behavior is applied to all four sites. The relative result — that Top10Lists.us delivers more records, with lower TTFB, through fewer redirect hops, and with no edge-level bot blocking — is robust to the spoofing limitation. The absolute latency numbers may shift under verified-bot conditions, in either direction. A reader who wants verified-bot timing should run the reproducibility script in Section 3 from a server inside one of the published bot IP ranges, or consult their own bot-traffic logs if they operate one of the comparison sites.

Other limitations

  • Measurements are point-in-time. Sitemap content drifts; bot policies change. The numbers above are a snapshot of these four sites on 2026-04-26 and are frozen at this URL.
  • Pinned-IP measurements (Section 1 TTFB) factor out DNS round-robin but may differ from real-world bot routing. Wall-clock-to-tree numbers are with un-pinned DNS — more representative of real bot ingestion.
  • One of the comparison sites' wall-clock measurement is null because the site blocks both bots with HTTP 403. That is a real-world observation, not a test failure.
  • Anyone with curl and Python 3 can re-execute the script in Section 3 to validate the numbers and observe current state.

7. Conclusion

Top10Lists.us delivers roughly 26x more sitemap records than the largest of three established U.S. content agencies tested, at 5x to 9x lower TTFB, with zero redirect hops, and with no edge-level bot blocking — the four metrics that determine whether an AI crawler can ingest a site at all.

Read the press release this benchmark supports at geolocus.ai/press →

Related