API Documentation

Base URL: https://zenhodl.net

ZenHodl Weekly

Stay on top of live results and API changes.

One weekly email with live results, one model insight, and product updates.

Useful if you care about proof, endpoints, and what changed.

Quickstart

Get your first edge signal in 30 seconds. For background on the models and methodology, read our research paper. research paper.

1
Get your API key

Create a free account at /signup (no credit card required), or choose a paid plan at /pricing.

2
Fetch live edges
curl -H "X-API-Key: sk_live_YOUR_KEY" https://zenhodl.net/v1/edges
3
Use the edge

Each signal tells you: the model's fair probability, the market ask price, and the gap (edge) between them.

// Signal: NCAAMB, Purdue (away), fair_wp=0.74, market_ask=0.63, edge=+11c
// Action: BUY Purdue at 63c. Model says it's worth 74c. Hold to settlement.
Python quickstart
import requests

resp = requests.get(
    "https://zenhodl.net/v1/edges",
    headers={"X-API-Key": "sk_live_YOUR_KEY"},
    params={"min_edge": 8}
)
for signal in resp.json()["signals"]:
    print(f"{signal['sport']} {signal['team']}: "
          f"fair={signal['fair_wp']:.0%} ask={signal['market_ask']:.0%} "
          f"edge=+{signal['edge']*100:.1f}c ({signal['confidence']})")

Which Endpoint Should I Use?

I want the smallest live feed of tradable signals

Use /v1/edges. This is the fastest first integration for alerting, bots, and scanners.

I want the full live board with score, clock, and market state

Use /v1/games. This is the best endpoint for dashboards and game-level monitoring.

I want richer per-sport prediction objects

Use /v1/predict/{sport}/live or /v1/predict/{sport}/pregame. These are the cleanest prediction endpoints for model consumers.

I want push instead of polling

Use /v1/ws/stream for browser/app clients, or /v1/webhooks for server-to-server delivery.

I want saved personal automation

Use /v1/watchlists and /v1/preferences to save filters and alert settings tied to a user account.

Key Concepts

Fair Win Probability (fair_wp)

Our ML model's estimate of the true probability a team wins, based on score, time, Elo, and sport-specific features. Ranges from 0.0 to 1.0. Independent from market prices — this is what makes edge detection possible.

Market Ask (market_ask)

The current Polymarket ask price for the contract. This is what you'd pay to buy. Ranges from 0.0 to 1.0 (equivalent to 0c to 100c).

Edge

edge = fair_wp - market_ask. When positive, the model thinks the contract is underpriced. An edge of 0.11 means the model sees an 11-cent probability gap before fees and slippage. Not the same as expected profit — actual profit depends on execution costs (~2-3c) and model accuracy. Our default minimum threshold is 8c.

Settlement

Prediction market contracts resolve to $1.00 (win) or $0.00 (lose) after the game ends. Buy at 63c, win = +37c profit. Buy at 63c, lose = -63c. The edge is your statistical advantage over many trades.

Authentication

All authenticated endpoints require an API key. Pass it via the X-API-Key header:

Security Note

Always pass your API key via the X-API-Key header, not in URL parameters. URL parameters appear in browser history, server logs, and referrer headers.

# curl
curl -H "X-API-Key: sk_live_your_key_here" https://zenhodl.net/v1/games
# Python
import requests
headers = {"X-API-Key": "sk_live_your_key_here"}
games = requests.get("https://zenhodl.net/v1/games", headers=headers).json()
// JavaScript (fetch)
const resp = await fetch("https://zenhodl.net/v1/games", {
  headers: { "X-API-Key": "sk_live_your_key_here" }
});
const data = await resp.json();

Get your API key at /signup (free tier) or /pricing (paid plans). Keys start with sk_live_. Keep it secret — treat it like a password.

Rate Limits

Rate limits are per-minute. Every response includes these headers:

X-RateLimit-Limit: 300
X-RateLimit-Remaining: 297
X-RateLimit-Tier: pro
Tier Requests/min Monthly Cap WebSocket Sports Delay
Free 10500-All 75 min
Starter ($49) 6010,0001All 7Real-time
Pro ($149) 300100,0005All 7Real-time
Enterprise (Custom) 1,000Unlimited20All 7Real-time

Monthly usage is metered on authenticated REST endpoints. /v1/usage is excluded so you can always inspect your own usage without consuming quota. WebSocket connection limits are enforced separately from monthly REST usage.

Errors

All errors return JSON with a detail field:

{"detail": "Rate limit exceeded (300 req/min for pro tier)."}
CodeMeaningExample
401Missing or invalid API key"Invalid or inactive API key."
402Payment required"Payment not completed."
403Tier too low / dashboard-only key"Your plan includes dashboard access only."
404Resource not found"No predictions for 20260101."
429Rate limit exceeded"Rate limit exceeded (60 req/min for starter tier)."

GET /v1/health

System health check. No authentication required. Use this to verify the API is running and check how many games are active.

curl https://zenhodl.net/v1/health

Response:

{
  "status": "ok",
  "uptime_seconds": 3600.5,
  "active_games": 12,
  "sports_loaded": ["NBA", "NCAAMB", "NCAAWB", "CFB", "NFL", "NHL", "MLB"],
  "last_espn_poll": "2026-03-22T19:30:00Z",
  "last_ws_update": "2026-03-22T19:30:01Z"
}
FieldDescription
statusok, starting, or degraded
active_gamesNumber of live games being tracked right now
sports_loadedWhich sport models are loaded and active
last_espn_pollLast time ESPN scores were fetched (UTC)

GET /v1/games

All live games with ML fair win probabilities and market prices. This is the primary endpoint for getting a full view of every tracked game.

ParamTypeDescription
sportstringFilter by sport (NBA, NCAAMB, NCAAWB, CFB, NFL, NHL, MLB). Optional.
venuestringFilter venue_prices to a specific venue (polymarket, kalshi, draftkings, fanduel, betmgm). Optional.
curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/games?sport=NBA&venue=polymarket"
import requests

games = requests.get(
    "https://zenhodl.net/v1/games",
    headers={"X-API-Key": "sk_live_..."},
    params={"sport": "NBA"}
).json()

for g in games["games"]:
    edge = (g["home_fair_wp"] - (g["home_market_ask"] or 0)) * 100
    print(f"{g['event_title']} | {g['time_display']} | edge: {edge:+.1f}c")

Response:

{
  "timestamp": "2026-03-22T19:30:00Z",
  "count": 5,
  "games": [{
    "sport": "NBA",
    "game_id": "401810889",
    "event_title": "Knicks vs. Wizards",
    "home_team": "NY", "away_team": "WSH",
    "home_score": 78, "away_score": 65,
    "period": 3, "seconds_remaining": 420.0,
    "time_display": "Q3 7:00",
    "status": "live",
    "home_fair_wp": 0.82,
    "away_fair_wp": 0.18,
    "home_market_ask": 0.75,
    "away_market_ask": 0.27,
    "home_edge": 0.07,
    "away_edge": -0.09,
    "model_confidence": "high",
    "is_score_change": false,
    "last_updated": "2026-03-22T19:29:55Z"
  }]
}

Response fields

home_fair_wpModel's fair probability for home team (0.0–1.0)
home_market_askCurrent Polymarket ask price (0.0–1.0). null if no market match
home_edgefair_wp - market_ask. Positive = underpriced. 0.07 = 7 cents edge
model_confidencehigh (edge >12c), medium (6–12c), low (<6c)
is_score_changetrue if a score change happened in the last 30 seconds (prices may lag)
statuslive, scheduled, final
seconds_remainingTotal seconds left in regulation (e.g. 420 = 7 minutes)

GET /v1/edges

Current edge signals — only games where the model disagrees with the market by at least min_edge. This is the "actionable trades" endpoint.

ParamTypeDescription
sportstringFilter by sport. Optional.
min_edgefloatMinimum edge in cents. Default: 8.0
venuestringFilter by venue (polymarket, kalshi, draftkings, etc.). Optional.
curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/edges?sport=NCAAMB&min_edge=10"
edges = requests.get(
    "https://zenhodl.net/v1/edges",
    headers={"X-API-Key": "sk_live_..."},
    params={"sport": "NCAAMB", "min_edge": 10}
).json()

for s in edges["signals"]:
    print(f"BUY {s['team']} @ {s['market_ask']:.0%} "
          f"(fair: {s['fair_wp']:.0%}, edge: +{s['edge']*100:.1f}c)")

Response:

{
  "timestamp": "2026-03-22T19:30:00Z",
  "count": 1,
  "min_edge": 0.10,
  "signals": [{
    "sport": "NCAAMB",
    "game_id": "401810889",
    "event_title": "Purdue vs. Illinois",
    "team": "Purdue",
    "side": "away",
    "fair_wp": 0.74,
    "market_ask": 0.63,
    "edge": 0.11,
    "period": 2,
    "seconds_remaining": 480.0,
    "score": "52-44",
    "confidence": "high",
    "is_score_change": false,
    "timestamp": "2026-03-22T19:29:55Z"
  }]
}

Response fields

teamTeam name the signal is for
sidehome or away
fair_wpModel's fair probability (0.0–1.0)
market_askPolymarket ask price (0.0–1.0)
edgefair_wp − market_ask. 0.11 = 11-cent probability gap (before fees/slippage)
confidencehigh (≥12c), medium (6–12c), low (<6c)

GET /v1/sports

Available sports and model metadata. Useful for discovering what models are loaded and their backtest performance.

curl -H "X-API-Key: sk_live_..." https://zenhodl.net/v1/sports

Response:

{
  "sports": [{
    "sport": "NCAAMB",
    "model_type": "xgb_isotonic",
    "train_games": 15230,
    "elo_teams": 362,
    "features": ["score_diff", "seconds_remaining", "period",
                 "time_fraction", "elo_diff", "pregame_wp"],
    "backtest_wr": 0.738,
    "backtest_c_per_trade": 12.0
  }]
}

GET /v1/predictions/latest

Download today's pre-game fair probability predictions as CSV. Also available by date: /v1/predictions/{YYYYMMDD}

# Today's predictions
curl -H "X-API-Key: sk_live_..." https://zenhodl.net/v1/predictions/latest -o predictions.csv

# Specific date
curl -H "X-API-Key: sk_live_..." https://zenhodl.net/v1/predictions/20260322 -o predictions.csv

Sample CSV row:

sport,game_id,home_team,away_team,start_time,home_fair_wp,away_fair_wp,elo_diff,model_pick,confidence
NCAAMB,401810889,Illinois,Purdue,2026-03-22T19:00:00Z,0.426,0.574,-92,Purdue,high

CSV files include a license header with your email and download token. See data license.

POST /v1/backtest

Pro tier and above

Run a WP model backtest with custom strategy parameters against our 25M+ row dataset. Test different edge thresholds, periods, and fee assumptions.

FieldTypeDefaultDescription
sportstringNBASport to backtest (NBA, NCAAMB, CFB, NFL, etc.)
min_edge_cfloat8.0Minimum edge in cents to enter a trade
max_edge_cfloat50.0Maximum edge (filter out suspicious outliers)
min_fair_wp_cfloat65.0Only trade if model confidence ≥ this
max_entry_cfloat78.0Don't buy above this price
min_entry_cfloat35.0Don't buy below this price
min_periodint2Earliest game period to enter
max_per_gameint3Max entries per game (both sides combined)
seasonslistallFilter by season, e.g. ["2024-25"]
taker_fee_cfloat2.0Platform taker fee per trade (cents)
slippage_cfloat1.0Expected slippage per trade (cents)
curl -X POST -H "X-API-Key: sk_live_..." -H "Content-Type: application/json" \
  -d '{"sport":"NCAAMB","min_edge_c":10,"min_period":2,"seasons":["2025-26"]}' \
  https://zenhodl.net/v1/backtest
result = requests.post(
    "https://zenhodl.net/v1/backtest",
    headers={"X-API-Key": "sk_live_..."},
    json={"sport": "NCAAMB", "min_edge_c": 10, "min_period": 2}
).json()

s = result["summary"]
print(f"{s['total_trades']} trades, {s['win_rate']:.1%} WR, "
      f"+{s['c_per_trade_net']:.1f}c/trade net")

Response:

{
  "summary": {
    "total_trades": 854,
    "wins": 630,
    "losses": 224,
    "win_rate": 0.738,
    "gross_pnl_c": 12024.0,
    "net_pnl_c": 10280.0,
    "avg_edge_c": 11.8,
    "c_per_trade_net": 12.0,
    "max_drawdown_c": 340.0,
    "best_streak": 18,
    "worst_streak": 5
  },
  "by_edge": [
    {"bucket": "8-10c", "trades": 412, "win_rate": 0.71, "c_per_trade": 9.8},
    {"bucket": "10-12c", "trades": 198, "win_rate": 0.76, "c_per_trade": 14.2},
    {"bucket": "12-15c", "trades": 144, "win_rate": 0.79, "c_per_trade": 16.5}
  ],
  "by_period": [
    {"period": 1, "trades": 320, "win_rate": 0.72, "c_per_trade": 10.1},
    {"period": 2, "trades": 534, "win_rate": 0.75, "c_per_trade": 13.2}
  ],
  "sample_trades": [{"game_id": "...", "side": "away", "edge_c": 11.1, "entry_c": 63.0, "profit_c": 37.0, "won": true}]
}

GET /v1/backtest/sports

List available sports and seasons for backtesting.

curl -H "X-API-Key: sk_live_..." https://zenhodl.net/v1/backtest/sports

Response:

{
  "sports": {
    "NCAAMB": {"seasons": ["2023-24", "2024-25", "2025-26"], "files": 3, "model_available": true},
    "NBA": {"seasons": ["2023-24", "2024-25", "2025-26"], "files": 3, "model_available": true},
    "CFB": {"seasons": ["2023", "2024"], "files": 2, "model_available": true}
  }
}

WS /v1/ws/stream

Real-time WebSocket stream. Updates every 5 seconds via WebSocket push.

URL: wss://zenhodl.net/v1/ws/stream
Push interval: 5 seconds
Auth: session cookie or WebSocket subprotocol
Connection limits: Per tier (1/5/20)
const API_KEY = "sk_live_your_key_here";
const ws = new WebSocket(
  "wss://zenhodl.net/v1/ws/stream",
  ["zenhodl.v1", API_KEY]
);

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.type === "edge_alert") {
    const d = msg.data;
    console.log(`EDGE: ${d.sport} ${d.team} | `
      + `fair=${d.fair_wp}c ask=${d.market_ask}c edge=+${d.edge}c`);
  }

  if (msg.type === "game_update") {
    const d = msg.data;
    console.log(`${d.event_title} ${d.time_display} | `
      + `fair=${d.home_fair_wp} ask=${d.home_market_ask}`);
  }
};

ws.onclose = () => setTimeout(() => location.reload(), 5000); // auto-reconnect
import asyncio, websockets, json

API_KEY = "sk_live_your_key_here"

async def stream():
    uri = "wss://zenhodl.net/v1/ws/stream"
    async with websockets.connect(uri, subprotocols=["zenhodl.v1", API_KEY]) as ws:
        async for raw in ws:
            msg = json.loads(raw)
            if msg["type"] == "edge_alert":
                d = msg["data"]
                print(f"EDGE: {d['sport']} {d['team']} "
                      f"fair={d['fair_wp']}c ask={d['market_ask']}c "
                      f"edge=+{d['edge']}c ({d['confidence']})")

asyncio.run(stream())

Message types

game_update Sent for every live game on each tick
{"type": "game_update", "data": {
  "sport": "NBA", "game_id": "401810889", "event_title": "Knicks vs. Wizards",
  "home_team": "NY", "away_team": "WSH", "home_score": 78, "away_score": 65,
  "period": 3, "time_display": "Q3 7:00",
  "home_fair_wp": 0.82, "away_fair_wp": 0.18,
  "home_market_ask": 0.75, "away_market_ask": 0.27,
  "home_edge": 0.07, "away_edge": -0.09
}}
edge_alert Sent when edge exceeds threshold
{"type": "edge_alert", "data": {
  "sport": "NCAAMB", "team": "Purdue", "side": "away",
  "fair_wp": 74.1, "market_ask": 63.0, "edge": 11.1,
  "score": "52-44", "confidence": "high"
}}

GET /v1/predict/{sport}/live

All live games with win probabilities and multi-venue edges. Returns fair ML odds and per-venue pricing from Polymarket, Kalshi, and major sportsbooks.

curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/predict/NBA/live"
import requests

resp = requests.get(
    "https://zenhodl.net/v1/predict/NBA/live",
    headers={"X-API-Key": "sk_live_..."}
).json()

for g in resp["games"]:
    print(f"{g['home_team']} vs {g['away_team']}: "
          f"P(home)={g['home_win_prob']:.0%} ML={g['fair_home_ml']}")
    for venue, v in (g.get("venues") or {}).items():
        if v.get("home_edge") and v["home_edge"] > 0.05:
            print(f"  +EV at {venue}: edge={v['home_edge']:.1%}")
const resp = await fetch("https://zenhodl.net/v1/predict/NBA/live", {
  headers: { "X-API-Key": "sk_live_..." }
});
const { games } = await resp.json();
games.forEach(g => console.log(`${g.home_team} ${g.fair_home_ml} | ${g.away_team} ${g.fair_away_ml}`));

Response:

{
  "timestamp": "2026-03-28T22:00:00Z",
  "sport": "NBA",
  "count": 5,
  "games": [{
    "game_id": "401585432",
    "sport": "NBA",
    "home_team": "LAL", "away_team": "BOS",
    "home_score": 78, "away_score": 75,
    "period": 3, "clock": "Q3 8:42",
    "home_win_prob": 0.72,
    "away_win_prob": 0.28,
    "model_confidence": "high",
    "fair_home_ml": -257,
    "fair_away_ml": 215,
    "venues": {
      "Polymarket": {"home_ask": 0.62, "home_edge": 0.10},
      "Kalshi": {"home_ask": 0.63, "home_edge": 0.09},
      "DraftKings": {"home_ml": "-150", "home_implied": 0.60, "home_edge": 0.12},
      "FanDuel": {"home_ml": "-165", "home_implied": 0.623, "home_edge": 0.097}
    },
    "best_home_venue": "DraftKings"
  }]
}

GET /v1/predict/{sport}/pregame

Today's upcoming games with Elo-based pregame win probabilities. No live game state needed — uses Elo ratings only.

curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/predict/NBA/pregame"

Response:

{
  "sport": "NBA", "count": 8,
  "games": [{
    "game_id": "401810900",
    "home_team": "LAL", "away_team": "BOS",
    "home_win_prob": 0.42, "away_win_prob": 0.58,
    "fair_home_ml": 138, "fair_away_ml": -138,
    "status": "scheduled"
  }]
}

GET /v1/predict/{sport}/{game_id}

Single game detail with full model output, multi-venue edges, and game state.

curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/predict/NBA/401585432"

GET /v1/fair-lines/{sport}

Fair moneyline odds in American format for all live games. Designed for integration into odds comparison tools.

curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/fair-lines/NBA"

Response:

{
  "sport": "NBA", "count": 5,
  "lines": [{
    "game_id": "401585432",
    "home_team": "LAL", "away_team": "BOS",
    "home_win_prob": 0.72, "away_win_prob": 0.28,
    "fair_home_ml": -257, "fair_away_ml": 215,
    "clock": "Q3 8:42", "status": "live"
  }]
}

GET /v1/usage

Your current month's API usage. Track request counts against your monthly cap.

curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/usage"

Response:

{
  "month": "2026-03",
  "requests_used": 4521,
  "requests_cap": 10000,
  "by_endpoint": {
    "/v1/predict/NBA/live": 3200,
    "/v1/predict/NHL/live": 821,
    "/v1/fair-lines/NBA": 500
  },
  "tier": "starter",
  "resets_at": "2026-04-01T00:00:00+00:00"
}
Tier Monthly Cap Price
Free500$0
Starter10,000$49/mo
Pro100,000$149/mo
EnterpriseUnlimitedCustom

Model Quality & Analytics

Endpoints for evaluating model quality, tracking closing line value (CLV), and monitoring data venues.

GET /v1/model/performance

Model quality metrics for all trained sports: Brier score, ROC-AUC, ECE (calibration error), accuracy, and full uncertainty/conformal prediction tables.

ParamTypeDescription
sportstringFilter to a single sport. Optional.
curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/model/performance?sport=NBA"
{
  "sports": [{
    "sport": "NBA",
    "brier_score": 0.139,
    "roc_auc": 0.890,
    "ece": 0.106,
    "accuracy": 0.803,
    "model_type": "Split-Phase",
    "trained_date": "2026-03-24T15:52:54Z",
    "n_test_games": 90,
    "conformal_table": [{"tf_lo": 0.0, "tf_hi": 0.1, "width": 0.029, "n_samples": 35672}, ...]
  }]
}

GET /v1/model/clv

Live Closing Line Value tracking. CLV measures how often the model's entry price beats the final market price — the gold standard for edge validation. Starter+ tier.

ParamTypeDescription
sportstringFilter by sport. Optional.
daysintLookback window in days. Default: 7
curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/model/clv?sport=NBA&days=30"
{
  "total_signals": 142,
  "avg_clv_c": 3.8,
  "clv_beat_pct": 61.2,
  "by_sport": [{"sport": "NBA", "count": 42, "avg_clv_c": 4.1, "beat_pct": 64.3}],
  "by_edge_bucket": [{"bucket": "8-12c", "count": 85, "avg_clv_c": 2.1, "beat_pct": 55.3}]
}

GET /v1/venues

List all connected data venues with real-time status. Shows which exchanges and sportsbooks are currently providing price data.

curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/venues"
{
  "venues": [
    {"venue": "polymarket", "status": "connected", "connection_type": "websocket", "markets_active": 7770},
    {"venue": "kalshi", "status": "connected", "connection_type": "websocket"},
    {"venue": "draftkings", "status": "polling", "connection_type": "rest_polling"}
  ]
}

GET /v1/snapshots/{sport}/{date}

Intraday win probability snapshots archived every 30 seconds during live games. Use for backtesting entry timing, studying momentum, or building custom models. Pro+ tier.

ParamTypeDescription
sportpathSport code (NBA, NHL, etc.)
datepathDate in YYYYMMDD format
curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/snapshots/NBA/20260330"

GET /v1/predictions/batch

Bulk download predictions for a date range as CSV. Max 90 days per request. Pro+ tier.

ParamTypeDescription
startstringStart date (YYYYMMDD). Required.
endstringEnd date (YYYYMMDD). Required.
sportstringFilter by sport. Optional.
curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/predictions/batch?start=20260301&end=20260330&sport=NBA" -o batch.csv

Webhooks

Push notifications when edge signals are detected. Register a URL and receive a signed POST request in real-time — no polling needed. Starter+ tier.

POST /v1/webhooks

Register a new webhook. Limits: Starter=1, Pro=3, Enterprise=10.

curl -X POST -H "X-API-Key: sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://your-server.com/hook", "filters": {"sport": "NBA", "min_edge_c": 10}}' \
  "https://zenhodl.net/v1/webhooks"

Each webhook POST includes an X-Webhook-Signature header (HMAC-SHA256) for verification.

GET /v1/webhooks

List your active webhooks.

curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/webhooks"

DELETE /v1/webhooks/{webhook_id}

Deactivate a webhook.

curl -X DELETE -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/webhooks/abc123"

Watchlists & Preferences

Store user-specific game lists and notification settings. These endpoints are useful when you want durable scanner filters or alert automation tied to an account.

GET /v1/watchlists

List the current user's saved watchlists.

curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/watchlists"

POST /v1/watchlists

Create a saved watchlist with your own filters and thresholds.

curl -X POST -H "X-API-Key: sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name": "High Edge NBA", "sports": ["NBA"], "teams": [], "min_edge_c": 10, "max_entry_c": 78}' \
  "https://zenhodl.net/v1/watchlists"

GET /v1/preferences

Read the user's alert and notification preferences.

curl -H "X-API-Key: sk_live_..." "https://zenhodl.net/v1/preferences"

PUT /v1/preferences

Update user notification settings for dashboard alerts and watchlists.

curl -X PUT -H "X-API-Key: sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"daily_summary": true, "weekly_summary": true, "alert_threshold_c": 9, "favorite_sports": ["NBA", "NHL"], "timezone": "America/New_York"}' \
  "https://zenhodl.net/v1/preferences"

Confidence Intervals

All prediction endpoints now include confidence_interval — calibrated prediction bands from conformal prediction tables. Use these to assess uncertainty and size positions appropriately.

{
  "home_fair_wp": 0.72,
  "confidence_interval": {
    "lower": 0.68,
    "upper": 0.76,
    "width": 0.08
  }
}

Width narrows as games progress (more data = more certainty). Early game: 10-15c width. Late game: 3-5c width.

Free Samples

Two public sample endpoints — no API key needed. Signed-in free accounts get broader delayed access on the authenticated API.

GET /v1/predictions/sample.csv

Download a CSV with live edge signals + resolved trade examples showing actual P&L (wins and losses).

curl https://zenhodl.net/v1/predictions/sample.csv -o sample.csv

GET /v1/edges/sample

Top 3 edge signals as JSON from the public sample feed. Delayed 15 minutes, NBA + NHL only. Create a free account for the live dashboard plus delayed /v1/games and /v1/edges access across all 7 sports.

curl https://zenhodl.net/v1/edges/sample
{
  "count": 2,
  "delay_minutes": 15,
  "note": "Delayed 15 min. Upgrade for real-time across 7 sports.",
  "signals": [
    {"sport": "NBA", "team": "NY", "edge": 11.2, "fair_wp": 78.5, "confidence": "high"},
    {"sport": "NHL", "team": "BOS", "edge": 9.1, "fair_wp": 71.3, "confidence": "medium"}
  ]
}

Ready to get started?

Get your API key and start receiving real-time edge signals.