# Account data tools

These tools cover the **property-scoped** surface of the MCP server: your own private data, one brand at a time. Every one on this page is **read-only** except `add_pitch_list_publishers`, which adds publishers to a pitch list, and all of them are gated by your membership: the server checks that your signed-in account can access the property (or organization) before returning anything.

Almost everything here starts from a **propertyId**, so the first call in any account session is `list_properties`. (The account surfaces that aren't property-scoped are [bulk placement scoring](/docs/mcp/scoring#bulk-placement-scoring-for-an-organization), which starts from an `organizationId` and writes, and organization-level [pitch lists](#pitch-lists).)

Many of these tools return **numeric series over time**. Nothing is charted server-side — the assistant builds any visualization from the returned data. Just ask for "a chart of share of voice over the last 90 days" and it will.

## Discover your properties

### `list_properties`

Lists the properties (brands / sites) on your account, each with its organization and your role. Use a returned `id` as the `propertyId` for every other tool on this page.

| Parameter | Type | Notes |
|---|---|---|
| `organizationId` | string (optional) | Restrict to one of your organizations. |
| `includeProspects` | boolean (optional) | Include bulk-imported prospect properties (default `false`). |

## Projects

Projects are bundled, time-bound tracking efforts (an SEO push, a PR campaign). See the [Projects dashboard](/docs/dashboards/projects) for the concept.

### `list_projects`

Lists a property's projects with each one's type, status, date range, goals, and counts. A returned id is also the `projectId` argument on `get_metrics_history`, `get_competitor_share_of_voice`, `get_answer_drilldown` and `get_answer_summaries`, which scopes any of them to that project's prompts.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `status` | `ACTIVE` \| `COMPLETED` \| `ARCHIVED` (optional) | Filter by status. |

### `get_project_insights`

A project's deep insights, matching its exported report: metric **deltas** from start to now (consistency, share of voice, mentions, owned citations, owned citation rate, and cumulative goal citations), a **weekly trend** series (including weekly goal hits), goals with their first-hit details plus **hit counts and influence rates** (the share of the project's citations each tracked publisher/article/author/coverage group accounts for), the annotation timeline, key insights, and next steps.

| Parameter | Type | Notes |
|---|---|---|
| `projectId` | string | From `list_projects`. |

<Callout>
Project-level deltas and weekly trends come from `get_project_insights`. Property-wide history (not scoped to a project) comes from `get_metrics_history` below.
</Callout>

## The four shared filters

Four tools take the same slice arguments, and they mean the same thing in each: `stage` (`awareness` / `consideration` / `decision`), `platform`, `tag` (a prompt tag), `projectId`, plus `locationPropertyId` for one tracked market. The tools are [`get_metrics_history`](#get_metrics_history) (trend), [`get_competitor_share_of_voice`](#get_competitor_share_of_voice) (ranking), [`get_answer_drilldown`](#get_answer_drilldown) (one day's prompts and answers) and [`get_answer_summaries`](#get_answer_summaries) (one prompt's wording week by week). So "share of answers on Perplexity, decision stage, for the pricing tag" is one question you can follow from the trend line down to the raw answer.

Resolve the values first: tags from `list_prompt_tags`, project ids from `list_projects`, location ids from `list_locations`. An id that belongs to another property is **rejected**, not silently ignored, because a filter that matches nothing returns an empty slice that reads like "nothing happened".

<Callout>
Every filter slices the **numerator and the denominator together**, so a sliced share is a real share of that slice. Two rules follow. Tag slices **overlap** (one prompt can carry several tags), so per-tag numbers do not sum to the property total. And an **empty slice is empty**: no answers landed in it. It is never a reason to call again without the filters and present those numbers as the answer to the sliced question.
</Callout>

## Historical trends

### `get_metrics_history`

A property's AI visibility metrics over time, one point per report or prompt-tracking run: share of voice, mentions, citations, citation rate, and per-platform breakdowns.

Pass `stage` and/or `platform` to add a **`slice`** to every point: the stored stage-scoped cell for that combination, or the consideration + decision headline when only a platform is given. A `slice` of `null` is a day whose stored row carries no cell for that combination: unknown for the slice, never zero, and never the brand-wide total standing in for it.

Pass a `tag` to get a **theme-scoped** series instead: one point per UTC day, each with per-stage mentions, opportunities and share of voice for that tag, plus the day's **headline** (which narrows to your stage when you pass one). `stage` and `platform` combine with it.

Pass a `projectId` to scope the whole discovery series to that project's prompts (recomputed live from the source answers, the same way the dashboard's project filter does it) and to get the grounding-search **ranking** trend for the project's tracked queries alongside. That recompute is brand-wide, so `projectId` and `locationPropertyId` cannot be combined; ask for one or the other. Project-level deltas and weekly trends come from `get_project_insights`.

<Callout>
Everything in `discovery` is the **focal subject's** number. A CATEGORY property tracks a market with no focal brand, so this series measures a subject that does not exist and sits at zero from end to end. The payload's `focalSubject` field says so explicitly. For the market's own trend use `get_competitor_share_of_voice` with `includeSeries: true`; for the prompts and answers behind one day use `get_answer_drilldown`.
</Callout>

<Callout>
An **empty tag slice means no tagged data existed on those days**, not that brand activity was zero. A tag added last week has nothing before then; read that as "no data", never as a drop.
</Callout>

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `projectId` | string (optional) | Scope the discovery series to this project's prompts and add its grounding-search trends. Cannot be combined with `locationPropertyId`. |
| `tag` | string (optional) | Return the tag-scoped daily series instead of the brand-wide one. Tags come from `list_prompt_tags`. |
| `stage` | `awareness` \| `consideration` \| `decision` (optional) | Narrow every point to one funnel stage. Works with and without `tag`. |
| `platform` | string (optional) | `all`, `chatgpt`, `gemini`, `perplexity`, `claude`, or `googleAiOverview`. Works with and without `tag`. |
| `locationPropertyId` | string (optional) | Scope to one LOCATION child of this property. Ids come from `list_locations`; anything else is rejected. |
| `startDate` / `endDate` | string (optional) | ISO date bounds. |
| `limit` | number (optional) | Max points when no date range is given (default 30, max 100). |

### `list_prompt_tags`

The **tag vocabulary** for a property: the free-form tags on its tracked prompts, plus their categories and use-cases. Call this before slicing anything by theme: a tag that does not appear here returns an empty slice. The same tags are the `tag` argument on `get_metrics_history`, `get_competitor_share_of_voice`, `get_answer_drilldown` and `get_answer_summaries`.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |

### `get_answer_drilldown`

The **L3 drill-down** behind the visibility dashboard. For one UTC day it returns the **raw prompt text** of every tracked prompt that ran in scope, and for each prompt every platform execution: which model answered, the tracked brands that answer named (`brandsNamed`), the focal brand's mention flag and rank, and its citation counts. Totals reconcile exactly with the same day's point from `get_metrics_history` for the same scope. This is how you answer "*why* did share of voice move on this date?".

<Callout>
**Check `hasFocalSubject` first.** When it is `false` the property tracks a market with no focal brand, so `totals.mentions`, every execution's `mentioned` and every `rank` are computed against nothing. Narrating "0 mentions" there is simply wrong. The finding is `brandTotals`: each tracked brand's `answersNamedIn`, `promptsNamedIn` and `shareOfAnswers` over exactly this slice, ranked. Those shares do not sum to 100, because one answer can name several brands.
</Callout>

`brandName` puts one brand under a **lens**, not a filter: no prompt is removed, every execution gains `namedFocusedBrand` (`null` means that execution produced no answer), and `focusedBrand` summarizes it, with `tracked: false` when the name matches none of the property's tracked brands. "Which answers named Roborock" is only answerable beside the ones that did not.

**Raw answer text**: pass a `queryId` from this tool's own `prompts` list and the response gains an `answers` block: that prompt's answers for the day, one entry per platform, each with the full text (capped at 6,000 characters, with `answerTextTruncated` saying when it was cut), the brands it named, and up to 25 citations. `get_answer_summaries` returns one prompt's answers **uncapped** and week by week instead.

If the day has no completed run (or falls outside the tracked range) the call reports that rather than returning zeros; pick a date that appears in `get_metrics_history`.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `date` | string | The UTC day to explain, `YYYY-MM-DD`. |
| `stage` | `awareness` \| `consideration` \| `decision` (optional) | Restrict to one funnel stage (default: all three). |
| `platform` | string (optional) | `all`, `chatgpt`, `gemini`, `perplexity`, `claude`, or `googleAiOverview`. |
| `tag` | string (optional) | Only prompts carrying this tag. |
| `locationPropertyId` | string (optional) | Scope to one LOCATION child. Ids come from `list_locations`; anything else is rejected. |
| `projectId` | string (optional) | Only prompts assigned to this project. Ids come from `list_projects`; anything else is rejected. |
| `brandName` | string (optional) | Put one tracked brand under a lens. Never removes prompts. |
| `queryId` | string (optional) | Also return that one prompt's full answer text for the day. |

### `get_site_consultations`

How often AI platforms consulted **one specific site directly** — a `site:acme.com …` grounding search — instead of searching the open web, split into your own site, tracked competitors, and third parties, for a window and the window immediately before it.

A scoped search means the model had already decided where the answer lives. Consulting **your** site is an authority signal; competitors consulted while you get none is the absence of one. See the [Brand Dashboard](/docs/dashboards/brand-dashboard#ai-consultations) for the concept and the in-app drill-down.

<Callout>
The payload has **two halves on different bases, and they are not meant to reconcile.** The headline counters — `current`, `previous`, `delta`, `consultRate`, `byPlatform`, `byFunnelStage` — **exclude** `brand_identity` and `brand_comparison` prompts, because a prompt that names the brand is expected to send the model to its site and counting it would be circular. The `domains` evidence rows **include** them, so a domain row's `count` can exceed what the headline counters imply.
</Callout>

`consultRate` is normalized **per execution, not per search**: `consultedExecutions / totalExecutions`, the share of answers that searched at all in which the model consulted your site. One answer can fan out into a dozen searches, so a chatty platform cannot inflate it.

Each `domains` row carries the bare hostname, its class (`own` / `competitor` / `third_party`), the competitor it belongs to where relevant, the consultation count, the platforms that consulted it, when it was last seen, and `residualQuery` — the topic searched, with the `site:` operator stripped (empty for a bare `site:acme.com`, which reads as "just go read their site").

A `third_party` row with **`brandAffine: true`** is a **suspected wrong domain**: one that looks like the brand's but isn't. That is the signal the [wrong-domain alerts](/docs/dashboards/brand-dashboard#ai-consultations) fire on. Treat it as a lead — the domain may be real and holding authority that should be yours, or it may serve nothing at all — never as a settled fact.

The `domains` list is capped at the **50 most-consulted**; `domainsTotal` gives the full count so a truncated list is never mistaken for the whole inventory.

This tool counts only searches carrying the `site:` operator. A search that consults a brand by **naming** it (`CompetitorX pricing`) is a different thing and does not appear here — it has no scoped domain to attribute. Those searches are still excluded from SERP checks and gap opportunities everywhere else, on the grounds that nothing you publish can rank for a question about a competitor's product; see [`list_tracked_fanouts`](/docs/mcp/citation-optimizer#list_tracked_fanouts--start-here).

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `days` | number (optional) | Current window length in days (default 7, max 90). The previous window is the same length immediately before it — the pair `delta` compares. |
| `locationPropertyId` | string (optional) | Scope to one LOCATION child of this property. Ids come from `list_locations`; anything else is rejected. |

<Callout>
Good in a **weekly summary or digest** alongside `get_metrics_history` — pass `days: 7` and narrate `current` against `previous` using `delta`.
</Callout>

### `get_consistency_history`

A property's **brand-consistency** score over time (overall and per platform) — how consistently AI platforms describe the brand vs. its source-of-truth identity.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `startDate` / `endDate` | string (optional) | ISO date bounds. |
| `limit` | number (optional) | Max points (default 30, max 100). |

## Locations and competitors

### `list_locations`

Per-market AI share of voice for a brand's tracked **locations** (its LOCATION child properties). Each entry carries the market's city / region / country and its latest measurement — share of voice, mentions, citations, citation rate, and the measurement date — alongside a rollup.

`averageSov` is **equal-weighted** across the markets that have data: a "typical market" number that treats a small market and a large one alike. It deliberately will not match the brand-wide share of voice from `get_metrics_history`, which pools every answer. Feed a returned `id` back as `locationPropertyId` to scope `get_metrics_history`, `get_competitor_share_of_voice`, `get_answer_drilldown` or `get_answer_summaries` to that one market; an id that is not this property's own location is rejected. A brand with no tracked locations returns an empty list, not an error.

Each market also carries `leader` — the brand AI named most often there in that same measurement, as a `name` and a `shareOfVoice` — or `null` when the market has no competitor data yet. For a CATEGORY parent, which tracks a market with no focal brand and therefore has no share of voice of its own, `leader` is the only ranking the row carries.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | The **parent** property id, from `list_properties`. |

### `get_competitor_share_of_voice`

The ranked **share of answers** for the brands AI names, over a window you choose and any slice of it. For a CATEGORY property the same tool ranks the market's tracked brands, which the product calls **Brands** rather than competitors.

The ranking is **pooled over the window**: each brand's share is the answers that named it divided by the answers analyzed across every measured day in the window. It is not read off the latest measurement, because a single night is a handful of answers per brand and reorders on noise. The default window is 7 days; pass `windowDays: 30` or `90` for a steadier order.

<Callout>
`shareOfVoice` is a **share of answers** and it does **not sum to 100** across brands, because one answer can name several of them. `answersNamedIn` is a brand's numerator; the snapshot's `answersAnalyzed` is the denominator every row shares. `shareOfAnswersDelta` compares this slice with the window of equal length immediately before it, in percentage points, and is `null` (never `0`) when that earlier window held no answers in the slice.
</Callout>

Each brand also carries `totalCitations`, `queriesAppeared` and `gapQueriesCount`, the prompts where the **brand appeared and the focal subject did not**, which is the acquisition gap worth acting on. Those three are **not slice-scoped**: they come from the latest whole measurement and ignore every filter, so label them as whole-measurement numbers or leave them out of a filtered answer.

**How to read the rest of the payload:**

- `empty: true`: the slice was measured and held no answers. Report the empty slice.
- `thin: true`: fewer than 20 answers landed in it, so the order is unstable. Widen the window before ranking anything on it.
- `basis: "latest_row"`: the window carries no stage-scoped history, so **no filter could be honored**. The figures are the latest single measurement as stored, `answersAnalyzed` is `0` and `series` is empty. Say that rather than presenting them as a filtered answer.
- `daysWithDimension` below `daysMeasured` under a platform or tag filter: the remaining days never recorded that detail. Quote `dimensionStartDate` ("platform and tag detail starts on that date") instead of a share that silently undercounts them.
- `brandShareOfAnswers` / `brandAnswersNamedIn` / `brandShareOfAnswersDelta` are the focal subject's side of the **same slice**. Top-level `brandShareOfVoice` is the unsliced latest-measurement figure, so the two are not comparable.
- `headline` pools the **consideration and decision** stages of the same window (narrowed to your stage when you pass one), which is the definition the brand dashboard's share-of-voice hero uses. It is `null` for properties whose data predates stage scoping; never mix it with the top-level figures inside one ranking.

<Callout>
For a **CATEGORY** property every focal-subject figure is `null` by design: `brandShareOfVoice`, `brandShareOfAnswers`, `brandAnswersNamedIn`, `brandPreviousShareOfAnswers` and `brandShareOfAnswersDelta`. A zero would read as "this market is invisible" rather than "there is no subject here". Each brand's `gapQueriesCount` then equals its `queriesAppeared`, which is correct: every prompt a brand appeared in is one the absent subject did not.
</Callout>

`includeSeries: true` adds `series`, the per-day line for the slice's top 6 brands, in table order, for charting. A point with `hasDimension: false` is a day that recorded no platform or tag detail: break the line there, never plot it as `0`. The series is omitted by default to keep an ordinary call small.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `windowDays` | `7` \| `30` \| `90` (optional) | Days to pool the ranking over (default 7). |
| `stage` | `awareness` \| `consideration` \| `decision` (optional) | One funnel stage (default: all three pooled). Also narrows `headline`. |
| `platform` | string (optional) | `chatgpt`, `gemini`, `perplexity`, `claude`, or `googleAiOverview`. |
| `tag` | string (optional) | One prompt tag, from `list_prompt_tags`. |
| `projectId` | string (optional) | Restrict to one project's prompts, recomputed live. Ids come from `list_projects`; anything else is rejected. |
| `locationPropertyId` | string (optional) | Rank inside one tracked market. Ids come from `list_locations`; anything else is rejected. |
| `includeSeries` | boolean (optional) | Add the per-day series for the slice's top brands (default `false`). |

### `get_recommendation_categories`

How AI recommends your brand and every other brand it names, read from your **daily tracking** answers only. AI Visibility reports are not included. For each answer to a discovery prompt, Spyglasses classifies every brand the answer names, tracked or not, into one of four categories:

- **Top choice**: the answer singles the brand out as its pick or its explicit top recommendation.
- **One of many**: the answer recommends the brand alongside others, often with a note on who it is best for.
- **Generic mention**: the answer names the brand without evaluating or recommending it.
- **Cautioned against**: the answer steers this buyer away from the brand, usually for a stated reason such as price or complexity.

Each brand in each answer also comes with the caveats the answer gives (a reason type, a short reason and the audience it applies to) and a short quote from the answer.

<Callout>
The payload has two families of numbers, and they answer different questions. **Rates** in `summary` are per answer, for your brand: `topChoice` is the percent of answers in the slice that name you as AI's pick, and answers that never name you are in the denominator (`notMentioned`). **Shares** in `leaderboard` are across every brand AI named, including brands you don't track: `sharePct` is a brand's recommendations (top choice plus one of many, with a top choice counted once) out of all brands' recommendations, so the column adds up to 100 with the `Other` row. `cautionedRatePct` in the leaderboard is a per-answer rate, not a share.
</Callout>

**How to read the rest of the payload:**

- `hasAssessedAnswers: false`: daily tracking has not produced classified answers yet. Say it is not set up, not that the brand scored zero.
- An answer count of `0` is an empty slice. `isEarlyRead: true` means fewer than 30 answers, so treat the rates as directional.
- `summary.previous` and `overview.previous` hold the window of the same length just before this one. Each is `null` when your history does not reach back a full window. When history reaches back but nothing ran in that window (a new project, for example), `previous` reports `0` answers. In both cases the deltas (`deltaPoints`) are `null`, never `0`, so there is no change to report.
- `reasons` lists why AI steers buyers away (cautioned-against answers only), grouped by reason type, with the audience each reason applies to. `isNew` marks a reason that did not appear in the previous window.
- `bestFor` lists who AI says you are best for when it recommends you.
- `citedNotRecommended` counts answers that cite one of your own pages but leave you out of the recommendations.
- `unclassified` counts answers where a tracked brand was named but not rated. It is rare and is not a verdict.
- `isTracked` is worked out when you ask, so a brand you start tracking today shows its full history right away.

<Callout>
A **CATEGORY** property tracks a market with no focal brand, so `summary` and `bestFor` are `null` by design and `reasons` covers every brand. Rank the leaderboard instead.
</Callout>

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `windowDays` | `7` \| `30` \| `90` (optional) | Days of daily tracking to read (default 7). Changes compare with the window of the same length just before it. |
| `stage` | `awareness` \| `consideration` \| `decision` (optional) | One funnel stage (default: all three). |
| `platform` | string (optional) | `chatgpt`, `gemini`, `perplexity`, `claude`, or `googleAiOverview`. |
| `tag` | string (optional) | One prompt tag, from `list_prompt_tags`. |
| `projectId` | string (optional) | Restrict to one project's prompts. Ids come from `list_projects`; anything else is rejected. |
| `locationPropertyId` | string (optional) | One tracked market. Ids come from `list_locations`; anything else is rejected. |
| `personaId` | string (optional) | Read the answers asked as this persona instead of the baseline. Ids come from `list_personas`; anything else is rejected. Persona answers run on the parent property only. |
| `topN` | number (optional) | Brands listed before the rest fold into `Other` (default 8). Your own brand is always listed. |
| `includeTrend` | boolean (optional) | Add your brand's top-choice and cautioned rates by day or by week (default `false`). |

## Personas

Every discovery prompt on a project with personas also runs daily as each persona: the same question, with the persona's one-line description in front ("I'm the CFO at a 120-person B2B SaaS company in the US; we run NetSuite and HubSpot…"). Persona answers run on your AI platforms except Google AI Overviews, which is a search query with no asker. They never change your headline metrics: every other tool reads the baseline answers, asked with no persona.

### `list_personas`

A property's buyer personas. Each comes with a role-first label (for example "CFO, early-stage startup"), its fields (role group, seniority, decision role, segment, company size, industry, region, tech stack, priorities and constraints), `contextText` (the exact description sent in front of each question), its `version` (it goes up when that text changes) and the projects it is on. Only personas on an active project run.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `includeArchived` | boolean (optional) | Include archived personas (default `false`). |

### `get_persona_lens`

How AI answers each buyer compared with the **baseline**: the same prompts and platforms asked with no persona, over the same window. Each row is one persona, or a group of personas that share a role group, segment, company size or seniority. For each row and each lens brand (your brand, your tracked competitors and the most-named untracked brands) you get:

- the persona's rates in percent (mention, top choice, recommended, cautioned against) and the baseline's;
- `deltaPoints`, the difference, with `deltaIntervalPoints` (its 90% interval) and `clear`, true when the difference is larger than its interval;
- `inBuyerStack`, true for a brand the persona says it already uses.

Each row also has `whoAiRecommends`: each brand's share of the recommendations AI gave this buyer (untracked brands included, adding up to 100 with `Other`), next to the same brand's baseline share.

<Callout>
Persona samples are small. A row marked `thin` has fewer answers than `thinThreshold`, so treat it as directional, and only call a difference real when `clear` is `true`. `pending` means the persona is on a project but its first answers have not arrived yet. Answers often repeat the tools a buyer already uses; `inBuyerStack` marks those brands so a restated tool is not read as a recommendation.
</Callout>

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `windowDays` | `7` \| `30` \| `90` (optional) | Days of daily tracking to read (default 30). |
| `platform` | string (optional) | `chatgpt`, `gemini`, `perplexity` or `claude`. |
| `projectId` | string (optional) | One project's prompts and personas. Ids come from `list_projects`; anything else is rejected. |
| `groupBy` | `persona` \| `roleGroup` \| `segment` \| `companySize` \| `seniority` (optional) | One row per persona (default), or one per shared attribute. |
| `includeArchived` | boolean (optional) | Include archived personas (default `false`). |

## Brand & comparison prompts

Prompts typed `brand_identity` ("What is [brand]?") or `brand_comparison` ("[brand] vs [competitor]") always mention the brand, so they are **excluded from share of voice** and measured by these two tools instead.

### `get_brand_prompt_health`

Per-prompt health metrics for brand/comparison prompts, one row per prompt per day: **expected-message coverage** (% of the prompt's "should appear" messages found in answers), **unwanted-message incidence** (% of answers containing a "should not appear" message — lower is better), and **theme drift**.

Drift is 0–1 against the prompt's trailing 28-day theme pool: 0 means today's answers cover the same themes as the recent past; 1 means an entirely new story. Each row includes the day's `themes`, the `novelThemes` driving the drift (themes with no close match in the pool), and a `messageBreakdown` showing each expected message's per-day match count and a sample matched sentence — so you can say *which* messages appeared and *what* shifted, not just the aggregate numbers.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `queryId` | string (optional) | Restrict to one prompt. |
| `startDate` / `endDate` | string (optional) | ISO date bounds. |
| `limit` | number (optional) | Max rows when no date range is given (default 60, max 200). |

### `get_comparison_verdicts`

Aggregated head-to-head outcomes from `brand_comparison` prompts: per compared brand and day, how often answers **favor** it, what they favor it **for** (`topFavoredFor` — "agencies", "multi-language teams"), the verdict shapes (`clear_winner`, `segmented_winners`, `no_verdict`, `list_only`), the latest framing summary from an answer that compared that brand, and frequency-ranked **strengths/weaknesses** the answers attribute to it. `isOurBrand` marks the property's own brand.

Each row also carries a derived `favoredRate` (0–100, one decimal) — `favoredCount / totalComparisons` for that row.

Read both counts carefully. Favored counts are **not zero-sum**: a segmented answer ("X for agencies, Y for in-house teams") favors several brands at once, so per-brand favored counts can each exceed half the total and the rates across brands can sum past 100. And `totalComparisons` is the **day's** count of comparison answers, shared by every brand row for that day — it is not that brand's own sample size.

`brand` is **alias-aware**: pass a tracked competitor's name and rows recorded under any of its aliases are included too, because stored rows key on whatever name the *answer* used. A name that matches no tracked competitor is still accepted as a raw compared-brand name.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `brand` | string (optional) | Restrict to one compared brand (case-insensitive; matches a tracked competitor's name or any alias, or a raw compared-brand name). |
| `projectId` | string (optional) | Re-aggregate over this project's comparison prompts only, instead of all of the property's. |
| `startDate` / `endDate` | string (optional) | ISO date bounds. |
| `limit` | number (optional) | Max rows returned (default 100, max 400) — applied to date-ranged reads too. |

## Message tracking and drift

These two pair together: one shows *how often* your key messages get pulled through, the other shows *how the wording changes*.

### `get_message_tracking`

How often each tracked key message is pulled through into AI answers over time — a mention rate per message, plus first/latest values and the change. Good for a per-message trend chart.

Filter by `tags` and/or `sentiment`. Tags plus `sentiment: NEGATIVE` is the **crisis-monitoring** cut: the messages you never want AI to repeat, and whether it is repeating them — a rising rate there is bad news, the inverse of every other metric on this page.

<Callout>
Key-message tags are a **separate namespace** from prompt tags. They live on the messages, not on the prompts, and teams usually mirror the names by convention — but a tag from `list_prompt_tags` may not exist here, and vice versa.
</Callout>

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `tags` | string[] (optional) | Only messages carrying at least one of these key-message tags (e.g. `["crisis"]`). |
| `sentiment` | `POSITIVE` \| `NEGATIVE` \| `NEUTRAL` (optional) | Only messages with this polarity. `NEGATIVE` = "should not appear" messages. |
| `startDate` / `endDate` | string (optional) | ISO date bounds. |

### `get_answer_summaries`

The **full answer text** AI platforms gave for one tracked query, bucketed by week (one representative answer per week per platform). Compare weeks to narrate how the AI's description of the brand shifts. Omit `query` to first list the property's tracked queries, then call again with the one you want.

`tag`, `stage`, `projectId` and `locationPropertyId` narrow the **candidate prompts** before anything is read, so the list you pick from is already the slice and the filters are never applied to a truncated result. `stage` maps each prompt's type to its funnel stage the same way every other tool does, which puts `brand_identity` prompts in awareness and `brand_comparison` prompts in decision.

Answer text is returned **in full** here, with no cap. `get_answer_drilldown` returns a capped version of the same day's answers alongside the prompts that produced them.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `query` | string (optional) | A tracked query (exact or substring). Omit to list available queries. |
| `platform` | string (optional) | `openai`, `claude`, `gemini`, `perplexity`, or `google_ai_overview`. |
| `tag` | string (optional) | Only prompts carrying this tag, from `list_prompt_tags`. |
| `stage` | `awareness` \| `consideration` \| `decision` (optional) | Only prompts whose type maps to this funnel stage. |
| `projectId` | string (optional) | Only prompts assigned to this project. Ids come from `list_projects`; anything else is rejected. |
| `locationPropertyId` | string (optional) | Only answers from one LOCATION child's runs. Ids come from `list_locations`; anything else is rejected. |
| `maxWeeks` | number (optional) | Most-recent weeks to return (default 12, max 52). |
| `startDate` / `endDate` | string (optional) | ISO date bounds. |

See the [Key Messages dashboard](/docs/dashboards/key-messages) for the concept behind both.

## Citation intelligence

### `get_citation_intelligence`

A property's citation intelligence: the **mix of sources** AI cites, broken down by media type, authority type, content format, and page type; per-platform stats; a **historical trend** (citation mix over time); and the top citation sources by owner (brand / competitor / third-party). Use the `historicalData` series to chart how the mix shifts.

The summary also includes the **cited-vs-evaluated split** (`citationTypeBreakdown` — sources referenced in the answer text vs merely fetched while composing it) and **earned-media rollups** (`earnedMedia` — third-party sources whose fetched page content mentions the brand, including *hidden earned media* that never surfaces in answer-text attribution). Each top source carries `contentMentions`: brands found in its pages' content, distinct from answer-text `brandMentions`.

For "what would this filter give me?" questions, `facetCounts` holds per-dimension value counts computed with that dimension's own filter lifted (so media type, authority type, content format and platform compose instead of zeroing each other out), and `facetTotals` holds the same tallies with all four lifted.

`facetPublisherCounts` holds the same facet-excluded tallies counted in distinct publishers instead of citations, and `facetPublisherDistinct` gives the distinct publisher total for each dimension (one publisher can appear under several values, so the values can add up to more). Each top source also carries `platformCitations`, its citations split by platform, and `summary.answerCount` is the number of distinct AI answers behind `totalCitations`.

For a category property (a tracked market with no focal brand) nothing is "our brand": `earnedMedia` is always zero, no source has `mentionsOurBrand`, and `contentMentions` only lists the tracked brands.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `platform` | string (optional) | Filter by platform, or `all`. |
| `contentFormat` | string (optional) | |
| `mediaType` | string (optional) | |
| `authorityType` | string (optional) | |
| `brandAttributed` | string (optional) | A brand name, `_any_brand`, or `_unattributed` — matches **answer-text attribution**. |
| `citationType` | string (optional) | `cited_inline`, `evaluated_source`, `search_result`, or `unclassified`. |
| `mentionedBrand` | string (optional) | A brand name, `_our_brand`, or `_any_competitor` — matches **fetched page content**, catching earned media the answer never attributes. |
| `startDate` / `endDate` | string (optional) | ISO date bounds. |

See the [Citation Intelligence dashboard](/docs/dashboards/citation-intelligence) for how these breakdowns are defined.

### `get_coverage_citations`

For a **coverage group** (a list of earned-media placement URLs), the AI citations its URLs earned from the property's tracked prompts and reports. Answers "which prompt and platform cited my placement, and in what context?"

Returns a **funnel** (coverage URLs → cited by AI → cited inline), a **cited-vs-uncited comparison** (average AIPQS, word count, content age, byline share, top authors), per-URL rollups, and the individual **citation hits** — each with the prompt text, platform, model, date, citation type (`cited_inline` = referenced in the answer text, a true citation; `evaluated_source` = fetched but not referenced), and, for inline citations, the **answer-text context** around the reference.

Call **without `groupId`** to first list the property's coverage groups, then call again with the one you want.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string | From `list_properties`. |
| `groupId` | string (optional) | A coverage group id. Omit to list available groups. |
| `url` | string (optional) | Filter hits to one coverage URL (any variant — normalized). |
| `citationType` | string (optional) | `cited_inline` (referenced in the answer — a true citation), `evaluated_source` (the AI read the page but didn't reference it), `search_result` (appeared in the AI's web search but wasn't picked up), `unclassified`, or `all`. |
| `platform` | string (optional) | `openai`, `claude`, `gemini`, `perplexity`, or `google_ai_overview`. |
| `startDate` / `endDate` | string (optional) | ISO date bounds on the citing answer's date. |
| `limit` | number (optional) | Max citation hits to return (default 50, max 200). |

Counts here are **scoped to your property's own tracked prompts and reports** — the same numbers as the coverage group's citations page in the dashboard.

### `get_project_goal_citations`

For a **project goal**, the citations behind its hit count — with each citation classified by **brand relevance**. Publisher (and author / page-group) goals match on the *source*, so their counts include citations unrelated to the brand; this tool separates them:

- `attributed` — the answer credited the brand for this source by name, **or** (on Google AI Overviews) the overview mentions the brand while this source is in its list. AIO doesn't credit sources individually, so those hits carry `attributedViaAio: true` — treat them as strong-but-circumstantial
- `content_mention` — the cited page's fetched **content** mentions the brand, but the answer never credited it (*hidden earned media*; includes the mention context from the page)
- `answer_mention` — the **answer** mentions the brand somewhere, just not attached to this citation
- `unrelated` — the source matched on its own authority, and the answer never mentions the brand

Hits are scoped to the **project window**, so totals match the project page's goal counts exactly. Returns a breakdown (total / attributed / content mentions / hidden earned media / answer mentions / cited inline / unrelated) plus per-citation prompt, platform, model, date, citation type, and answer context. Call **without `goalId`** to list the project's goals with their citation counts, then call again with the one you want.

| Parameter | Type | Notes |
|---|---|---|
| `projectId` | string | From `list_projects`. |
| `goalId` | string (optional) | A goal id. Omit to list the project's goals. |
| `relevance` | string (optional) | `attributed`, `content_mention`, `answer_mention`, `unrelated`, `brand` (attributed or content mention), `hidden` (content mention but not cited inline), or `all`. |
| `citationType` | string (optional) | `cited_inline`, `evaluated_source`, `search_result`, `unclassified`, or `all`. |
| `platform` | string (optional) | `openai`, `claude`, `gemini`, `perplexity`, or `google_ai_overview`. |
| `startDate` / `endDate` | string (optional) | ISO date bounds on the citing answer's date. |
| `limit` | number (optional) | Max citation hits to return (default 50, max 200). |

## Pitch lists

A [pitch list](/docs/dashboards/pitch-lists) is a named list of publishers a PR team plans to pitch. A property list belongs to one brand; an organization list spans many, and each of its rows can name a brand or none. These three tools read pitch lists and add to them. `add_pitch_list_publishers` is the one tool on this page that **writes**.

### `list_pitch_lists`

Lists the pitch lists on a property, or the organization-level lists on an organization. Pass one of the two ids. Use a returned `id` as the `listId` for the other two tools.

| Parameter | Type | Notes |
|---|---|---|
| `propertyId` | string (optional) | From `list_properties`. Returns that property's pitch lists. |
| `organizationId` | string (optional) | From `list_my_organizations`. Returns the organization-level pitch lists. |

### `get_pitch_list`

One pitch list with its rows. Each row carries the publisher's domain and name, Est. Traffic, Domain Rank, status, note and, on organization lists, its brand; the AI citation counts for the window, split by platform; and a **Placed suggestion** when a scored placement for the brand exists on a publisher whose row is still Target or Pitched. The suggestion is only a suggestion: the status changes only when someone changes it.

Citation counts come from tracked brands' prompts and reports. On an organization list, a row with a tracked brand counts that brand's citations, a row with no brand counts citations across every tracked brand in the organization, and a row with a scoring-only brand has no count (it reads *Not tracked* in the app). Domain Rank is DataForSEO's third-party authority score and is **not** an input to AIPVS.

| Parameter | Type | Notes |
|---|---|---|
| `listId` | string | From `list_pitch_lists`. |
| `window` | number (optional) | Citation window in days: `30`, `90`, or `365`. Defaults to `90`. |
| `includeAipvs` | boolean (optional) | Also return each publisher's AI Placement Value Score and tier, scored in the row's brand context (without brand context for rows that have no brand). |
| `limit` | number (optional) | Max rows to return, in list order (default 200, max 500). The status and citation counts always cover the whole list. |

### `add_pitch_list_publishers`

**Writes.** Adds publisher domains to a pitch list in your account. Name the list one of two ways:

- `listId` adds to an existing list.
- `propertyId` or `organizationId` plus `name` adds to the list with that name in that scope, and creates the list first if there isn't one.

Each domain (or URL, trimmed to its domain) becomes a row with the status Target. Domains already on the list are skipped, and values that aren't a domain are reported back rather than failing the call. A domain Spyglasses hasn't seen before gets a publisher record and is queued for enrichment, so its traffic and logo fill in shortly afterward and its Domain Rank within a few hours. Rows added to an organization list this way carry no brand. The call **spends no credits**.

| Parameter | Type | Notes |
|---|---|---|
| `listId` | string (optional) | An existing list, from `list_pitch_lists`. Use this, or a scope plus `name`. |
| `propertyId` | string (optional) | With `name`: add to that property's list of that name. |
| `organizationId` | string (optional) | With `name`: add to that organization's list of that name. |
| `name` | string (optional) | The list name, used with `propertyId` or `organizationId`. |
| `domains` | string[] | Publisher domains or URLs to add, up to 200 per call. |
| `note` | string (optional) | A note applied to every row this call adds. It shows on the list, on the shared page and in exports. |

## A typical flow

<Steps>

<Step>
`list_properties` → copy the `propertyId` you want to work with.
</Step>

<Step>
For trends: `get_metrics_history` / `get_consistency_history`, then ask for charts.
</Step>

<Step>
For messaging: `get_message_tracking`, then `get_answer_summaries` on the most strategic query.
</Step>

<Step>
For a specific effort: `list_projects` → `get_project_insights`.
</Step>

<Step>
For one slice: `list_prompt_tags` → `get_competitor_share_of_voice` with the filters → `get_answer_drilldown` on a date from its series, with the same filters and a `brandName` lens.
</Step>

<Step>
For outreach: `list_pitch_lists` → `get_pitch_list` with `includeAipvs: true`, then `add_pitch_list_publishers` for the outlets worth adding.
</Step>

</Steps>

Or run the **Analyze a project**, **Who does AI name here?**, **Track message drift**, or **Citation mix over time** [prompts](/docs/mcp/prompts).

## Related

- [Projects dashboard](/docs/dashboards/projects) · [Key Messages dashboard](/docs/dashboards/key-messages) · [Citation Intelligence dashboard](/docs/dashboards/citation-intelligence) · [Pitch Lists](/docs/dashboards/pitch-lists)
- [Scoring tools](/docs/mcp/scoring) — evaluate publishers and placements for a property
