Skip to main content
Polling the full events endpoint every few seconds is expensive, burns data points, and can trip your per-second throttle if you scale it across many sports or books. This guide covers how to keep data fresh without paying for or requesting far more than you need.

Why Polling Efficiency Matters

TheRundown usage is not just about request count. The cost of a polling loop depends on:
  • how many sportsbooks you request
  • how many markets you include
  • whether you pull full event payloads or only deltas
  • how often you refresh
A naive loop that fetches full event snapshots every few seconds can waste both data points and burst budget. Delta endpoints solve this by returning only what changed since your last request.

Shrink Every Response First

Before you tune polling intervals, make each response smaller.
curl -H "X-TheRundown-Key: YOUR_API_KEY" \
  "https://therundown.io/api/v2/sports/4/events/2026-02-26?market_ids=1,2,3&affiliate_ids=19,23&main_line=true&offset=300"
Use these levers aggressively:
  • market_ids: biggest control for response size
  • affiliate_ids: only request the books you actually surface
  • main_line=true: skip alternate lines if your product only shows the primary market
  • event-specific endpoints: if you only care about a handful of games, do not fetch the full slate
  • hide_closed_markets=1: useful during market discovery to avoid off-board clutter

Delta Endpoint Bootstrap Flow

The pattern for using delta endpoints has three stages:

1. Fetch the full snapshot

Start by loading the complete event data for the sport and date you need. This gives you the initial state and the first delta cursor.
curl "https://therundown.io/api/v2/sports/4/events/2026-02-26?key=YOUR_API_KEY&market_ids=1,2,3&affiliate_ids=19,23&main_line=true&offset=300"
The response includes meta.delta_last_id — save this value. It is an integer cursor, and it is the bootstrap cursor for the /api/v2/markets/delta (price-change) feed.

2. Poll the market delta endpoint

On each subsequent poll, pass your saved cursor to /api/v2/markets/delta. It returns only the prices that changed since that cursor — the most efficient way to keep odds fresh.
curl "https://therundown.io/api/v2/markets/delta?key=YOUR_API_KEY&last_id=PREVIOUS_DELTA_LAST_ID&sport_id=4&market_ids=1,2,3"
Each response includes its own meta.delta_last_id (also an integer) — use that as last_id on the next poll. Do not bootstrap with last_id=0: a cursor that has fallen too far behind the current head is rejected, so always seed from a fresh events snapshot.
The two delta endpoints take different, non-interchangeable cursors:
  • /api/v2/markets/delta (price changes) uses an integer cursor. Bootstrap it from the events snapshot’s integer meta.delta_last_id, then follow the integer cursor in each markets-delta response. This is the endpoint for odds polling.
  • /api/v2/delta (full event-object changes: status, scores, the whole event) uses an ordered UUID cursor, e.g. 11f1-23b3-f4d42784-8057-a3a997572248. That cursor is only returned by /api/v2/delta’s own responses — the events snapshot does not provide it.
Passing the integer events cursor to /api/v2/delta returns a 400. For price and odds polling, use /api/v2/markets/delta.

3. Merge updates into local state

Each delta response contains the full updated object — replace (do not partially merge) the corresponding entry in your local cache. Update your cursor to the new delta_last_id from the response.

Choosing Between Event and Market Delta

EndpointReturnsCursor typeBootstrap sourceBest for
GET /api/v2/markets/deltaIndividual price changes onlyIntegerEvents snapshot meta.delta_last_idOdds-focused apps (recommended for price polling)
GET /api/v2/deltaFull event objects (scores, status, markets)Ordered UUIDThe endpoint’s own response meta.delta_last_idApps that need full event-object changes alongside odds
The market delta is usually the cheapest option because it returns only the specific prices that changed, rather than the entire event object. The cursors are not interchangeable — the integer from the events snapshot bootstraps markets/delta only, and /api/v2/delta rejects it with a 400. Match your polling frequency to your use case. Faster polling uses more data points and increases the chance of hitting your burst limit if you parallelize heavily.
Use CaseIntervalEndpointNotes
Live odds screen5–10sMarket deltaFastest practical interval for REST
Pre-match odds monitoring30–60sMarket deltaLines move slowly before game time
Live scores15–30sEvent deltaScore updates come in bursts during play
Pre-match schedules5 minFull eventsSchedules rarely change close to game time
Historical/closing linesOn demandOpeners/closingFetch once after the event starts or ends
For sub-second updates, use the WebSocket endpoint on a WebSocket-enabled tier instead of polling. WebSocket traffic does not increment the HTTP request counter, but pushed messages are still metered as data points.

Cache TTL Recommendations

Not all data changes at the same rate. Cache aggressively for reference data and use shorter TTLs for live data.
Data TypeRecommended TTLEndpoint
Sports list24 hoursGET /api/v2/sports
Affiliates list24 hoursGET /api/v2/affiliates
Teams6 hoursGET /api/v2/sports/{id}/teams
Market definitions6 hoursGET /api/v2/markets
Events (pre-match)5 minutesGET /api/v2/sports/{id}/events/{date}
Events (live)Use deltaGET /api/v2/delta or GET /api/v2/markets/delta
Market pricesReal-timeDelta endpoint or WebSocket

Staleness Guards

Your delta cursor can become stale if you stop polling for an extended period. When this happens, the delta endpoint may return an error or skip events that changed while you were away. How to detect stale data:
  • Track the updated_at timestamp on your cached events. If the newest update is more than 5 minutes old during a live game window, your data may be stale.
  • If a delta response returns an empty result but you know games are in progress, your cursor may have expired.
  • If you receive an error response from the delta endpoint, re-bootstrap from a full snapshot.
How to recover:
  1. Discard your current delta cursor
  2. Fetch a fresh full snapshot from the events endpoint
  3. Extract the new delta_last_id and resume polling

Watch Your Usage Headers

Every production poller should log and monitor:
  • X-Datapoints
  • X-Datapoints-Used
  • X-Datapoints-Remaining
  • X-Datapoints-Reset
  • X-Rate-Limit
That gives you the feedback loop to tune filters and polling intervals before users start hitting limits.

Code Example: Python Polling Loop

This example fetches a full snapshot, then polls the market delta endpoint with automatic fallback to a full refresh when the cursor goes stale.
import requests
import time

API_KEY = "YOUR_API_KEY"
BASE = "https://therundown.io/api/v2"
SPORT_ID = 4
MARKET_IDS = "1,2,3"
AFFILIATE_IDS = "19,23"
POLL_INTERVAL = 5  # seconds

# Local cache: event_id -> event data
events = {}

def fetch_full_snapshot():
    """Load the full event list and return the delta cursor."""
    resp = requests.get(
        f"{BASE}/sports/{SPORT_ID}/events/2026-02-26",
        headers={"X-TheRundown-Key": API_KEY},
        params={
            "market_ids": MARKET_IDS,
            "affiliate_ids": AFFILIATE_IDS,
            "main_line": "true",
            "offset": "300",
        }
    )
    data = resp.json()

    for event in data.get("events", []):
        events[event["event_id"]] = event

    cursor = data.get("meta", {}).get("delta_last_id", "0")
    print(f"Loaded {len(events)} events, cursor={cursor}")
    return cursor

def poll_market_delta(last_id):
    """Fetch price changes since last_id. Returns new cursor."""
    resp = requests.get(
        f"{BASE}/markets/delta",
        headers={"X-TheRundown-Key": API_KEY},
        params={
            "last_id": last_id,
            "sport_id": SPORT_ID,
            "market_ids": MARKET_IDS,
        }
    )

    if resp.status_code != 200:
        print(f"Delta error {resp.status_code}, re-bootstrapping...")
        return None  # Signal to re-bootstrap

    data = resp.json()
    changes = data.get("deltas", [])

    for change in changes:
        eid = change.get("event_id")
        print(
            f"Price change: {eid} {change.get('market_name')} "
            f"{change.get('participant_name')} -> {change.get('price')}"
        )

    new_cursor = data.get("meta", {}).get("delta_last_id", last_id)
    return new_cursor

# Bootstrap
cursor = fetch_full_snapshot()

# Poll loop
while True:
    time.sleep(POLL_INTERVAL)
    result = poll_market_delta(cursor)

    if result is None:
        # Cursor went stale, re-bootstrap
        cursor = fetch_full_snapshot()
    else:
        cursor = result

WebSocket vs. Polling Decision Guide

FactorREST PollingWebSocket
Latency5–60s depending on intervalSub-second
Usage impactConsumes data points and counts toward HTTP burst limitConsumes data points but does not increment the HTTP request counter
Implementation complexitySimple HTTP requestsRequires connection management, reconnection logic
Data freshnessAs fresh as your poll intervalReal-time
ReliabilityEach request is independentMust handle disconnects and reconnections
Best forPre-match monitoring, low-frequency updatesLive odds screens, real-time dashboards
Many production applications use both: WebSocket for live windows, with delta polling as a fallback when the socket disconnects or for lower-tier keys that do not have WebSocket access. See the Building an Odds Screen guide for this pattern in practice.