Eazler AI Lab AI Engineering Journal

Running GEO/SEO With an AI Agent

Measurements from applying GEO (generative engine optimization) to our own iOS app site. The assumption that broke hardest was "LLM traffic is still early." It was already 93% of our app referrals — and the site's robots.txt was blocking that exact crawler.

Published: 2026-09-05 Scope: GEO / SEO / crawler access / measurement Case: AlarmK (iOS mission alarm) 한국어: 한국어로 읽기

1. Executive summary

We applied GEO/SEO to the official site of a shipped iOS app. The measurements taken before the work mattered more than the work itself, because most of our assumptions did not survive contact with the data.

93%
of app referrals — 179 of 192 — came from ChatGPT
0 words
served by the home page without JavaScript
9 crawlers
disallowed in robots.txt, including GPTBot and ClaudeBot

All three point the same way: LLM-driven traffic was already happening, and the site was in a state where an LLM could not read it.

2. What the data said first

First-time downloads by source from App Store Connect, over 90 days (2026-06-02 to 08-30).

SourceDownloadsShare
App Store search37462%
App referrer19232%
Web referrer203%
App Store browse183%
Total606

Broken down by referring app, "app referrer" is ChatGPT 179, Google Chrome 4. So 93% of app referrals, and 30% of all downloads, came from someone being pointed at the app inside ChatGPT. June and July were close to zero; the curve turns sharply upward in early August.

What we learned. We had filed "optimize for LLMs" under future work. It was already a primary acquisition channel. GEO should have been treated as reinforcing a channel that already worked, not as a bet. Without measuring first, we would have prioritized entirely wrong.

Conversely, 20 web referrals was implausibly low. The reason turns up later.

3. A fact contract, before a line of copy

The biggest risk in letting an AI write marketing pages is not code quality — it is plausible falsehoods. Promoting a feature that does not exist, reaching for an unverifiable superlative, or inventing facts about a competitor.

So before writing a single page, we had the agent read the whole repository and produce a fact contract first. Three lists came out of it.

Claims traceable to a file and line. Usable as written.

Topics the repo does not settle. Write conservatively or omit.

Specific phrasings that are barred, each with its reason.

The exercise surfaced internal contradictions before it produced any copy. The marketing JSX described three missions while the versioned user guide documented eight. One commit's subject line claimed it changed a contact address to A while its diff changed it to B.

The generalizable rule. Telling an AI "don't make things up" is not enough. You have to name the single source that decides what is true. Here that rule was: the versioned user guide wins; marketing code is not trusted.

4. What the agent actually found

The work started as SEO. Most of what came out were bugs that had been sitting in plain sight for months.

The home page served zero words without JS

The home page renders React through an in-browser Babel transform, so <body> is a single empty <div id="root">. A crawler's first pass — before rendering — sees zero words and zero internal links. Every other page depended entirely on the sitemap for discovery.

Three legal pages were effectively blank

The guide, privacy and terms pages put three languages on one URL and hide two with CSS — but the served HTML marked none of them active. Any client that did not run JavaScript saw 15 to 35 words. They were listed in the sitemap with no content behind them.

PageBeforeAfter
User guide35 words2,106 words
Privacy policy15 words591 words
Terms of service15 words275 words

A pricing page selling features that do not exist

A fully built pricing component sat in the code with rendering switched off. Checking its 14 feature rows against the documentation, zero were both real and not already free. Six shipped free, four could not exist at all — one of them blocked by an OS-level limitation, not an engineering gap — and three were undocumented. The comment above it said restoring the section was a one-line change.

A privacy policy disclosing collection that was not happening

This one is the agent catching the agent. The policy stated in three languages that the site used a web analytics product. It did not — the beacon was never injected. Disclosing collection you are not doing is less dangerous than the reverse, but it is still false.

What we learned. Agent output has to be re-verified against the live response after deployment. Reading the source leads you to conclude "it is allowed in the CSP, so it must be on." One curl broke that assumption.

59% of requests were 4xx

CDN metrics showed 7,060 4xx against 5,000 2xx over seven days. Thirty images in the guide pointed at a flat path with no file behind it, rewritten to the real localized path by JavaScript at runtime. The browser requests the original path before the script fixes it. Fixing the blank-document problem above made it worse, by making those images visible on load.

5. robots.txt was blocking the LLM crawlers

The most ironic finding. Checking robots.txt after deployment, the CDN was prepending a managed block, and inside it every one of these carried Disallow: /.

GPTBot · ClaudeBot · Google-Extended · CCBot · Bytespider
Applebot-Extended · meta-externalagent · Amazonbot

Content-Signal: search=yes, ai-train=no, use=reference

The entire point of the project was to make the site legible to LLMs, and ChatGPT and Claude were disallowed at the robots.txt layer. It is a CDN dashboard setting, not a file in the repository, so no amount of editing code removes it.

Note the contradiction. The Content-Signal says use=reference — quoting is allowed — while the same file says Disallow: /. The intent was presumably "don't train on this, but you may cite it." A crawler that cannot read the page cannot cite it either. A textbook case of a default that does not match the intent.

One more distinction mattered. The dashboard's per-crawler block toggles were all off and the logs showed zero failures. The site was declaring a prohibition in robots.txt without enforcing it at the edge. Compliant crawlers honor the declaration, so the outcome is the same. The useful thing to know is that there are two blocking layers and they live in different places.

6. How to pick a measurement tool

While evaluating event tracking, we measured the candidates rather than assuming.

AssetTransfer size (gzip)
An entire landing page6.5 KB
gtag.js151 KB
A lightweight analytics script1.5 KB

The measurement script is 23 times heavier than the thing being measured. Those pages were deliberately built with zero JavaScript for SEO. Measuring would have damaged what we were measuring.

The decisive fact was elsewhere. This is an iPhone app's site, so most visitors are on Safari — and Safari caps script-set first-party cookies at seven days. The returning-visitor and user-count metrics a tool like this is bought for come out structurally wrong on exactly this audience.

The takeaway. Write down the question you need answered before choosing a tool. Ours was two integers: views per page and CTA clicks per page. Buying 151 KB and a consent banner for two integers is not a trade worth making.

7. The App Store attribution trap

The plan to append utm_* to App Store links died on measurement.

?utm_source=… → 301 → parameters gone
?pt=…&ct=…  → 301 → pt and ct survive

The App Store's own redirect discards utm_*. Apple's campaign attribution uses a different token scheme entirely.

The more useful finding was adjacent. Every App Store link on the site carried rel="noopener noreferrer", and noreferrer instructs the browser to omit the Referer header. The site was suppressing the web-referral reporting App Store Connect already provides for free, with no parameters at all. That is why "web referrer" read 20.

8. What this is not yet

This is not a success story. At the time of writing one page is indexed, and the pages submitted for indexing sit in "Discovered – currently not indexed."

Diagnosing that state: 200 responses, TTFB around 0.5s, correct canonicals, index, follow, and enough content. With no technical blocker, what remains is crawl budget, and on a new domain that is decided by external links and time. External backlinks currently number zero.

To be precise. Days are not enough to judge GEO/SEO work. Every number in this article is a pre-work baseline, not a result. Results are weeks away. Case studies often blur that line, so we are stating it.

9. Checklist

If another team runs the same exercise, this is the order we would suggest.

  1. Measure first. Check App Store Connect or your referral analytics for LLM-driven traffic that already exists. Do not set priorities from assumptions.
  2. Reproduce the crawler's view with curl. Count the words and internal links visible without JavaScript. On an SPA the answer is often zero.
  3. Actually open robots.txt. Read the deployed response, not the file in your repo. Your CDN may be prepending a block.
  4. Check that your AI-crawler policy matches your intent. There are two layers — declaration and enforcement — configured in different places.
  5. Build a fact contract first. Before handing copy to an AI, name the single source that decides what is true.
  6. Re-verify after deploying. Against the live response, not the source. That is where we caught an error the agent itself had introduced.
  7. Audit rel on outbound links. noreferrer may be erasing your attribution.
  8. Choose measurement tools after defining the question. Weigh page weight and your audience's browser behaviour together.

The subject of this case study is the official site of AlarmK, a mission alarm app for iPhone. Its per-permission alarm behaviour is documented on the AlarmKit page.

10. Sources

SourceWhat it coversLink
Apple — App Analytics dimensionsSource-type definitions including App Store search, app referrer and web referrerdeveloper.apple.com
Apple — Campaign linksThe pt, ct and mt parameters and how campaign links are generateddeveloper.apple.com
MDN — rel=noreferrerThe link relation that instructs the browser to omit the Referer headerdeveloper.mozilla.org
Google — Cookie lifetime limitsScript-set first-party cookies capped at 400 days in Chrome and 7 days in Safarisupport.google.com
Google — Tag platform CSPThe CSP directives Google tags require to functiondevelopers.google.com
Cloudflare — Web Analytics FAQNo custom events, no query strings logged, and how edge analytics differdevelopers.cloudflare.com
AlarmK — AlarmK and Apple AlarmKitThe site this case study is about; per-permission alarm delivery pathsalarmk.com/alarmkit

Figures were measured directly in App Store Connect, Cloudflare and Google Search Console as of 2026-09-05. Indexing status and traffic mix will change after that date.