> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appeeky.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Website and Google search

> Check the app's website for Google and AI crawlers, write llms.txt from the listing, pull Search Console, and track Google ranks and AI Overview citations on your own DataForSEO account.

People also find an app through its website: in Google, in Google's AI Overviews, and in the pages AI assistants cite. These commands check the site, record how it does in Google, and let `verify` measure a listing change that also changes the website's copy.

## Point the spec at the site

```yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
web:
  url: https://sleepsounds.app/
  searchConsole: sc-domain:sleepsounds.app   # optional; default: the url's domain
  keywords:                                  # optional; default: the store targets
    us: [rain sounds, white noise app, sleep sounds for baby]
  countries: [us]                            # optional
  device: mobile                             # mobile (default) | desktop
```

| Key | Meaning |
| - | - |
| `url` | The page `seo check` reads, and the site whose ranks are tracked |
| `searchConsole` | The Search Console property: `sc-domain:example.com` or `https://example.com/`. Default: `sc-domain:` plus the url's domain |
| `keywords.<country>` | Google searches to watch with `seo ranks` and `seo keywords`. Default: that storefront's `targets` |
| `countries` | Storefronts to watch. Default: only the first one under `keywords`, or else under `targets` |
| `device` | Which Google results page to read. Default `mobile`, where people look for apps |

## seo check

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npx @appeeky/spec seo check            # or: seo check https://sleepsounds.app/pricing
```

Reads the page the way a crawler does. It needs no key and no account.

```
Search
  ✓ Served over HTTPS
  ✓ Title: "Sleep Sounds: Rain & White Noise" (32 characters)
  ! Meta description: 58 characters
      Aim for 120–160 characters
  ✓ Canonical: https://sleepsounds.app/
  ✓ Page language set
  ✓ One <h1>
  ! No Open Graph image
      Add og:title, og:description and og:image so shared links show a card
  ✓ Sitemap: https://sleepsounds.app/sitemap.xml

The app
  ✓ Links to the App Store listing
  ! No Smart App Banner
      Add <meta name="apple-itunes-app" content="app-id=1234567890"> so Safari offers the app
  ! No structured data
      Add JSON-LD of @type MobileApplication with name, operatingSystem, applicationCategory, offers and aggregateRating
  ✓ The page names the app ("Sleep Sounds")

AI assistants
  ✗ robots.txt blocks GPTBot, ClaudeBot
      Allow the AI crawlers in robots.txt so assistants can read and cite the site
  ! No llms.txt
      Publish /llms.txt: run appeeky-spec seo llms --out public/llms.txt
  ✓ 412 words readable without JavaScript
  ! 2 of 9 target keywords appear on the page
      Use the phrases people search for (targets in appeeky.spec.yaml) in the page's copy, e.g. in an FAQ
  ! No FAQ
      Answer the questions people ask assistants on the page, with FAQPage structured data

9 of 17 fine.
```

`✓` is fine, `!` is a warning, `✗` fails. Any `✗` makes it exit 1, so you can run it in CI.

| Check | Fails or warns when | Fix |
| - | - | - |
| HTTPS | The site is not served over HTTPS (fail) | Serve it over HTTPS |
| Title | No `<title>` (fail), or longer than 65 characters | The app's name and what it does, under about 60 characters |
| Meta description | Missing (fail), or under 70 or over 170 characters | 120–160 characters: what the app does and for whom |
| noindex | The robots meta tag or `X-Robots-Tag` header says `noindex` (fail) | Remove it |
| Canonical | No `<link rel="canonical">` | Add one with the page's preferred URL |
| Language | No `lang` on `<html>` | `<html lang="en">`, or the page's language |
| One h1 | Zero or several `<h1>` | Exactly one, saying what the app does |
| Open Graph | No `og:title` and `og:image` | Add `og:title`, `og:description`, `og:image` |
| Googlebot | robots.txt blocks Googlebot from the site (fail) | Remove `Disallow: /` for Googlebot and `*` |
| Sitemap | No sitemap at the robots.txt `Sitemap:` line or `/sitemap.xml` | Publish one and name it in robots.txt |
| Store links | No link to the App Store or Google Play listing of `stores` | Link to the listing |
| Smart App Banner | No `apple-itunes-app` meta tag with the app's id | Add it so Safari offers the app |
| App structured data | No JSON-LD of type `MobileApplication`, `SoftwareApplication` or `WebApplication` | Add `MobileApplication` JSON-LD |
| The app's name | The page text does not say the app's name | Say it in the text, not only in the logo |
| AI crawlers | robots.txt blocks GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, Claude-SearchBot, PerplexityBot, Google-Extended or Applebot-Extended (fail) | Allow them, so assistants can read and cite the site |
| llms.txt | No `/llms.txt` with a Markdown heading | `seo llms --out public/llms.txt` |
| Text without JavaScript | Under 150 words readable without running JavaScript | Render the text on the server: most AI crawlers do not run JavaScript |
| Target keywords | Fewer than 30% of your store `targets` appear on the page | Use those phrases in the copy, for example in an FAQ |
| FAQ | No `FAQPage` structured data | Answer the questions people ask assistants, with `FAQPage` markup |

A site that does not answer, or answers with an HTTP error, fails at once: check `web.url`.

## seo llms

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npx @appeeky/spec seo llms --out public/llms.txt
```

Writes llms.txt from the listing: the app's name and subtitle, the first paragraph of the description, links to the App Store, Google Play, the website and support, the first storefront's target keywords as "Good for", and the full description. Without `--out` it prints it. Run it again after a listing change so the site and the store say the same thing.

## Search Console: seo pull

`seo pull` reads Google Search Console with your own Google credentials, in this order:

1. `GSC_CREDENTIALS`: a service account JSON key, or its path. Create the service account in Google Cloud, enable the Search Console API, and add its email as a user of the property in Search Console.
2. `GOOGLE_APPLICATION_CREDENTIALS`, the same way. In CI this is often your Google Play service account; it works for Search Console once its email is a user of the property.
3. Your gcloud login on this machine: `gcloud auth application-default login --scopes=https://www.googleapis.com/auth/webmasters.readonly,https://www.googleapis.com/auth/cloud-platform`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npx @appeeky/spec seo pull
```

```
✓ appeeky/seo/  sc-domain:sleepsounds.app
  last 28 days: 1240 clicks (+12%) · 38410 impressions (+4%)
  212 queries, 37 pages. Read appeeky/seo/summary.md.
```

Search Console data settles after about two days, so the window ends two days ago. It writes to `appeeky/seo/`:

| File | What is in it |
| - | - |
| `daily.csv` | `date,clicks,impressions,ctr_pct,position` per day. Each pull adds the last 60 days and keeps older ones |
| `queries.csv` | The top 250 queries of the last 28 days |
| `pages.csv` | The top 100 pages of the last 28 days |
| `summary.md` | Clicks and impressions against the 28 days before, the top queries, queries close to the top (position 4–15, 20+ impressions), and queries that contain your store targets |

The close-to-the-top queries are the cheapest wins: a better title, description or section can move them onto the first screen. A `403` means the account is not a user of the property yet.

## Google ranks and AI Overviews on DataForSEO

With your own [DataForSEO](https://dataforseo.com) account, Spec reads Google's results page itself. Set `DATAFORSEO_LOGIN` and `DATAFORSEO_PASSWORD` from its API access page. DataForSEO bills each call.

| Command | Writes | Quote |
| - | - | - |
| `seo ranks` | Where the site and the app's store page rank, and AI Overview citations, to `ranks.csv`, `ranks.md`, `changes.md` | About \$0.02 per keyword |
| `seo keywords` | Volume, difficulty, CPC, intent and related ideas to `keywords.csv` | About $0.01 per storefront, plus $0.0135 for each of the first 3 keywords (related ideas) |
| `seo domain` | What the site ranks for, its estimated visits and competing sites, to `domain.md` and `ranked-keywords.csv` | About \$0.05 |

Each one quotes first and asks before spending:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npx @appeeky/spec seo ranks --dry-run
# 12 keywords in US · about $0.24 on DataForSEO
# Nothing was asked.
npx @appeeky/spec seo ranks
```

```
✓ appeeky/seo/ranks.md  7 of 12 in the top 100 · 2 in the top 10 · AI Overview cites you 1×  $0.240
  US rain sounds                  site 14   store page 3    AI cites calm.com, sleepfoundation.org
  US white noise app              site 8    store page 5    AI cites you
  changed US "white noise app": into the top 10 (12 → 8)
```

`--dry-run` works before the keys are set. `--yes` skips the question. The real cost of each run is printed at the end.

* **`seo ranks`** reads the top 100 results for each keyword. `site` is the best position of any page on your site, `store page` is where the app's App Store or Google Play page ranks on Google. `ranks.csv` gets one row per keyword per check (`date,country,keyword,position,url,store_position,ai_overview,ai_cites_you`); a second check on the same day replaces the first. Keywords entering or leaving the top 10, and AI Overview citations gained or lost, go to `changes.md`. `--every <days>` skips the check when the last one is newer.
* **`seo keywords`** writes volume, difficulty, CPC and intent for your keywords, plus up to 50 related ideas from each of the first three, to `keywords.csv` (`tracked` is `yes` for yours).
* **`seo domain`** reports for the first watched storefront: how many keywords the site ranks for, how many in the top 3 and top 10, estimated monthly visits, the top keywords (marking store targets), and sites that rank for the same searches.

When Google shows an AI Overview that cites other sites but not yours, those are the pages to beat: compare their copy with yours, and run `seo check` for the AI crawler items.

## What verify reads

| Metric | From | Better |
| - | - | - |
| `search_clicks` | Average clicks per day in `daily.csv` (needs 3+ days in the window) | Up |
| `search_impressions` | Average impressions per day in `daily.csv` | Up |
| `web_rank` | The site's average Google position in `ranks.csv`, 101 when not in the top 100 | Down |
| `ai_overview_citations` | The share of checked keywords whose AI Overview cites the site | Up |

Experiments can use them too: `experiment add --metric web_rank`. See [Push, measure and revert](https://docs.appeeky.com/docs/spec-releases.md).

## On a schedule

The daily workflow and the Docker `daily` routine run `seo pull` when `GSC_CREDENTIALS` is set, and `seo ranks --every 7 --yes` when `DATAFORSEO_LOGIN` is set (`SEO_RANKS_EVERY` changes the days). `seo keywords` and `seo domain` are for when you plan copy; run them by hand. The weekly agent cannot run the paid ones with `--yes`. See [GitHub Actions & CI](https://docs.appeeky.com/docs/spec-ci.md).

Related: [AI answers](https://docs.appeeky.com/docs/spec-ai-visibility.md) · [On your own keys](https://docs.appeeky.com/docs/spec-own-keys.md) · [GitHub Actions & CI](https://docs.appeeky.com/docs/spec-ci.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.