Pull Google Search Console data, generate an SEO optimization strategy with an LLM (via Codex CLI), apply it to a local codebase OR convert it into a click-by-click GHL operator playbook, and re-measure after deploy.
Works on Windows / macOS / Linux. No Lods.AI dependencies.
- Python 3.10+
- Codex CLI installed (
npm i -g @openai/codexor your distribution's method) - An OpenAI API key configured for Codex (any plan that runs gpt-5.x will work)
- A Google Cloud project with the Search Console API + Site Verification API enabled
Your friend already has a domain property added in GSC — but it probably isn't verified yet for API access. The DNS-TXT verification path works on every registrar.
- Open https://console.cloud.google.com/iam-admin/serviceaccounts → pick (or create) a project → Create Service Account.
- Name it
gsc-reader→ no roles needed → Done. - On the service-account row: ⋮ → Manage keys → Add key → JSON.
- Save the JSON file as
gsc-service-account.jsonin this folder.
In the same project, enable:
- Google Search Console API (https://console.cloud.google.com/apis/library/searchconsole.googleapis.com)
- Google Site Verification API (https://console.cloud.google.com/apis/library/siteverification.googleapis.com)
Heads-up: Google's UI may show "Email not found" when you try to add a service-account user to a property. This is a widely-reported bug as of 2026. The DNS-TXT path below is the canonical fix — it's what Google's API docs actually recommend.
In a shell, get the verification token from Google:
# Replace yourdomain.com with the friend's actual domain
python -c "
from google.oauth2 import service_account
from googleapiclient.discovery import build
creds = service_account.Credentials.from_service_account_file(
'gsc-service-account.json',
scopes=['https://www.googleapis.com/auth/siteverification']
)
sv = build('siteVerification', 'v1', credentials=creds, cache_discovery=False)
resp = sv.webResource().getToken(body={
'site': {'identifier': 'yourdomain.com', 'type': 'INET_DOMAIN'},
'verificationMethod': 'DNS_TXT'
}).execute()
print('Add this TXT record to yourdomain.com root:')
print(' ', resp['token'])
"Add the printed google-site-verification=… value as a TXT record on the root
of yourdomain.com at your registrar (Cloudflare / GoDaddy / Namecheap etc).
Wait 5-10 minutes, then check propagation:
python verify_dns.py yourdomain.com --expect-token "google-site-verification=ABC123..."When all 3 resolvers see the token, finalize verification:
python -c "
from google.oauth2 import service_account
from googleapiclient.discovery import build
creds = service_account.Credentials.from_service_account_file(
'gsc-service-account.json',
scopes=['https://www.googleapis.com/auth/siteverification']
)
sv = build('siteVerification', 'v1', credentials=creds, cache_discovery=False)
sv.webResource().insert(
verificationMethod='DNS_TXT',
body={'site': {'identifier': 'yourdomain.com', 'type': 'INET_DOMAIN'}}
).execute()
print('✓ verified')
"The service account's client_email now has access to the GSC property.
(You can also add the service account as a "Full" user in GSC Settings → Users
& Permissions — but if the UI throws "Email not found", DNS verification works
instead.)
pip install -r requirements.txt
python pull_gsc.py --site "sc-domain:yourdomain.com" --days 90 > gsc_data.jsonIf your property is URL-prefix style (not domain), use the URL form:
python pull_gsc.py --site "https://yourdomain.com/" --days 90 > gsc_data.jsonYou should see a JSON dump with top_queries, top_pages, daily, sitemaps,
etc. Skim it — make sure clicks > 0.
Open Codex in this directory:
codexPaste the prompt from prompts/01-strategy.md.
Codex will read gsc_data.json + write strategy.md.
Read strategy.md. Tweak it by hand if you disagree with any priority.
Code-based site (Astro / Next / Hugo / vanilla HTML):
# cd into your site repo, copy strategy.md over, then:
codexPaste the prompt from prompts/02-apply-local.md.
Codex shows diffs, you approve, it writes the changes.
GoHighLevel site:
codexPaste the prompt from prompts/03-ghl-handoff.md.
Codex writes ghl-playbook.md — a click-by-click guide. Your friend follows
it inside the GHL UI.
python pull_gsc.py --site "sc-domain:yourdomain.com" --days 14 > gsc_after.jsonOpen Codex. Paste:
Compare
gsc_data.json(90d baseline) againstgsc_after.json(last 14d since the SEO updates). Which actions moved the needle? Which didn't? Surface anything unexpected (queries that gained or lost rank we didn't touch). Output a 1-page report atseo-impact.md.
| Path | Purpose |
|---|---|
pull_gsc.py |
Standalone GSC puller — dumps last N days to JSON |
verify_dns.py |
Cross-resolver DNS TXT propagation checker |
prompts/01-strategy.md |
Codex prompt: GSC JSON → strategy.md |
prompts/02-apply-local.md |
Codex prompt: strategy.md → local code changes |
prompts/03-ghl-handoff.md |
Codex prompt: strategy.md → GHL operator playbook |
examples/ |
Sample GSC dump, strategy, and GHL playbook for reference |
.env.example |
Template for the service-account path env var |
Service-account JSON not found — drop gsc-service-account.json in this
folder (it's in .gitignore).
GSC API returns 403 — your service account isn't a user on the property
yet. Either verify the domain via DNS TXT (preferred — see Section 1c) or add
the client_email from the JSON as a "Full" user in GSC Settings → Users.
No rows returned — either the property is brand new (<2 days) or the
window is too narrow. Try --days 90 first. GSC also has a 1-2 day data lag.
Codex can't find gsc_data.json — make sure you cd into the directory
containing the JSON before running codex.
Once your friend is comfortable with the manual flow, the same modules can run on a schedule:
- Cron (Linux/macOS) or Task Scheduler (Windows) → run
pull_gsc.pyweekly, diff against last week, ping a Slack/Telegram webhook with the deltas. - A Codex routine (if his plan supports them) can run the whole pipeline daily and email the strategy.