# Calibration Ledger — trust a forecast, with evidence (beta) (`publicrecords/calibration-ledger`) Actor

Given a claim from an organization (IMF, OECD, Fed/FOMC, SPF, ECB SPF, or a listed issuer), returns that organization's track record: error distribution, intervals, bias, band coverage, worst misses with source links — or honest 'no coverage' (free). Organizations only, never people.

- **URL**: https://apify.com/publicrecords/calibration-ledger.md
- **Developed by:** [Public Records](https://apify.com/publicrecords) (community)
- **Categories:** AI, Developer tools, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 scores

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Calibration Ledger — who has actually been right?

Give it a claim from an institution — a forecast, a guidance range, a dated promise — and it answers with that institution's **record on claims like it**: how far off they've been, how often the outcome fell inside the band they gave, and the evidence for every number. Organizations only, never people. Nothing is guessed: when the record is too thin, the answer is *no coverage*, and that answer is free.

**Sources graded so far:** FOMC Summary of Economic Projections · Philadelphia Fed Survey of Professional Forecasters (six variables, reproduced exactly against the Fed's own published error tables) · OECD Economic Outlook (15 variables, 61 economies, 59 editions since 1996) · IMF World Economic Outlook · ECB Survey of Professional Forecasters · US corporate guidance from 8-K exhibits, resolved against first-print XBRL actuals · dated corporate promises from newswires.

**What you get with every answer:** sample size and distinct target periods, mean error, RMSE, empirical 70%/90% intervals with bootstrap confidence bounds, band hit rates, the source's own printed actuals versus independent vintages, and a claim id you can `verify` — it returns the SHA-256 of the source bytes and where the value sits in them.

**Modes:** `text` (paste a sentence), `lookup`, `score`, `compare`, `issuer`, `coverage` (free), `verify` (free). Works as an Actor run, via API, and in Standby for agents.

**Honesty rules baked in:** declared coverage on every response; intervals refused below 10 distinct targets; extreme-regime cells flagged; cross-institution disagreements flagged; no person is ever a source. Every source's attribution rides along with the answer. Underlying data is available free from the publishers; this Actor sells its own analysis.

**Example input (the prefill):** `{"mode": "lookup", "source": "oecd_eo", "series": "RGDP_GROWTH_A", "geo": "CAN", "target_period": "2027", "point": "1.8", "published_at": "2026-06-01"}`. That is one `lookup` event, the cheapest paid answer.

**Limits and data freshness:** the image bundles a snapshot of the published serving copy (`serving.sqlite`, plus `coverage.json` when present) taken at build time. A run answers from that snapshot first, so the first answer never waits on a download. A background etag check against the published copy then runs. Until it finishes, or if the hub is unreachable, answers come from the bundled snapshot. Every answer's `_provenance.ledger` names the etag and file time it was served from. `coverage` mode reads the bundled `coverage.json` when it sits beside the snapshot. A new snapshot ships with the next Actor build.

**Output:** one dataset item per run: the answer itself, and the same object is the `OUTPUT` record. That holds for the free statuses too: a `no_coverage`, `unparsed` or `insufficient_history` item carries the full answer (`reason`, `nearest_coverage`, attribution). The demand log for those answers is kept separately, in the run's `DEMAND` key-value record, not in the dataset. Fields of a `lookup` item:

| Field | Meaning |
|---|---|
| `status` | `ok` (a record was served, charged), or a free status: `no_coverage`, `unparsed`, `insufficient_history`, `error`, `charge_limit_reached` |
| `claim` | the claim as read: source, series, geo, target_period, point, published_at, horizon_days, horizon_bucket |
| `record` | the source's track record at that horizon: `n`, `n_targets`, `mean_error`, `mae`, `rmse`, error quantiles (`p05`…`p95`), `share_over`, `uncertainty` (bootstrap CIs), `window`, `vintage`, `rule`, `interval_reliable`, `cross_check_flags`, `largest_misses` |
| `interval_70`, `interval_90` | empirical intervals around the claimed point |
| `bias_adjusted_point` | the point corrected by the source's mean error |
| `mean_error_ci90`, `rmse_ci90` | 90% bootstrap bounds |
| `reading` | one plain-language sentence |
| `_provenance` | `ledger` (repo, file, etag, file time served from), `policy`, `mode`, `attribution`, `notice`, `billable_event`, and `charged` plus `charge_id` (present only when the charge succeeded) |

Example item (shortened) for the example input:

```json
{"status": "ok",
 "claim": {"source": "oecd_eo", "series": "RGDP_GROWTH_A", "geo": "CAN", "target_period": "2027", "point": 1.8,
           "published_at": "2026-06-01", "horizon_days": 578, "horizon_bucket": "15-24m"},
 "record": {"n": 26, "n_targets": 26, "mean_error": -0.728, "mae": 1.344, "rmse": 2.057, "window": ["1998", "2024"],
            "interval_reliable": true, "largest_misses": ["…"]},
 "interval_70": [0.06, 2.52], "interval_90": [-2.33, 3.53], "bias_adjusted_point": 1.07,
 "mean_error_ci90": [-1.396, -0.173], "rmse_ci90": [1.124, 2.833],
 "reading": "oecd_eo: 26 resolved RGDP_GROWTH_A claims over 26 distinct target periods for CAN at this horizon; …",
 "_provenance": {"mode": "lookup", "ledger": {"file": "serving.sqlite", "etag": "abe0caf7…"},
                 "attribution": ["Source: OECD Economic Outlook (© OECD, CC BY 4.0)."],
                 "billable_event": "lookup", "charged": "lookup", "charge_id": "<run id>-lookup-<epoch ms>-<hex6>"}}
```

**Pricing (pay per event, USD):** `lookup` $0.02 · `score` $0.02 · `text` $0.03 · `compare` $0.05 · `issuer` $0.05. At most one of these per answer, and only when `status` is `ok`. `coverage`, `verify`, errors, malformed input and every free status above are not charged. The pricing table also lists `actor-start` at $0.005; this code does not emit that event.

**Billing:** the paid event is charged *before* the item is written (charge first, serve second). `charged` appears on the item only when that charge succeeded. If your spend limit has no room, nothing is charged and a free `charge_limit_reached` item is written instead of the record. If the charge is refused or fails, the record is withheld, a free `error` item (`charge_refused` / `charge_failed`) is written, and the run's status message says `CHARGE_FAILED`. Every charge gets a `charge_id` (`<run id>-<event>-<epoch ms>-<hex6>`), logged as `CHARGE event=… charge_id=…` before the charge is sent. If the item cannot be written after a successful charge (each write is checked; after a successful charge the push is tried twice more; every retry is safe: a retry is blind only when the request never reached the server (connection refused, DNS failure, HTTP 429), otherwise the dataset is first checked for the item by its `charge_id` (item count as fallback) and a found item counts as served, and when that cannot be told nothing is retried, so a run never writes the record twice), the run fails with `DATASET_WRITE_FAILED_AFTER_CHARGE`; the log line, the run's status message and the `OUTPUT` failure item all carry that `charge_id` (the failure item also keeps the billed answer under `record`), so a billed-but-unserved answer can be found and refunded. If a free item cannot be written, the run fails with `DATASET_WRITE_FAILED`. If the serving copy is unavailable, you get a free `error` item naming the fault (`SERVING_*`) and the run exits cleanly. In Standby, the reply waits for the charge with no client-side timeout (a sent charge is never abandoned): a charge that lands, however slowly, serves the record; only a charge that is refused or fails withholds the record, and a free `charge_failed` error is returned instead.

# Actor input Schema

## `mode` (type: `string`):

text = hand it a sentence ("The IMF expects Canada's economy to grow 1.8% in 2027"). lookup/score/compare use structured fields.

## `claim_text` (type: `string`):

Free-text claim for text mode (e.g. "The IMF expects Canada's economy to grow 1.8% in 2027").

## `source` (type: `string`):

imf_weo | oecd_eo | fed_sep | spf | ecb_spf | pbo, or an issuer name; optional in text mode

## `series` (type: `string`):

Generated from rules.yaml — every series the ledger resolves.

## `geo` (type: `string`):

ISO3 country code, EA, or USA. Required for most score/lookup/compare calls.

## `target_period` (type: `string`):

Target period as YYYY, YYYYQn, or YYYY-MM when looking up a specific claim.

## `point` (type: `string`):

Numeric claimed value for lookup mode (string so decimals stay exact).

## `published_at` (type: `string`):

When the claim was published (YYYY-MM-DD), for lookup/verify.

## `horizon_bucket` (type: `string`):

Optional horizon bucket filter for score mode.

## `band_kind` (type: `string`):

For Fed SEP score mode: central_tendency or range.

## `claim_id` (type: `string`):

Ledger claim id for verify mode.

## `issuer` (type: `string`):

Organization name as it appears on its releases/filings (e.g. Salesforce Inc). Both records are returned: dated promises (newswires) and numeric guidance (EDGAR).

## Actor input object example

```json
{
  "mode": "lookup",
  "claim_text": "The IMF expects Canada's economy to grow 1.8% in 2027.",
  "source": "oecd_eo",
  "series": "RGDP_GROWTH_A",
  "geo": "CAN",
  "target_period": "2027",
  "point": "1.8",
  "published_at": "2026-06-01"
}
```

# Actor output Schema

## `answers` (type: `string`):

Dataset: one-shot answer item, or Standby demand-log items.

## `output` (type: `string`):

Key-value store OUTPUT record: same object as the dataset item.

## `records` (type: `string`):

All key-value store records of the run (OUTPUT, DEMAND, STAGE_MS, RUN_SUMMARY).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "lookup",
    "claim_text": "The IMF expects Canada's economy to grow 1.8% in 2027.",
    "source": "oecd_eo",
    "series": "RGDP_GROWTH_A",
    "geo": "CAN",
    "target_period": "2027",
    "point": "1.8",
    "published_at": "2026-06-01"
};

// Run the Actor and wait for it to finish
const run = await client.actor("publicrecords/calibration-ledger").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "lookup",
    "claim_text": "The IMF expects Canada's economy to grow 1.8% in 2027.",
    "source": "oecd_eo",
    "series": "RGDP_GROWTH_A",
    "geo": "CAN",
    "target_period": "2027",
    "point": "1.8",
    "published_at": "2026-06-01",
}

# Run the Actor and wait for it to finish
run = client.actor("publicrecords/calibration-ledger").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "lookup",
  "claim_text": "The IMF expects Canada'\''s economy to grow 1.8% in 2027.",
  "source": "oecd_eo",
  "series": "RGDP_GROWTH_A",
  "geo": "CAN",
  "target_period": "2027",
  "point": "1.8",
  "published_at": "2026-06-01"
}' |
apify call publicrecords/calibration-ledger --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,publicrecords/calibration-ledger"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/nff3bVHTmDFCfkTDA/builds/jWwRFC5OinxLoeZY9/openapi.json
