Skip to main content

Overview

The report endpoints, Get Brands Report, Get Domains Report, and Get URLs Report, all share three controls that shape what you get back:
  • dimensions decide how the report is broken down.
  • filters decide which underlying chats feed into the numbers (applied before aggregation).
  • having decide which aggregated results are returned (applied after aggregation).
Understanding the difference between these three is the key to getting metrics that mean what you think they mean.

Dimensions

Every report always breaks down by its own entity: the Brands report returns one result per brand, the Domains report one per domain, the URLs report one per URL. A report with no dimensions returns a single aggregated result per entity. Adding dimensions splits each entity’s result into finer slices. You get one result for each combination of the entity and the requested dimension values. For example, on the Brands report, requesting ["tag_id", "model_id"] returns one result per (brand × tag × model) combination, not one per (tag × model) pair. On the URLs report the same dimensions produce one result per (URL × tag × model), and so on. Available dimensions include model_id, model_channel_id, country_code, prompt_id, tag_id, topic_id, chat_id, date, week, and month.
Always break down by engine (model_channel_id) rather than only looking at the aggregate. An overall number can hide that you’re strong on one engine and invisible on another.

filters vs having

Both narrow a report, but they act at different stages and have very different effects on ratio metrics (share_of_voice, visibility, retrieval_rate, retrieved_percentage, citation_rate).

filters, applied before aggregation

filters decide which chats are counted at all. Because they run before the numbers are computed, they shrink both the numerator and the denominator of every ratio metric. Use filters when you want to genuinely restrict the scope of the analysis, for example, “only chats from ChatGPT” or “only chats in Germany.”

having, applied after aggregation

having decide which finished results come back. They run after every metric has already been computed, so they do not change any metric’s value. They only hide results you don’t want to see. Use having when you want to keep the full-population metrics but only display a subset of results.
Population fields (model_id, model_channel_id, country_code, prompt_id, tag_id, topic_id, chat_id) in having require the matching value in dimensions, otherwise the field isn’t part of the breakdown and the request is rejected.

Pitfall: collapsing share of voice with filters

share_of_voice is a brand’s mentions divided by the mentions of all in-scope brands. If you scope to a single brand using filters, you remove every other brand from the calculation before it runs, so the denominator becomes just that one brand, and share of voice collapses to 1.0.
Filtering by brand_id in filters always reports share_of_voice as 1.0. That number is meaningless.
To look at one brand while keeping a meaningful share of voice, put brand_id in having instead:
{
  "dimensions": ["model_channel_id"],
  "having": [
    { "field": "brand_id", "operator": "in", "values": ["kw_abc123"] }
  ]
}
This returns only that brand’s results, but its share of voice is still measured against every brand that appeared in the same scope, which is the value you actually want. The same principle applies to the Domains and URLs reports: filters shrink the denominators behind retrieval_rate and retrieved_percentage, while having selects results without touching them.

Pitfall: double-counting when you sum across tags yourself

A single prompt can carry multiple tags. (Topics are different: each prompt belongs to exactly one topic, so topics don’t overlap.) When you break a report down by tag_id, a chat whose prompt has three tags contributes to all three tag results. That’s exactly what you want when comparing tags against each other, since each tag’s number reflects everything labeled with it. The trouble starts when you take those per-tag results and add them up yourself to get a total. Because overlapping chats were counted under every tag they belong to, your hand-rolled total counts them multiple times and overshoots the truth.
Never sum tag-level results to get a project total. A chat tagged enterprise and q4-campaign is counted under both, so adding the two tag results double-counts it.
The fix is simple: let the report do the aggregation. Request the report without the tag_id dimension (or with no dimensions at all) and Peec aggregates over distinct chats, counting each one once:

Correct

Request once without tag_id. Each chat is counted a single time.

Incorrect

Request broken down by tag_id, then add the results together. Multi-tagged chats are counted once per tag.
This applies to every additive value (mention and visibility counts, retrieval counts) and to any ratio you might try to reconstruct from them. If you need both views, per-tag detail and a correct total, make two requests rather than deriving one from the other.
When you do need to combine results across non-overlapping dimensions (such as model_id or date), use the aggregation formulas documented on each report endpoint rather than naively averaging. Ratio metrics must be recombined from their underlying sums, not averaged.