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

# Headline and daily together

> Get a period's headline Visibility Score and its daily trend in two calls, and see why the daily rows don't average to the headline.

A visibility card shows two things: one number for the period, and a line of daily values under it. They come from two calls with the same range and filters. The only difference is `group_by: ["date"]` on the second.

The headline is **not** the average of the daily values, and should not be. Each daily row is computed from that day's answers; the headline is computed from every answer in the period at once.

## How this example works

1. **Headline:** no `group_by`. One row.
2. **Daily:** `group_by: ["date"]`. One row per UTC day with answers, dates ascending. Days with no answers have no row.
3. **Show both, and the gap between the average and the headline**, to see why you never derive one from the other.

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

const rr = new RankRaft();
const q = {
  site_id: process.env.RANKRAFT_SITE_ID,
  start_date: "2026-09-01",
  end_date: "2026-09-30",
  metrics: ["visibility_score", "prompt_coverage", "answers"],
};

const [headline, daily] = await Promise.all([
  rr.reports.visibility(q),
  rr.reports.visibility({ ...q, group_by: ["date"], limit: 100 }), // 92 days at most, so one page
]);

const h = headline.data[0];
console.log(`Headline: ${h.visibility_score} (coverage ${(h.prompt_coverage * 100).toFixed(0)}%, ${h.answers} answers)`);

for (const d of daily.data) {
  console.log(`  ${d.date}  ${String(d.visibility_score).padStart(3)}  coverage ${(d.prompt_coverage * 100).toFixed(0)}%  (${d.answers} answers)`);
}

const avg = daily.data.reduce((s, d) => s + d.visibility_score, 0) / daily.data.length;
console.log(`Average of daily scores: ${avg.toFixed(1)}  — not the headline, and not a substitute for it`);
```

```python Python theme={"system"}
import os
from rankraft import RankRaft

q = dict(
    site_id=os.environ["RANKRAFT_SITE_ID"],
    start_date="2026-09-01",
    end_date="2026-09-30",
    metrics=["visibility_score", "prompt_coverage", "answers"],
)

with RankRaft() as rr:
    headline = rr.reports.visibility(**q)
    daily = rr.reports.visibility(**q, group_by=["date"], limit=100)  # 92 days at most, so one page

h = headline.data[0]
print(f"Headline: {h['visibility_score']} (coverage {h['prompt_coverage']:.0%}, {h['answers']} answers)")

for d in daily:
    print(f"  {d['date']}  {d['visibility_score']:>3}  coverage {d['prompt_coverage']:.0%}  ({d['answers']} answers)")

avg = sum(d["visibility_score"] for d in daily) / len(daily.data)
print(f"Average of daily scores: {avg:.1f}  — not the headline, and not a substitute for it")
```

```bash curl theme={"system"}
# Headline
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","prompt_coverage","answers"]}'

# Daily
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",
       "group_by":["date"],"limit":100,"metrics":["visibility_score","prompt_coverage","answers"]}'
```

## Why the two differ

| Reason | Effect |
| - | - |
| **Prompt coverage is cumulative.** A prompt named on any day of the period counts as covered for the period. | The headline's coverage is usually higher than any single day's, so the headline score is usually higher than the daily average. |
| **Days have different numbers of answers.** A day where a scan only partly completed, or Google showed few AI Overviews, has fewer. | An average gives a 5-answer day the same weight as a 40-answer day. The headline weights each answer once. |
| **Rounding.** Each daily rate is rounded to whole points before its score is computed. | Rounding errors add up across 30 averaged values. |

The same is true of weeks and months: a monthly score is one call over the month, not the mean of its weeks. See [Visibility over time](/cookbook/visibility-over-time) for weekly and monthly lines.

## Common follow-ups

> **[Visibility Score with change](/cookbook/visibility-score)**
>
> Add the change against the previous period.

> **[Visibility over time](/cookbook/visibility-over-time)**
>
> Weekly and monthly buckets.


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