Last week, a founder in a Discord asked me a question I didn't have a good answer to:
"I spend 6 hours a week updating meta descriptions. How do I know ChatGPT even sees my site when someone asks it about my category?"
Fair question. I didn't know either. So I spent a weekend on it, and here is what I learned.
The actual question is simpler than the industry makes it
Every "AI visibility" vendor pitches a different thing. Some count citations. Some count mentions. Some run thousands of prompts and chart the trend. Pricing starts at $29/mo and goes north of $500/mo.
Under the marketing, there are really three things you can measure:
- Does the model mention your brand at all when asked a category question?
- In what position does it mention you (first, deep in a list, as an afterthought)?
- Who else gets named, and more often?
That is it. If your audit tells you more than that, read it carefully — a lot of "signals" are inferences on top of these three facts.
A script that just does the three
I wrote this to answer my own question. It is 300 lines of Python, zero dependencies beyond urllib and a Gemini API key (free tier).
import urllib.request, json, os
def ask_gemini(prompt):
key = os.environ["GEMINI_API_KEY"]
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-latest:generateContent?key={key}"
body = {
"contents": [{"parts": [{"text": prompt}]}],
"generationConfig": {"thinkingConfig": {"thinkingBudget": 0}}
}
req = urllib.request.Request(url, data=json.dumps(body).encode(),
headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req) as r:
return json.loads(r.read())["candidates"][0]["content"]["parts"][0]["text"]
Gotcha I lost 40 minutes to: thinkingBudget: 0 is not optional. Without it, Gemini 2.5 Flash spends its budget on hidden reasoning and returns a 100-character stump instead of the actual answer. First time I ran five prompts and got nothing but headings.
Then a small analyzer that reads the answer looking for your brand name:
def find_brand(text, brand, competitors):
text_l = text.lower()
mentions = {brand: text_l.count(brand.lower())}
for c in competitors:
mentions[c] = text_l.count(c.lower())
# Position: first heading or top-level numbered item containing brand
position = None
for i, line in enumerate(headings_and_top_level_lists(text)):
if brand.lower() in line.lower():
position = i + 1
break
return {"mentions": mentions, "position": position}
The ugly part is headings_and_top_level_lists. My first version counted every bullet, including nested ones under a competitor. Linear showed up at "position 1" because it was nested under "Jira is the leader, and alternatives include...". Not what you want.
Fix: only count headings and top-level numbered items. Skip nested bullets. Skip emphasis.
What the three competitors look like at a glance
I checked four tools last week while researching. Here is how they split the market:
| Tool | Price | What they do differently |
|---|---|---|
| Profound | $500+/mo (as of Sept 2026, starter plans discontinued) | Enterprise dashboards, citation graphs, team seats |
| Peec AI | €199/mo | Daily tracking, 4+ engines, European billing |
| Otterly | $29/mo | ChatGPT, Claude, Perplexity (no Gemini) |
| Findora | varies | Public-page audit + answer evidence labels |
My own script is the fifth entry on this list, and I will be honest about where it sits: it only talks to Gemini (the free API tier), it does not have a dashboard, and it runs weekly on a cron, not daily. For a founder spending $0 to get a weekly signal, that may be enough. For a marketing team, Otterly or Peec is the serious answer.
What the weekly report actually looks like
The report is a static HTML file (24 KB), generated by a GitHub Action. Structure I settled on after a few iterations:
Week of 2026-W43
Summary
5/5 prompts mentioned your brand
Average position: 1.2
Top competitors mentioned: 3
Prompt 1: "best project management tool for small teams"
Position 1 (headline)
Also mentioned: Asana, Jira, Trello
[full answer]
Prompt 2: ...
Nothing fancy. The point is you open an email on Monday, see "0/5 last week, 3/5 this week" at a glance, and decide whether to spend the hour on a landing page or a product change.
The failure mode of these tools (including mine)
Gemini (and the others) is not deterministic. The same prompt returns slightly different wording each time. One week your brand is at position 1, the next week position 3, next week position 1 again, and nothing changed on your site.
So do not act on a single week. Look at a 4-week rolling trend, or run the same prompt 3 times and take the median position.
I wrote this in the FAQ because it is the thing I would want someone to tell me before I bought.
Code and config
Repo: github.com/98dkcc488b-glitch/askvisible
# .github/workflows/weekly.yml
on:
schedule:
- cron: "0 7 * * 1" # Monday 07:00 UTC
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.11" }
- run: python run.py
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
Fork, add your config.json, commit, and the first report lands in your reports/ folder on Monday.
If you want the all-in-one package (config template, GitHub Action, email delivery via SMTP action, sample report), there is a $19 lifetime download — same code, just packaged. Build it yourself from the repo if you prefer.
Honest note about this post: I wrote it with Claude's help. The code and the methodology are mine, built and tested on a real Gemini key against a real brand. Posting it on dev.to because this is the audience that will tell me if the position-detection logic is wrong, and that is more valuable than any marketing channel I could buy.
This post was drafted with AI assistance and reviewed for accuracy.
Top comments (0)