I run is structurally consistent; a more complex format would warrant a parser.
One thing to note: the function takes a prefix argument (like /models/ or /games/). That's how I distinguish detail pages from the index pages that also appear in the sitemap. I want a URL like /models/qwen2-7b/, not /models/.
Checking ads.txt and affiliate strings
The ads.txt check is a separate fetch, not the HTML check. It looks for the AdSense publisher ID pattern:
const adsTxtRes = await fetch(`https://${site}/ads.txt`);
const adsTxt = await adsTxtRes.text();
const hasAdsensePub = /pub-\d{10,}/.test(adsTxt);
The HTML checks are string presence checks against the fetched page:
const hasSection = html.includes(section);
const hasAdsense = html.includes("adsbygoogle") && html.includes("data-ad-client");
const hasAmazon = html.includes("Gear up on Amazon") || html.includes("amazon.com/s?k=");
The section variable is site-specific. For aiappdex.com it's "Run this model on"; for findindiegame.com it's "Find on other stores"; for ossfind.com it's "Self-host on". These are heading strings that only appear in the rendered HTML when the relevant env var is set.
I deliberately check strings that are human-readable rather than env var names or data-attributes. If the heading renders, the CTA is live. If the heading is absent, something upstream didn't connect. The message in that case tells me exactly which env var to check in Cloudflare.
Why this is better than a visual check
The pattern I was relying on before — opening a few pages after a deploy and eyeballing them — has two problems. First, it's slow across three sites with multiple CTA types. Second, it's unreliable at catching conditional rendering: an affiliate block that's absent looks the same as a block I intentionally disabled or that I haven't scrolled to yet.
A script that fetches programmatically, checks presence by string match, and reports pass/fail for each CTA type takes about two seconds and catches the failure unambiguously. The output is readable in a terminal and doesn't require loading a browser.
This connects to the same principle behind the three-tier content quality ladder: checks at different stages catch different things. Post-deploy verification catches deploy-time configuration problems. Pre-commit linting catches content problems. Neither replaces the other.
What I'd add
Right now the script only checks one sample page per site. A more thorough version would check one page from each content type per site — a model page, a compare page, an alternatives page — since some CTAs only render on specific page types. That would require more sitemap traversal but would catch more edge cases.
The output format is human-readable but not machine-parseable. If I wanted to hook this into CI and fail a deploy when a CTA is missing, I'd add a JSON output mode and return a non-zero exit code on any ✗. For now I run it manually after deploys — it takes less than ten seconds and the terminal output is enough.
Part of an ongoing 6-month experiment running three AI-curated directory sites. The technical claims here are real; this article was AI-assisted.
SOCIAL SHARE CARD GENERATOR