> ## Documentation Index
> Fetch the complete documentation index at: https://docs.peec.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Filtering and dimensions

> How dimensions, filters, and having work on report endpoints, and the pitfalls to avoid.

## Overview

The report endpoints, [Get Brands Report](/api-reference/reports/get-brands-report), [Get Domains Report](/api-reference/reports/get-domains-report), and [Get URLs Report](/api-reference/reports/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`.

<Info>
  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.
</Info>

## `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.

<Info>
  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.
</Info>

## 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`.

<Warning>
  Filtering by `brand_id` in `filters` always reports `share_of_voice` as `1.0`. That number is meaningless.
</Warning>

To look at one brand while keeping a meaningful share of voice, put `brand_id` in `having` instead:

```json theme={null}
{
  "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.

<Warning>
  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.
</Warning>

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:

<CardGroup cols={2}>
  <Card title="Correct" icon="check">
    Request once without `tag_id`. Each chat is counted a single time.
  </Card>

  <Card title="Incorrect" icon="xmark">
    Request broken down by `tag_id`, then add the results together. Multi-tagged chats are counted once per tag.
  </Card>
</CardGroup>

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.

<Info>
  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.
</Info>
