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

# Visibility Score with change

> This period's Visibility Score for your brand and its change against the previous period of the same length.

The headline on the Overview is your Visibility Score for the range and its change against the period before. That is two calls to the visibility report: one for the current window, one for the previous window of the same length.

## How this example works

1. **No `group_by`.** One row for the whole window, computed from every answer in it.
2. **`brands: "owned"`** (the default) returns only your brand.
3. **Run it for both windows.** The previous window ends the day before the current one starts and has the same number of days.
4. **Subtract.** The score's change is in points; a rate's change is in percentage points.

```js JavaScript theme={"system"}
import { RankRaft } from "rankraft-js";

const rr = new RankRaft();
const site_id = process.env.RANKRAFT_SITE_ID;

const START = "2026-09-01";
const END = "2026-09-30";

const DAY = 86_400_000;
const iso = (t) => new Date(t).toISOString().slice(0, 10);
function previous(start, end) {
  const s = Date.parse(`${start}T00:00:00Z`);
  const days = (Date.parse(`${end}T00:00:00Z`) - s) / DAY + 1;
  return [iso(s - days * DAY), iso(s - DAY)];
}

const metrics = ["visibility_score", "mention_rate", "citation_rate", "prompt_coverage", "answers"];
async function headline(start_date, end_date) {
  const page = await rr.reports.visibility({ site_id, start_date, end_date, metrics });
  return page.data[0] ?? null; // null when no answers were collected
}

const [prevStart, prevEnd] = previous(START, END);
const [now, before] = await Promise.all([headline(START, END), headline(prevStart, prevEnd)]);

if (!now) throw new Error("No answers in this range yet.");
const pp = (a, b) => `${((a - b) * 100 >= 0 ? "+" : "")}${((a - b) * 100).toFixed(0)} pp`;

console.log(`${now.brand.name}: ${now.visibility_score}` +
  (before ? ` (${now.visibility_score - before.visibility_score >= 0 ? "+" : ""}${now.visibility_score - before.visibility_score} vs ${prevStart}–${prevEnd})` : " (no earlier data)"));
if (before) {
  console.log(`  mentioned  ${(now.mention_rate * 100).toFixed(0)}%  ${pp(now.mention_rate, before.mention_rate)}`);
  console.log(`  cited      ${(now.citation_rate * 100).toFixed(0)}%  ${pp(now.citation_rate, before.citation_rate)}`);
  console.log(`  coverage   ${(now.prompt_coverage * 100).toFixed(0)}%  ${pp(now.prompt_coverage, before.prompt_coverage)}`);
}
console.log(`  from ${now.answers} answers`);
```

```python Python theme={"system"}
import os
from datetime import date, timedelta
from rankraft import RankRaft

SITE_ID = os.environ["RANKRAFT_SITE_ID"]
START, END = date(2026, 9, 1), date(2026, 9, 30)
METRICS = ["visibility_score", "mention_rate", "citation_rate", "prompt_coverage", "answers"]

def previous(start, end):
    days = (end - start).days + 1
    return start - timedelta(days=days), start - timedelta(days=1)

def headline(rr, start, end):
    page = rr.reports.visibility(site_id=SITE_ID, start_date=start, end_date=end, metrics=METRICS)
    return page.data[0] if page.data else None  # None when no answers were collected

prev_start, prev_end = previous(START, END)
with RankRaft() as rr:
    now = headline(rr, START, END)
    before = headline(rr, prev_start, prev_end)

if now is None:
    raise SystemExit("No answers in this range yet.")

if before:
    print(f"{now['brand']['name']}: {now['visibility_score']} "
          f"({now['visibility_score'] - before['visibility_score']:+d} vs {prev_start}–{prev_end})")
    for label, key in [("mentioned", "mention_rate"), ("cited", "citation_rate"), ("coverage", "prompt_coverage")]:
        print(f"  {label:<10} {now[key]:.0%}  {(now[key] - before[key]) * 100:+.0f} pp")
else:
    print(f"{now['brand']['name']}: {now['visibility_score']} (no earlier data)")
print(f"  from {now['answers']} answers")
```

```bash curl theme={"system"}
# Current window
curl https://api.rankraft.ai/v1/reports/visibility \
  -H "X-API-Key: $RANKRAFT_API_KEY" -H "Content-Type: application/json" \
  -d '{"site_id":"'$RANKRAFT_SITE_ID'","start_date":"2026-09-01","end_date":"2026-09-30",
       "metrics":["visibility_score","mention_rate","citation_rate","prompt_coverage","answers"]}'

# Previous window: the same 30 days, ending the day before
curl https://api.rankraft.ai/v1/reports/visibility \
  -H "X-API-Key: $RANKRAFT_API_KEY" -H "Content-Type: application/json" \
  -d '{"site_id":"'$RANKRAFT_SITE_ID'","start_date":"2026-08-02","end_date":"2026-08-31",
       "metrics":["visibility_score","mention_rate","citation_rate","prompt_coverage","answers"]}'
```

## Available metrics

| Metric | Scale | Better |
| - | - | - |
| `visibility_score` | 0–100 | higher |
| `mention_rate` | 0–1 | higher |
| `citation_rate` | 0–1 | higher |
| `prompt_coverage` | 0–1 | higher |
| `average_position` | 1 = first, `null` if never named | lower |
| `share_of_voice` | 0–1 | higher |
| `answers` | count | — |

Definitions: [How metrics are calculated](/cookbook/how-metrics-are-calculated).

> [!TIP]
>
> A change of a few points over a small number of answers is often noise. Print `answers` beside every change, and compare windows of at least a week.

## Common follow-ups

> **[Headline and daily together](/cookbook/headline-and-daily)**
>
> Add the daily trend under the headline.

> **[Segment by engine](/cookbook/segment-visibility)**
>
> See which engine moved.

> **[Competitor leaderboard](/cookbook/competitor-leaderboard)**
>
> Did a competitor move too?

> **[Only some engines or prompts](/api-reference/filtering)**
>
> Add a filter to both calls.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.