Silver Risk Index
API v1

One number for the mood of the gold market, recompiled every 60 seconds from news in every language we can reach. Built for trading systems to poll directly.

Authentication

Create a key at /keys. Send it any of three ways:

curl -H "Authorization: Bearer gri_live_xxx" https://evandersignal.com/api/v1/index
curl -H "X-API-Key: gri_live_xxx"            https://evandersignal.com/api/v1/index
curl "https://evandersignal.com/api/v1/index?key=gri_live_xxx"

Default limit 120 requests/minute per key. The index only changes once a minute, so polling faster returns the same value — check age_seconds rather than hammering it.

Endpoints

GET

/api/v1/index

The current reading. This is the one most systems want.
{
  "index": 6.26,                    // 1 = sell, 5 = undecided, 10 = buy
  "verdict": "BUY MOOD",
  "raw": 0.129,                     // -1..+1 before the 1-10 mapping
  "coverage": 0.76,                 // 0..1 how much evidence is behind it
  "stories": 982,                   // weighted stories in the window
  "as_of": 1784528400,
  "as_of_iso": "2026-07-20T06:20:00Z",
  "age_seconds": 12
}
Use coverage. An index of 7 at 0.20 coverage is three headlines shouting; the same 7 at 0.80 is a genuine consensus. Size positions on both numbers, not just the index.
GET

/api/v1/outlook

Near-future read: where the index goes on today's stories alone, and the force currently arriving.
{
  "current": 6.26,
  "regime": "building_bullish",     // stable | building_* | fading_* | drifting
  "momentum": { "1h": 0.04, "6h": 0.21, "24h": -0.10 },
  "volatility_24h": 0.18,
  "pressure": {
    "last_hour_polarity": 0.31,     // net gold-pull of the newest stories
    "last_hour_stories": 47,
    "baseline_24h_polarity": 0.14,
    "divergence": 0.17              // positive = incoming news more bullish
  },                                //            than what's already priced
  "projection": [
    { "horizon_h": 1,  "if_no_new_news": 6.24, "low": 6.0, "high": 6.5 },
    { "horizon_h": 6,  "if_no_new_news": 6.11, "low": 5.6, "high": 6.7 },
    { "horizon_h": 12, "if_no_new_news": 5.98, "low": 5.3, "high": 6.7 }
  ],
  "emerging_channels": [ ... ]      // what's heating up right now
}
What if_no_new_news actually is. Every story's weight halves every 18 hours. So tomorrow's index is already partly determined by today's book — that path is computed exactly, not guessed. pressure.divergence is the leading part: fresh stories are barely represented in the current value yet, so their net polarity tells you which way the number is about to be pulled. Bands are realised index volatility scaled by √time.

This is a projection of the index, not of the gold price, and it is not investment advice.
GET

/api/v1/channels

The current reading broken into its transmission channels — which forces are pushing, and how hard.
{ "channels": [
  { "channel": "geopolitics", "label": "Geopolitical risk",
    "polarity": 0.45, "weight": 3.21, "stories": 260, "beta": 0.75,
    "note": "War, escalation, strikes, blockades, sanctions." }, ... ] }
GET

/api/v1/history

Minute-by-minute index history. ?hours=24 (max 2160 = 90 days).
{ "hours": 24, "count": 1440,
  "ticks": [ { "ts": 1784528400, "index": 6.26, "raw": 0.129,
               "coverage": 0.76, "stories": 982 }, ... ] }
GET

/api/v1/wire

The scored stories themselves — audit any reading back to its sources. ?limit=50 (max 200).
{ "count": 50, "items": [
  { "title": "...", "url": "...", "language": "zh",
    "channel": "monetary_policy", "polarity": -0.55, "intensity": 0.6,
    "confidence": 0.7, "scored_by": "lexicon", "rationale": "" }, ... ] }
GET

/api/v1/ping

Check a key works.

Errors

CodeMeaning
401Missing, unknown, or revoked key
429Over the key's per-minute limit
503No index computed yet (cold start)

Polling example

import requests, time

H = {"Authorization": "Bearer gri_live_xxx"}
URL = "https://evandersignal.com/api/v1"

while True:
    d = requests.get(f"{URL}/index", headers=H, timeout=10).json()
    # Only act on a reading with real evidence behind it.
    if d["coverage"] >= 0.5:
        if d["index"] >= 7.0:
            print("bullish gold mood", d["index"])
        elif d["index"] <= 3.0:
            print("bearish gold mood", d["index"])
    time.sleep(60)