# Reproduce the figure

> Recompute any saving from your own data, with the queries and the API call that returns it.

You can check every saving LightHouse shows in three steps of increasing depth: read the figure with its method, recompute it from the per-lever read, and recompute it from your raw records.

All examples use a `viewer` key and your tenant’s service token, as in [Authentication](/api/authentication/).

## 1. The API call that returns the figure

`GET /api/v1/savings/headline?period=YYYY-MM` returns the month’s headline and every lever, each with its baseline, method, conservative choices, coverage and detail. Omit `period` for the current month.

**curl**

```bash
curl "https://$AAL_TENANT.atlanticagenticlabs.com/api/v1/savings/headline?period=2026-10" \
  -H "Authorization: Bearer $AAL_KEY" \
  -H "CF-Access-Client-Id: $CF_ACCESS_CLIENT_ID" \
  -H "CF-Access-Client-Secret: $CF_ACCESS_CLIENT_SECRET"
```

**Python**

```python
import os

import httpx

BASE = f"https://{os.environ['AAL_TENANT']}.atlanticagenticlabs.com/api/v1"
HEADERS = {
    "Authorization": f"Bearer {os.environ['AAL_KEY']}",
    "CF-Access-Client-Id": os.environ["CF_ACCESS_CLIENT_ID"],
    "CF-Access-Client-Secret": os.environ["CF_ACCESS_CLIENT_SECRET"],
}

doc = httpx.get(f"{BASE}/savings/headline", params={"period": "2026-10"}, headers=HEADERS).json()
print(doc["headline"]["sentence"])
for lever in doc["levers"].values():
    print(f'{lever["name"]:<50} {lever["status"]:<20} {lever["usd"]}  ({lever["coverage"]["sentence"]})')
```

**TypeScript**

```ts
const BASE = `https://${process.env.AAL_TENANT}.atlanticagenticlabs.com/api/v1`;
const HEADERS = {
  Authorization: `Bearer ${process.env.AAL_KEY}`,
  "CF-Access-Client-Id": process.env.CF_ACCESS_CLIENT_ID!,
  "CF-Access-Client-Secret": process.env.CF_ACCESS_CLIENT_SECRET!,
};

const doc = await (await fetch(`${BASE}/savings/headline?period=2026-10`, { headers: HEADERS })).json();
console.log(doc.headline.sentence);
for (const lever of Object.values<any>(doc.levers)) {
  console.log(lever.name, lever.status, lever.usd, lever.coverage.sentence);
}
```

What to look at in each lever:

- `status`: `ok` (counted), `included` (already inside another lever), `partial_comparison` (a figure, not counted), or why there is no number (`no_data`, `no_price_card`, `no_comparison`, `no_cost_feed`).
- `coverage`: how much of your estate reported what the lever needs. A saving on 40% coverage is a saving on 40% of your work.
- `detail`: the lever’s own working. For routing, `detail.source` is `decisions` or `counterfactual`.

The same document is in LightHouse at **Insights → Savings**, and through MCP as the `savings` tool with `view=headline`. See the [Savings Headline reference](/api/reference/savings-and-caching/#savings-headline).

## 2. Recompute a lever from its own read

Each lever has a read that carries the inputs to its formula.

| Lever                        | Read                               | What it carries                                                                                                                                                     |
| ---------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Retrieval                    | `GET /api/v1/retrieval`            | `stuffing`: `stuffed_tokens`, `sent_tokens`, `tokens_avoided`, `usd_uncached`, `usd_cached`, the `baseline` used, and the same by agent, pack, index, model and day |
| Model routing                | `GET /api/v1/routing`              | Every decision with its candidates and their costs; totals with `saving_usd`, `escalation_usd` and `off_saving_usd`                                                 |
| Prompt caching               | `GET /api/v1/caching`              | Tokens by kind and the price card, by model, agent, pack, deployment and provider                                                                                   |
| Infrastructure and ephemeral | `GET /api/v1/deployments/profiles` | Each deployment’s profile and its estimated always-on cost                                                                                                          |

Most take `format=csv` for a spreadsheet.

### Example: the caching lever, by hand

This recomputes the caching saving for each model from `GET /api/v1/caching` and compares it with LightHouse’s own figure. Run it with the `BASE` and `HEADERS` from step 1.

**Python**

```python
doc = httpx.get(f"{BASE}/caching", params={"since": "2026-10-01", "until": "2026-10-31"},
                headers=HEADERS).json()
M = 1_000_000
for row in doc["by_model"]:
    card = row["price"]
    if not card or card["cache_read_per_m"] is None:
        print(row["key"], "no cache-read price: saves nothing")
        continue
    inp = card["input_per_m"]
    read_rate = card["cache_read_per_m"]
    write_rate = card["cache_write_per_m"] if card["cache_write_per_m"] is not None else inp
    mine = (row["cache_read_tokens"] * (inp - read_rate)
            - row["cache_write_tokens"] * max(write_rate - inp, 0)) / M
    print(f'{row["key"]:<45} mine {mine:10.4f}   LightHouse {row["saved_usd"]:10.4f}')
```

**TypeScript**

```ts
const q = new URLSearchParams({ since: "2026-10-01", until: "2026-10-31" });
const cache = await (await fetch(`${BASE}/caching?${q}`, { headers: HEADERS })).json();
const M = 1_000_000;
for (const row of cache.by_model) {
  const card = row.price;
  if (!card || card.cache_read_per_m === null) { console.log(row.key, "no cache-read price"); continue; }
  const inp = card.input_per_m;
  const write = card.cache_write_per_m ?? inp;
  const mine = (row.cache_read_tokens * (inp - card.cache_read_per_m)
    - row.cache_write_tokens * Math.max(write - inp, 0)) / M;
  console.log(row.key, mine.toFixed(4), row.saved_usd.toFixed(4));
}
```

A model that more than one deployment calls on different price cards can differ slightly here, because LightHouse prices each deployment on its own card. Group by deployment (`format=csv&group_by=deployment`) to compare exactly.

## 3. Recompute from your raw records

For the deepest check, export your tenant (**Setup and admin → Data export**; see [Leaving](/data-sovereignty/leaving/#export)). `records.jsonl` holds every telemetry record LightHouse stores for you, one per line, with its `type`, `deployment`, `ts` and `attrs`. The manifest’s SHA-256 lets you show the file is the one LightHouse produced.

This recomputes retrieval’s tokens avoided for a month, by the formula on [How a saving is calculated](/cost/savings/#retrieval-against-context-stuffing):

```python
import json
from collections import defaultdict

DEFAULT_WINDOW = 200_000
records = [json.loads(line) for line in open("records.jsonl", encoding="utf-8")]
month = [r for r in records if r["ts"].startswith("2026-10")]

# The smallest context window among the priced models each pack called.
window_of = {}
for r in records:
    if r["type"] == "models":
        limits = r["attrs"].get("limits") or {}
        w = limits.get("context_tokens") or limits.get("context_window")
        if w:
            window_of[(r["deployment"], r["attrs"]["model"])] = w
called = defaultdict(set)
for r in month:
    if r["type"] == "model_call" and r["attrs"].get("outcome") != "refused":
        called[(r["deployment"], r["attrs"]["pack"])].add(r["attrs"]["model"])

def window(deployment, pack):
    known = [window_of[(deployment, m)] for m in called[(deployment, pack)] if (deployment, m) in window_of]
    return min(known) if known else DEFAULT_WINDOW

avoided = measured = unmeasured = 0
for r in month:
    if r["type"] != "retrieval":
        continue
    a = r["attrs"]
    corpus = a.get("corpus_tokens_in_scope")
    if corpus is None:
        unmeasured += 1          # a search that did not report its scope counts nothing
        continue
    measured += 1
    stuffed = min(corpus, window(r["deployment"], a["pack"]))
    weight = 1 / (a.get("sample_rate") or 1)
    avoided += max(stuffed - a["context_tokens_sent"], 0) * weight

print(f"tokens avoided: {avoided:,.0f} over {measured} searches ({unmeasured} unmeasured)")
```

Compare `tokens avoided` with `stuffing.tokens_avoided` from `GET /api/v1/retrieval` for the same dates. Multiply by your input price (or your cache-read price, if you cached that month) per million to get the dollar figure.

The script takes each model’s context window from any `models` record; LightHouse uses the deployment’s newest complete snapshot, which matters only if a window changed during the month.

Days are your tenant’s calendar days in its time zone in LightHouse’s reads; the script above uses UTC dates, so a search near midnight can fall in a different month. Set your tenant’s time zone to UTC, or filter by your own zone, for an exact match.

## Comparing with LightHouse’s figure

If your figure and LightHouse’s differ:

1. Check the **period and time zone** first: LightHouse buckets days in your tenant’s time zone.
2. Check **coverage**: searches or calls that reported nothing are counted by LightHouse as unmeasured and given no saving.
3. Check the **price card**: LightHouse uses each deployment’s newest complete snapshot, not today’s provider list price.
4. If they still differ, tell your certified forward-deployed engineer, with the period and the lever. A disagreement between two calculations is a bug in one of them, and we want to know which.

Source: https://docs.atlanticagenticlabs.com/cost/reproduce/
