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

# Citation share and volume over time

> Your daily owned citation share and the daily total number of citations, both from one API call.

Two daily lines come out of one call: how many links the engines gave in answers to your prompts (**volume**), and what share of them pointed to your site (**owned share**). Group the citations report by `date`, then sum per day.

> [!WARNING]
>
> Don't average the daily shares to get a period share. A day with 200 citations and a day with 20 do not weigh the same. Use [Owned citation share](/cookbook/owned-citation-share) for the period figure.

## How this example works

1. **`group_by: ["date"]`, `entity: "domain"`, no filter.** Each row is one domain on one day.
2. **Fetch every page.** Days × domains runs to hundreds of rows.
3. **In one pass, per date:**
   * volume = the sum of `citations` over all rows;
   * owned share = the sum of `citations` where `owner` is `own`, divided by that day's volume.
4. **Repeat for the previous window** if you want an overlay line.

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

const rr = new RankRaft();

const rows = await rr.reports
  .citations({
    site_id: process.env.RANKRAFT_SITE_ID,
    start_date: "2026-09-01",
    end_date: "2026-09-30",
    entity: "domain",
    group_by: ["date"],
    metrics: ["citations", "answers"],
    limit: 100,
  })
  .all();

const days = new Map(); // date -> { total, owned }
for (const r of rows) {
  const d = days.get(r.date) ?? { total: 0, owned: 0 };
  d.total += r.citations;
  if (r.owner === "own") d.owned += r.citations;
  days.set(r.date, d);
}

console.log("date,citations,owned_citations,owned_share");
for (const [date, d] of [...days].sort(([a], [b]) => a.localeCompare(b))) {
  console.log([date, d.total, d.owned, (d.owned / d.total).toFixed(4)].join(","));
}
```

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

with RankRaft() as rr:
    rows = rr.reports.citations(
        site_id=os.environ["RANKRAFT_SITE_ID"],
        start_date="2026-09-01",
        end_date="2026-09-30",
        entity="domain",
        group_by=["date"],
        metrics=["citations", "answers"],
        limit=100,
    ).all()

total, owned = defaultdict(int), defaultdict(int)
for r in rows:
    total[r["date"]] += r["citations"]
    if r["owner"] == "own":
        owned[r["date"]] += r["citations"]

print("date,citations,owned_citations,owned_share")
for d in sorted(total):
    print(f"{d},{total[d]},{owned[d]},{owned[d] / total[d]:.4f}")
```

```bash curl theme={"system"}
curl https://api.rankraft.ai/v1/reports/citations \
  -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",
       "entity":"domain","group_by":["date"],"metrics":["citations","answers"],"limit":100}'
```

## Reading the lines

* **Volume moves with how often the engines search**, not only with your prompts. Engines decide per question whether to look things up, and an answer from memory has no citations. A dip in volume with a steady share usually means fewer searches, not lost ground.
* **Days without citations** have no rows. Treat a missing day as "no links that day", not as zero share.
* **Per engine:** use `group_by: ["date", "platform"]` and key the sums on date and engine.
* `answers` on a row counts distinct answers citing that domain that day. Don't add it across domains: one answer citing three domains would count three times.

## Common follow-ups

> **[Owned citation share with change](/cookbook/owned-citation-share)**
>
> The period figure and its change.

> **[Citation rank by domain](/cookbook/citation-rank)**
>
> Which domains take the rest of the share.


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