AI Visibility
AI platforms are becoming a discovery channel: people ask Perplexity, Gemini, Claude, or ChatGPT "what's the best X?" instead of scrolling search results. AI Visibility is the rank tracker for those answers — it queries the platforms with the prompts your buyers actually ask and measures three things:
- Presence rate — the share of answers that mention your brand (or cite your domain), tracked over time.
- Cited-domain map — which websites the platforms cite as sources, classified as yours, a competitor's, or other (review sites, Reddit, editorial). These are the pages that shape AI answers about your category.
- Per-platform breakdown — how Perplexity, Gemini, Claude, and ChatGPT compare, so you know where you're strong and where you're invisible.
Everything runs against the platforms' official APIs — no scraping.
Not the same as Bot & Crawler Detection
AI Visibility measures how AI answers talk about your brand. To see which AI crawlers (GPTBot, ClaudeBot, PerplexityBot, …) visit your website, use Bot & Crawler Detection — the two are unrelated datasets.
Setup
AI Visibility is configured per project under AI visibility → Visibility in the project sidebar.
1. Configure provider API keys (server)
The analysis calls provider APIs with keys configured on the Kitbase server. A search provider with no key is simply disabled — with one exception: GEMINI_API_KEY is required to run analyses at all, because beyond powering Gemini it also runs the answer-analysis pass that extracts brand mentions from every answer (a separate, non-grounded gemini-2.5-flash call). Without it, starting an analysis is rejected and scheduled runs are skipped.
| Env var | Provider | Notes |
|---|---|---|
PERPLEXITY_API_KEY | Perplexity Sonar | Uses the sonar model; citations and actual request cost come back in the response |
GEMINI_API_KEY | Google Gemini | Uses Search grounding; cost is estimated from configured rates |
ANTHROPIC_API_KEY | Claude | Uses the Messages API's web_search tool; cost is computed from real token + search usage in the response |
OPENAI_API_KEY | ChatGPT | Uses the Responses API's web_search tool; cost is computed from real token + search usage in the response |
On Kitbase Cloud, which platforms your analyses run against is set by your plan — Starter tracks ChatGPT; Pro adds Gemini and Perplexity; Business covers every available answer platform. Per-organization overrides are supported.
Self-hosted deployments supply their own keys and run against every platform they've configured. Model names, per-minute rate limits, cost rates, and the default monthly spend cap are configurable under the ai-visibility: section of the server config; on Kitbase Cloud the monthly limit is set per plan.
2. Add your brand and competitors
Create one self brand (your product) with:
- Primary domain — the registrable domain (e.g.
example.com). Citations of this domain count as self-citations and classify the domain map. If the project has a website connected (set at project creation or on the project's Website page), the field is prefilled with it — a brand can still use a different domain than the tracked site. - Aliases — alternative names the platforms might use (
Kitbase,kitbase.dev, …). An extraction model parses every answer into the full list of companies it names; those extracted names are then matched to your brands by name, alias, or domain — so add the spellings and short forms the platforms actually use.
Add competitor brands the same way (name + domain + aliases) to see them in the domain map and mention data. You don't have to know them all up front — once analyses run, Suggested competitors proposes brands the AI itself named that you aren't tracking, and one click adds them here (see Reading the results). Brand changes apply retroactively: adding, renaming, or removing a brand (or its aliases) automatically re-matches your stored analysis history against the new list, so a newly added competitor appears with its full history instead of starting from zero — no re-run and no extra AI cost.
Tracking a competitor only in some topics
Competitor sets are rarely uniform across everything you measure. A brand may compete directly with you on one topic and be irrelevant on another, where its appearance in answers is noise that inflates its rank and dilutes everyone's share of voice.
Each competitor on the setup page carries a topic scope next to its domain — All topics by default, or N topics excluded. Open it to uncheck the topics that competitor should not be measured in.
An excluded competitor is treated as not existing for the runs of that topic, rather than as present zero percent of the time. In practice:
- Those runs leave the competitor's denominator as well as its numerator, so its visibility rate reads over the topics it is actually tracked in.
- It contributes nothing to share of voice on those topics — the remaining brands re-normalise to 100%.
- Filter the results to a topic it is excluded from and it disappears from the competitor leaderboard, the trends chart, share of voice, and the topics breakdown entirely.
- Prompts with no topic always count. Only real topics can be excluded.
The scope is applied when results are read, not when answers are analysed, so it applies to your whole stored history the moment you change it and costs nothing to undo — re-check a topic and the competitor's full history is back. Archiving a topic leaves its exclusions in force over the runs already tagged with it, so past numbers don't silently move.
3. Add prompts
Prompts are the questions your buyers ask AI platforms — e.g. "best session replay tool for startups", "Kitbase vs PostHog", "how do I track AI crawler traffic". Add them in bulk (one per line).
Once you have a few, the prompts page lets you work on several at once: tick the rows you want and the summary strip turns into a toolbar that can move them to a topic or delete them. Both apply as a single transaction — if one prompt in the selection can't be changed, none of them are.
Deleting is how you remove a prompt, and it is permanent. The prompt's answers go with it, and every period it ran in loses its contribution to the numbers — a deleted prompt stops shaping last month's figures as well as next month's. If you want a prompt to stop running but keep its history and its place in past periods, set active: false on it instead — through the API, or through the active column of the spreadsheet the prompts page can download and re-upload. Prompts switched off that way still appear in the table, struck through, so they stay reachable.
On Kitbase Cloud the number of active prompts per project is plan-limited (per-organization overrides supported). Prompts with active: false keep their historical data and don't count against the limit. AI visibility is also limited to a set number of projects per organization by plan (Starter 1, Pro 2, Business 5; overrides supported) — a project claims a slot once it has its first active prompt. Self-hosted deployments use the configured server cap instead.
4. Add personas (optional)
A persona is the buyer you're tracking for: a name plus a few sentences on what they're trying to do, what gets in their way, and the words they'd actually type into an AI assistant — "Ops lead at a 3PL. Drowning in manual freight paperwork. Searches things like 'cheapest way to automate freight documents'."
Personas do two things, and only these two:
- They shape the prompts you write. Ask for prompt suggestions with a persona selected and the generated questions carry that buyer's situation as context — "best freight automation for a 40-person 3PL". Each suggestion comes back labelled with the persona it was written for, a one-line reason, and a suggested intent tier, so you can tell what you're about to spend a prompt slot on.
- They group your results. Every prompt belongs to exactly one persona, so each answer lands in exactly one bucket and per-persona share of voice adds up to your whole result set. That's what makes "we're strong with ops leads and invisible to CFOs" a statement you can trust rather than an artefact of double-counting.
A persona is never sent to an AI platform. It doesn't change what we ask, it isn't a roleplay instruction, and it doesn't multiply what a run costs — two personas on the same question is still one question. This is deliberate: prompts phrased as roleplay ("You are an IT consultant…") pull answers toward general explanation and away from naming products, which would move your visibility number instead of segmenting it.
Personas are a simulated buyer lens, not observed demand. They describe how we'd expect a given buyer to ask — no tool can show you what real people typed into ChatGPT.
You can draft personas with AI from your brand and domain, but drafts are never saved on their own: you edit and accept each one. An unreviewed auto-generated persona comes out broad enough ("Marketing Manager") that it produces the same generic prompts as no persona at all. Two or three specific personas beat a long list. On Kitbase Cloud the number of active personas per project is plan-limited; archiving one frees a slot, keeps past runs resolving, and frees the name for reuse.
Prompts written before you added personas have no persona attached. The setup screen offers a one-click backfill that applies each prompt's current persona to its past runs, so historical comparisons aren't split in half by the day you started using them.
5. Regions (set by support)
By default every project is measured from the United States — that's the vantage point AI platforms answer from, and it's what a project runs unless support adds more.
A region is the unit you are measured in — US or EU. Some regions span more than one country internally; which ones is server configuration (ai-visibility.regions.*), so self-hosted deployments can redefine what a region spans.
Each prompt runs once per region. Where a region spans several countries your prompts are spread across them and the answers roll back up to the region, so a multi-country region costs one pass, not one per country. A given prompt always lands in the same place, so its trend line compares like with like run over run.
That makes the cost multiplier the number of regions you track: adding EU to a US-only project roughly doubles its provider spend, not quadruples it. Both the pre-run estimate and your monthly limit account for this, so a multi-region run is rejected the same way an oversized single-region one is.
On Kitbase Cloud the region set is configured per organization by support — contact them to add one. It is not something you turn on yourself, precisely because each region multiplies what every project in your organization spends per run. Every project in the organization measures from the whole set, and the Setup tab shows you which regions are running and the countries behind each.
Removing a region takes effect immediately: projects fall back to whatever remains. Self-hosted deployments have every region available.
Once more than one region is live, every result view can be filtered by region, and each individual run records both the region it reports under and the exact country it queried.
Prompt Explorer
AI visibility → Explorer answers "what does AI say about us right now?" without any of the setup above. Type a question, tick up to four platforms, and every answer lands side by side in about ten seconds.
Nothing here is tracked. The question is not added to your prompt set, the answers never enter presence rate, share of voice, or any other measurement, and running one has no effect on your trend lines. It exists for the two moments the tracked product is bad at: the first five minutes with a new project, and the middle of writing a prompt when you want to know what it actually returns before committing to measuring it.
Each answer column carries:
- the platform and model that produced it, and whether that is a model API answer or a consumer surface. These are not the same evidence — the ChatGPT API answers as a configured model, which is not what a person sees on chatgpt.com — so the label is on every card rather than left to be inferred from the platform name.
- the brands it named, split into yours and competitors, when Detect your tracked brands is on. Same extraction pass the tracked runs use.
- the sources it cited, and the searches the platform actually ran — often the most useful part, because a prompt about "best event lead capture software" is frequently answered by searching for something else entirely.
- Highlight a brand takes any name at all, tracked or not, and reports whether each answer and each source names it. Use it on a prospect's brand, a competitor nobody has configured yet, or a product you are merely curious about.
Track this prompt on the results header adds the question to your tracked set, where it starts building a trend from the next analysis. That is the path this feature exists to open: try it, then track it.
What it costs
Every run spends your organization's AI budget — one provider call per platform, plus one extraction call per platform when brand detection is on. Three things keep that bounded:
- An identical question is free for seven days. Same prompt, same region, same platforms, same server-side settings reuses the stored answers and calls no platform. Cards served that way are marked Cached answer. Run fresh deliberately bypasses it and pays again.
- A per-organization daily cap limits how many explorations can be started in a rolling 24 hours, independent of the monthly budget.
- Up to four platforms per question.
Which platforms you can explore with is a plan setting, and it is deliberately separate from the platforms your tracked analyses use — the direct model APIs cost materially more per call than the consumer surfaces tracking prefers. The picker only ever shows what your plan actually allows; if it is empty, contact support.
Explorations are shared across your project's users (so a colleague opening one costs nothing) and are deleted automatically after 30 days. Recent explorations, above the answers, is the way back to any of them — opening one re-reads the stored answers and pays nothing.
Platforms settle independently: if one is busy or down, its column says so while the others show their answers. Region selection follows the same rule as tracked runs — a region samples one of its countries, so EU is a European reading rather than an exact-country choice, and there is no web-search toggle because search behaviour is decided per platform by the server.
Requires the aivisibility.manage permission, since it spends money.
Running an analysis
On Kitbase Cloud with an active paid subscription, analyses run automatically every 24 hours — the dashboard shows a countdown to the next run in place of the run button. An automatic run only starts once the project is fully configured (your own brand plus at least one active prompt) and skips any cycle that would exceed the organization's monthly limit.
On trials and self-hosted deployments, click Run analysis — the run starts immediately. A run that would exceed your organization's monthly limit is rejected.
Suspended organizations keep read access to their AI visibility data and get a single lifetime analysis to try the feature. Once that run is used, starting another analysis (or requesting prompt suggestions) returns BILLING_018 until the plan is upgraded; the full reset is also unavailable while suspended.
Each analysis is a background job with one unit of work per (prompt × platform). Jobs are:
- Resumable — completed provider calls are recorded and never re-executed, so a server restart or deploy mid-run resumes from where it left off instead of re-paying for finished queries.
- Pausable — pause settles after the current batch; resume continues with the remaining prompts only.
- Cancellable — cancelling keeps the results of already-completed units.
Only one analysis can be active per project. Each completed job becomes one data point in the presence-rate chart, so the daily automatic run (or a regular manual click) builds your trend line. AI answers are non-deterministic — treat single runs as samples and read the trend, not one day's number.
Reading the results
Visibility by persona breaks the same window down by buyer, one row per persona plus a row for prompts with no persona, with each tracked brand's share of voice and visibility rate inside that persona's prompts. Because the rows partition your runs rather than overlapping, they are directly comparable with each other and reconcile to your project totals.
- Presence rate counts a prompt as "present" when the answer mentions any of your aliases or cites your primary domain. Filter by platform or view the combined rate. The presence series and per-platform breakdown also report the mentioned and cited rates separately, so you can see whether platforms talk about you, link to you, or both.
- Presence by platform over time plots one line per AI platform (Perplexity, Gemini, Claude, ChatGPT) across your completed analyses, so you can see which platforms are picking you up — and which are trailing — as your presence trends. Toggle between the presence and cited rate.
- Competitor trends over time plots one line per tracked brand (you plus your top competitors), so you can watch how your presence or citation rate moves against theirs run over run, not just at a single point in time.
- Share of voice normalizes each brand's presence against the total across all tracked brands per run, with a rank over time — a competitive share, not just your own rate.
- Suggested competitors surfaces brands the AI named on its own that you aren't tracking yet — extracted from each answer during the analysis pass and ranked by how many runs mentioned each. Anything you already track (your brand or a competitor, by name or alias) is filtered out, so the list is purely new discoveries. Click Track on one to start measuring it against you: it moves straight onto the competitors leaderboard from the next run.
- Cited domains aggregates citations across recent runs. Domains classified OTHER with high citation counts are where the platforms get their information — prime targets for content or PR. Each domain also carries a source type (UGC, review-site, news, reference, social, docs, editorial, or vendor) from a curated list, so you can see which kinds of sources shape answers in your category.
- Cited pages lists the exact pages (URLs) the platforms cited, across every domain, most-cited first — each with its page title, domain, source type, and the platforms that cited it. Switch the scope to Mentioning you to only count citations from answers where your brand appeared: those are the pages the platforms lean on when they talk about you, and the best places to be present (or to pitch).
- Framing — the extraction pass labels every detected mention with sentiment (positive / neutral / negative) and whether the answer recommends the brand versus merely mentioning it. When the answer presents a ranked list, it also records the brand's position within it.
- Per-prompt breakdown shows presence (and the analysis signals) for every prompt across each platform — a prompt × platform heatmap of where you win and lose.
- Answers is its own tab: every answer the platforms gave, newest first, one row each. Search for a word in the prompt or the answer (the excerpt lands on the hit), then narrow by platform, region, topic, which tracked competitors the answer named, where your brand landed in it (listed first, top 3, top 5, mentioned at all, or absent), and how the mention read (positive / neutral / negative). Every filter is in the URL, so a slice of answers is a link you can send. Clicking a row opens the full run drill-down.
- Sponsored placements shows who is buying ad slots inside the AI answers for your prompts. Google sells placements within AI Mode and AI Overview answers, and ChatGPT sells them inside its own answers; each advertiser is classified against your tracked brands the same way a cited domain is — so the panel answers the question that matters: is a competitor paying for the answer you already rank in? Every number is scoped to the platforms that can report ads at all (AI Mode, AI Overview, ChatGPT), so a zero means no ads were shown, never "we didn't look". ChatGPT also reports which ads were actually displayed: an ad the model was served but never put on screen is excluded from these counts, because nobody saw it. Each advertiser carries its ad headline and description, which is the competitor's own paid positioning, refreshed on every run.
- Run drill-down shows the full answer text, extracted citations, and detected brand mentions for any individual query — including each mention's sentiment, recommended flag, and rank — useful for verifying matches and understanding phrasing. When the answer carried advertising, the sponsored results are listed alongside it.
How detection works
Each answer is processed once, right after it's collected: an extraction model parses the full answer text into a structured list of every company or product it names, each with its sentiment, recommendation status, list position, and any domain the answer ties to it. That extraction is stored per run. Matching extracted names to your tracked brands then happens in plain code (by name, alias, or primary domain) — which is why brand edits re-match history instantly without any new AI calls, and why untracked names can surface as Suggested competitors. Citations are matched separately by comparing each cited domain to your brands' primary domains.
The analysis signals (sentiment, recommended, rank, source type, mentioned/cited split) are populated for jobs run after the feature shipped; older jobs report them as not analyzed (null) rather than zero, so mixed-window charts don't understate rates. Runs from before the extraction pipeline keep their original mention data and are not re-matched on brand changes.
Tracking runs also feed Kitbase's aggregated public statistics at kitbase.dev/data — which domains each AI engine cites most, how often it cites anything at all, and how many sources an answer carries. Those pages publish percentages pooled across every customer and nothing else: no counts, and nothing that identifies a customer, project, prompt, brand, or end user. Your own domain is excluded from your own answers before anything is aggregated.
Tracking a new brand (reset)
To start over with a different brand, open Setup → Danger zone → Reset AI Visibility. The reset permanently deletes the project's entire AI Visibility state: all analysis history (jobs, runs, answers, citations, mentions, and metrics) and all configuration (your brand, competitors, and prompts). Any running analysis is stopped. Afterwards the project returns to the setup wizard so you can configure the new brand from scratch. Requires the aivisibility.manage permission and cannot be undone.
API
All endpoints live under /{orgSlug}/projects/{projectId}/ai-visibility/ and require a bearer token. Viewing requires the aivisibility.view permission; configuration and job control require aivisibility.manage.
| Method & path | Purpose |
|---|---|
DELETE (feature root) | Full reset: deletes all history and configuration to start tracking a new brand |
GET/POST /brands, PUT/DELETE /brands/{brandId} | Brand + competitor CRUD. aliases and excludedTopicIds are part of the payload and are replaced wholesale — send the full set on every write, or an omitted one clears it. excludedTopicIds is rejected for the self brand |
GET/POST /prompts, PUT/DELETE /prompts/{promptId} | Prompt CRUD (POST is bulk and accepts topicId/personaId for the new prompts; DELETE is permanent — the prompt's runs and metrics go with it, so past periods lose its contribution. To stop a prompt but keep its history, PUT it with active: false). PUT replaces the prompt's own fields only and never touches its topic or persona — move those with the two calls below |
POST /prompts/bulk-move | Moves up to 500 prompts into one topic in a single transaction — {"promptIds": [...], "topicId": "…"}, with an omitted or null topicId moving them out of every topic. All or nothing: an unknown prompt id, or a topic that isn't an active topic of this project, fails the call and moves nothing. Pass a single id to move one prompt |
POST /prompts/bulk-assign-persona | Assigns one persona to up to 500 prompts — {"promptIds": [...], "personaId": "…"}, with an omitted or null personaId unassigning them. Same all-or-nothing rule, and the call never touches their topic or text. Separate from the move because each call owns one column, which is what lets null mean "unassign" on both without a flag saying whether the other was meant to be left alone |
POST /prompts/bulk-delete | Permanently deletes up to 500 prompts — {"promptIds": [...]} — with the same cascade as a single DELETE. All or nothing, for the same reason |
POST /prompts/suggest | Candidate prompts for a brand, not persisted. Pass personaIds to write them for specific buyers; each suggestion returns its personaId, a reason, and a suggested intentTier |
GET/POST /topics, PUT/DELETE /topics/{topicId} | Topic CRUD (DELETE archives: prompts are uncategorized, history keeps resolving the topic) |
POST /topics/backfill | Applies each prompt's current topic to its pre-topic runs. {"dryRun": true} returns only eligibleRuns |
GET/POST /personas, PUT/DELETE /personas/{personaId} | Persona CRUD (DELETE archives: prompts are unassigned, history keeps resolving the persona). Plan-limited per project |
POST /personas/suggest | Drafts 2-3 candidate personas from a brand name and domain. Nothing is persisted — save the ones you want via POST /personas |
POST /personas/backfill | Applies each prompt's current persona to its pre-persona runs. {"dryRun": true} returns only eligibleRuns |
GET /topics-breakdown?jobs=10 | Per-topic visibility and share of voice, one row per topic plus an uncategorized bucket |
GET /personas-breakdown?jobs=10 | Per-persona visibility and share of voice, one row per persona plus a no-persona bucket. Rows partition the window, so their shares are directly comparable |
GET /jobs/estimate | Pre-run summary: active prompt count, the platforms that will run, and whether the run is allowed |
GET /schedule | Auto-run schedule state: whether automatic runs are enabled, when the next run is due (nextRunAt, secondsUntilNextRun), and whether a manual run is currently allowed |
POST /jobs | Start an analysis job |
GET /jobs, GET /jobs/{jobId} | Job history and live progress, incl. the platforms each job ran (providers) |
GET /jobs/{jobId}/runs?regions=US | The job's per-(prompt × platform × region) runs (each carries its promptId, region, and the market country actually queried) |
GET /regions | Regions this project is measured from, each with the countries it covers. Never empty (US at minimum). Read-only — the set is granted per organization by support, so there is no write endpoint |
POST /jobs/{jobId}/pause · /resume · /cancel | Job control |
GET /visibility?provider=ALL&limit=30 | Presence-rate series per job, incl. runsWithMention / runsWithCitation and derived mentionRate / citationRate |
GET /provider-series?limit=30 | Presence series per job split by platform — one entry per AI provider (self brand), each with presenceRate / citationRate / mentionRate. Powers the per-platform trend chart |
GET /competitor-series?provider=ALL&limit=30 | Presence series per job split by brand — one entry per tracked brand (self + competitors), each with presenceRate / citationRate and normalized shareOfVoice. Powers the competitor trend chart |
GET /domains?provider=ALL&jobs=10&limit=25 | Cited-domain map over recent jobs, incl. sourceType, the citing providers (AI platforms), and citationShare — the domain's share (0–1) of every citation the window recorded, not only the domains on this page. Null when the window recorded no citations at all |
GET /domains/{domain}/citations?provider=ALL&jobs=10&page=0&size=20 | Drill-in for one cited domain: totals, citing providers, and the paginated list of exact cited URLs |
GET /citations/pages?provider=ALL&mentioningBrand=false&jobs=10&page=0&size=20 | Flat cited-pages list across all domains, most-cited first, each with domain, title, classification, sourceType, the citing providers, and citationShare — the page's share (0–1) of every citation the window recorded, taken against the same denominator the domain map uses. mentioningBrand=true only counts citations from answers where your brand appeared |
GET /breakdown?jobs=10 | Per-platform comparison, incl. mentioned/cited splits, sentiment counts, recommendedRate, and avgAnswerRank |
GET /competitors?jobs=10&limit=25&provider=ALL | Every tracked brand ranked, incl. shareOfVoice, mentionRate, citationRate, recommendedRate, avgAnswerRank, rank buckets, and sentiment counts |
GET /discovered-competitors?jobs=10&limit=25&provider=ALL&topics=®ions= | Suggested (untracked) competitors the AI named organically, ranked by mentionCount (distinct runs), with the citing providers and a best-effort primaryDomain. Already-tracked brands are excluded. Track one via POST /brands |
GET /share-of-voice?provider=ALL&jobs=10 | Per-job share-of-voice series: each brand's normalized share and dense rank |
GET /prompts-breakdown?jobs=10 | Per-prompt breakdown with an overall rollup plus one cell per platform |
GET /answers?page=0&size=20&search=&brandIds=&position=&sentiments= | Project-wide answer list, newest first. Takes every shared filter plus four of its own: search (a word in the prompt or answer text), brandIds (answers naming any of these tracked brands), position (FIRST / TOP_3 / TOP_5 / MENTIONED / NOT_MENTIONED for your own brand), and sentiments (how your brand's mention read). Each row carries an excerpt, your brand's outcome (selfPresent / mentioned / cited / answerRank / sentiment), and every tracked brand the answer named |
GET /prompts/{promptId}/runs?provider=ALL®ions=US | Recent answers for one prompt (a fixed-size history), each with its region and market |
GET /paid-placements?provider=&jobs=10 | Advertising shown inside the AI answers, over the same job window as every other read. Returns advertisers (domain, classification, placementCount, promptCount, the platforms, and a sample ad headline/description) and prompts (which of your prompts drew ads, how many competitorAdvertisers, and selfMentioned — whether your brand was named organically in an ad-carrying answer). promptsMeasured / runsMeasured are the denominators, counted only over platforms that can report ads. Ads a platform reported as served but never displayed are excluded from every count |
POST /paid-placements/backfill | Re-reads the responses already stored on this project's runs (SERP and ChatGPT alike) to recover the placements they carried. Never calls a provider, so it costs nothing and is safe to re-run. {"dryRun": true} returns only eligibleRuns. Also repairs runs whose stored answer turned out to be the ad block (answersRepaired), which leaves those runs unanalyzed until they are extracted again. Requires aivisibility.manage |
GET /exploration-providers | The platforms Prompt Explorer can actually run against for this organization: the server's configured synchronous adapters intersected with the plan's exploration platforms. Deliberately different from the platforms tracked analyses use — render the explorer's picker from this, never from a hardcoded list or from the tracking set |
POST /explorations | Runs one ad-hoc prompt against 1–4 platforms synchronously and returns every answer with its citations, tracked-brand mentions, fan-out queries and cache provenance. Body: promptText, providers[], optional region, analyze (default true), highlightBrand, runFresh. Spends provider budget on every call, is subject to a per-organization daily cap on top of the monthly budget, and reuses an identical question's answers for 7 days unless runFresh is set. Platforms settle independently, so a 201 can contain both SUCCEEDED and FAILED rows — check each result's status. Requires aivisibility.manage |
GET /explorations?limit=20 | Exploration history for the project, newest first. Summaries only (no answers): id, prompt, region, platforms, who ran it and when |
GET /explorations/{explorationId} | One exploration with every answer exactly as it was returned, including which platforms were served from cache. Re-executes nothing and re-pays nothing |
DELETE /explorations/{explorationId} | Removes an exploration from the project's history. Other explorations that copied one of its answers are unaffected — they hold their own copy. Requires aivisibility.manage |
GET /runs/{runId} | Single-run drill-down (answer, citations, mentions with sentiment / recommended / rank, and any paidPlacements the answer carried — each with adUrl (where the ad points), the advertiser's own advertiserUrl / advertiserFaviconUrl where the platform reports them, and isRendered: false means the platform served the ad but never displayed it, null means the platform does not report visibility) |
Shared filters
Every analytics read accepts the same filter set, so a selection made in one panel means the same thing in all of them. For each filter, omitting it (or sending it empty) means everything — there is no ALL sentinel:
| Filter | Parameter | Notes |
|---|---|---|
| Platform | provider | Repeat to select several |
| Topic | topicIds | Repeat to select several; the literal uncategorized matches runs with no topic |
| Region | regions | US / EU; only meaningful once the project runs more than one |
| Date window | preset, from, to | A preset (e.g. today, last_7_days) takes precedence over a from/to pair (YYYY-MM-DD). Boundaries are resolved in the project's reporting timezone, set in project settings — requests do not carry one |
When a date window is given, jobs that finished inside it are aggregated instead of the last-jobs window. The dashboard sends presets directly and uses explicit dates for custom ranges and period-over-period comparisons.
The project-wide answer list (GET /answers) is narrowed by nothing but its filters, so it takes the whole shared set on top of its own four.
Row-listing reads scoped by a path id — GET /jobs/{jobId}/runs, GET /prompts/{promptId}/runs, and the org-wide GET /ai-visibility/overview — are already narrowed by that id, so of the shared set they take regions (and, on the per-prompt history, provider). This matters once more than one region is live: a job's run list and a prompt's fixed-size recent history otherwise interleave every region with no way to separate them.
New analysis fields on the responses above are nullable — null means the job predates the analysis pass (or it was disabled), never zero.
Comparing against an earlier window
The reads listed below compare against an earlier window whenever they answer for a date range, and take the three comparison parameters shared with the rest of the analytics API — comparePreset (previous_period, previous_week, previous_month, previous_quarter, previous_year), or a custom compareFrom/compareTo pair. Compare to an earlier period covers what each preset means, the one-year guard, and how absoluteChange / percentageChange read; the rest of this section is what is specific to AI visibility.
A comparison needs a date window
Without preset or from/to, these reads aggregate the last jobs analyses — a count, not a period — and there is no "the N jobs before those" to compare against. This is the one case where a report comes back with no comparison at all. Asking for one anyway is refused with VAL_001 rather than answered without one, which would look like a report that honoured the request and simply found no change.
| Read | What a comparison adds |
|---|---|
GET /visibility | comparisonSeries — the same presence series over the earlier window's own jobs |
GET /provider-series | comparisonSeries, split by platform the same way points is |
GET /breakdown | Per platform: previousRunCount, previousRunsWithBrand, previousPresenceRate, previousCitationCount, previousMentionRate, previousCitationRate, previousAvgAnswerRank |
GET /competitors | Per brand: previousVisibilityRate, previousRank, previousShareOfVoice, previousCitationCount, previousAvgAnswerRank |
GET /share-of-voice | Per brand: previousRunsWithBrand, previousShareOfVoice, previousRank — one window-wide figure per brand, repeated on every point |
GET /topics-breakdown | Per topic: previousRunCount and previousPromptsWithRuns, with each brand's own previousVisibilityRate and previousShareOfVoice inside the row |
GET /prompts-breakdown | Per prompt × platform cell: previousRunCount, previousRunsWithBrand, previousPresenceRate, previousAvgAnswerRank |
GET /domains | Per domain: previousCitationCount and previousCitationShare |
GET /citations/pages | Per page: previousCitationCount and previousCitationShare |
Series reads (/visibility, /provider-series) return the earlier window as its own jobs, with their own ids and finish times — the two windows rarely run the same number of scans and no job here has a counterpart there, so line the two up by position rather than by date.
/domains, /citations/pages, /breakdown, /competitors and /prompts-breakdown also report comparisonJobsIncluded beside jobsIncluded. It is the field that tells the two zeros apart: 0 says the project ran no analysis in that window at all — which is why every previous figure is zero and every previous share is null — rather than having run and found nothing.
Rates and positions follow the convention the rest of the API uses. previousRank and previousAvgAnswerRank are null, never a number, when the brand was not measured then: reporting a missing brand as last place would read as a climb it never made, and 0 is the best answer position there is, not the absence of one. absoluteChange on a row ranked by a rate — presence, visibility, share of voice — is a percentage-point difference, so 0.4286 against 0.2857 reads as 14.3.
Full request/response schemas are in the API reference.
Next steps
- Bot & Crawler Detection — see which AI crawlers visit your site (a separate dataset).
- CLI — run
kitbase ai-visibilitycommands from the terminal or CI. - API reference — full request/response schemas.