Docs

How to read AIMentionTracker's numbers

Every rule below describes a way to be wrong that looks exactly like being right. That is why they are documentation and not a footnote.

Card required, nothing charged for 7 days

Why this page exists

A client that misreads this data does not crash. It renders. It produces a chart, an email, a slide in a client review — and the wrong version is indistinguishable from the right one by anyone reading the output, because the output is a number and numbers look equally confident whatever is behind them.

We sell measurement honesty. If our own API makes it easy to publish a confidently wrong figure in a customer's name, the product's claim is refuted by its own integration. So the rules travel with the data: GET /me returns the thresholds in force, every rate carries its denominator, and the three rules below are repeated on five pages rather than one.

Three rules you must not break

These are not style preferences. Breaking them produces a confidently wrong report in your customer's name.

  1. 1

    A null rate means "not enough answers yet". It is NOT zero.

    Rates come back as {present, responses, rate, min_sample}. When rate is null, the sample is below the floor. Say “3 of 4 answers, too few to quote a percentage”. Never render it as 0%, and never let it into an average. Reporting “0% visibility” for a brand that was named in 3 of 4 answers is the single most damaging thing you can do with this data.

  2. 2

    Never average the engines.

    There is no single visibility score and you must not compute one. ChatGPT, Perplexity and Google AI Overviews are different surfaces that disagree, so a mean describes nothing real. Report per engine. If the user insists on one number, give them the pooled counts (present and responses summed) and say plainly what you did.

  3. 3

    An empty alerts list does NOT mean nothing changed.

    A finding is raised only when both comparison windows carry enough answers AND the move is larger than its own margin of error. On a small plan that excludes most real movement. Say “no change large enough to distinguish from noise at this sample size”. GET /me shows the thresholds in force.

Rule one: null is not zero

Every rate in the API is an object, never a bare number, and the denominator is always there:

A real case
{
  "engine":     "perplexity",
  "present":    3,
  "responses":  4,
  "rate":       null,
  "min_sample": 5
}

Three of four Perplexity answers named the brand. The rate is null because four answers is too small a sample to quote a percentage from — 75% and 100% are one answer apart, and next week the same brand could read 50% with nothing having changed but which four answers landed.

A client has three honest options and one wrong one.

  • Say the fraction.“Named in 3 of 4 Perplexity answers.” This is almost always the right choice.
  • Say the sample is too small.“Not enough answers yet to quote a rate.”
  • Show nothing, and say why.A dash with “below the 5-answer floor” beats a confident figure.
  • Never render it as 0%. And never feed a null into an average, where it silently becomes a zero one layer down.

Do not hardcode 5. min_sample arrives on every rate and reporting_rules.min_sample arrives on GET /me, so a client that reads them follows a change in the floor without a release.

Rule two: never average the engines

ChatGPT, Claude, Gemini, Perplexity and Google's AI surfaces retrieve differently. One reads the live web on every query; another leans on what it already knows; another is a search result page with an answer on top. They disagree about your brand because they are genuinely looking at different things.

So a mean across them describes no surface a buyer will ever see. Worse, it hides the finding: a brand named in most ChatGPT answers and almost no Perplexity ones has a specific, fixable problem, and averaging turns that into an unremarkable middling number with nothing to act on.

Report per engine. If somebody insists on one figure, pool the counts rather than the rates — sum present, sum responses, divide once — and label it as pooled, because that at least is a real quantity with a real denominator.

If you must have one number
const present   = engines.reduce((n, e) => n + e.present, 0)
const responses = engines.reduce((n, e) => n + e.responses, 0)

// Say what this is: "named in 21 of 34 answers across 5 engines",
// NOT "62% AI visibility". And skip it entirely below min_sample.

GET /me returns reporting_rules.engines_averaged: false. It is always false. It is in the response so a client can state the rule to a user rather than leave them wondering why there is no single score.

Rule three: an empty alerts list is not silence

A finding has to clear two bars before we raise it. Both comparison windows must carry enough answers, and the move must be larger than its own margin of error — a 95% confidence interval on the difference of two proportions. Whichever bar is higher wins.

On a small plan, that suppresses most real movement. This is on purpose. An alerting system that fires on noise trains people to ignore it, and then it fails at the one thing it exists for.

But it means an empty list carries a specific meaning, and it is not “nothing changed”. It is “no change large enough to distinguish from noise at this sample size”. A client that renders the first one is telling a customer something we did not measure.

GET /me tells you the thresholds in force
"reporting_rules": {
  "alerts": {
    "min_delta_points":  ...,   // percentage points of change required
    "min_sample_each_side": ...,// answers needed on BOTH sides
    "also_suppressed_below_statistical_noise": true,
    "empty_list_means": "..."   // the honest phrasing, ready to surface
  }
}

empty_list_means is a string you can put in front of a user directly. It exists so the phrasing does not have to be reinvented — correctly — in every client that calls this API.

Our failures are not your absence

A collection can fail. An engine times out, a scrape is blocked, a vendor has an incident. When that happens the run is excluded entirely: it is not in the denominator, and it never appears in GET /answers.

The alternative — counting a failed run as an answer that did not name you — would turn our outage into your visibility drop. You would see a fall, investigate a problem that does not exist, and the number would recover when our vendor did. That is the one direction an error in this product must never point.

outcome: "empty" is the opposite case and it is counted. The engine answered and named nobody. That happened, we watched it happen, and it belongs in the denominator — it is evidence that a prompt produces answers where no brand gets recommended at all, which is a different and often more useful finding than losing to a competitor.

AI visibility metrics people misread

Four more, each one a place where the obvious reading is the wrong one.

  • avg_rank_when_named is conditional. It is the mean position on the answers where you appeared, not across all answers, and it is null when you were never named. How often you appear and where you sit when you do are two questions; this field answers only the second.
  • framing is not sentiment. It counts cue phrases and reports how many mentions it could classify at all — classified is the denominator, not the total mentions. framing_signals returns the phrases behind the judgement so it can be checked. Do not present it as a tone score, because it does not grade tone.
  • citations counts answers, not links. It is how many answers cited your own domain, so an answer citing you three times counts once. And a citation is not a mention: being read and being named are different, which is the whole point of answers_citing_you_without_naming_you on the sources endpoint.
  • mentioned: false rows are data. They are not missing entries. They mean we looked for that brand in that answer and it was absent — which is what makes a competitor comparison meaningful rather than a list of the ones that happened to show up.

Check your client against this

Five minutes, and it catches almost everything. Run each of these against your own integration before it goes in front of a customer.

  • Feed it a rate: null row. Does any screen show 0%?
  • Feed it two engines, one null. Does an average appear anywhere, including in a chart axis or a sparkline?
  • Feed it an empty alertsarray. Does the interface say “no changes”?
  • Feed it a brand with configured: false. Does it draw zeroes instead of saying setup is unfinished?
  • Change min_sample in the fixture. Does your client follow it, or is 5 hardcoded somewhere?
Start from the live rules, not from a constant
curl -H "X-API-Key: $AMT_API_KEY" https://app.aimentiontracker.ai/api/v1/me

Questions about reading the data

The ones that decide whether a report is honest.

What does a null rate mean in AIMentionTracker?
It means the sample is below the floor of 5 answers, so no percentage can honestly be quoted yet. It does not mean zero. Present and responses are still returned, so say "3 of 4 answers" instead. Rendering null as 0% reports a collapse in visibility that did not happen.
Why does AIMentionTracker not give one AI visibility score?
Because the engines are different surfaces that retrieve differently and disagree, so a mean across them describes nothing that exists. A brand can be named in most ChatGPT answers and almost no Perplexity ones; one number hides exactly the thing you would act on. GET /me returns engines_averaged: false so a client can state this rather than assume it.
Does a failed collection count as my brand being absent?
No. An error or a timeout on our side is excluded from the denominator entirely and never appears in the answers list. Our outage must never read as your absence — that would be our failure reported as your problem, which is the one direction an error in this product must never point.
What does outcome "empty" mean on an answer?
That the engine answered and named nobody. It is a real observation, it stays in the denominator, and it is different from a failed collection in a way that matters: it is evidence that a prompt produces answers where no brand gets recommended at all.
Does an empty alerts list mean nothing changed?
No. A finding is raised only when both comparison windows carry enough answers and the move is larger than its own margin of error. On a small plan that suppresses most real movement. The honest phrasing is "no change large enough to distinguish from noise at this sample size", and GET /me returns the thresholds in force.
Is framing a sentiment score?
No, and it is deliberately not called one. It counts cue phrases in the sentence that named you and reports how many mentions it could classify at all. It does not grade tone. The classified count is the denominator, and framing_signals returns the phrases behind each judgement so it can be checked rather than trusted.
What is avg_rank_when_named?
The mean position in the answer's list on the answers where you were named — not across all answers. It is null when you were never named. Averaging it against answers that did not name you would mix two different questions: how often you appear, and where you sit when you do.

7-day trial

Numbers you can put your name on

Every rate arrives with its denominator, every engine is reported separately, and our outages are never your absence. Add your brand and see the answers behind it.

Card required, nothing charged for 7 days.

On every plan

  • Every answer stored in full, with its citations.
  • Every rate shown with the number of checks behind it.
  • Cited-but-not-named reported as its own state.