Sports Data Infrastructure

    Pre-Match vs In-Play Data: Integration Needs

    Understand the technical and business differences between pre-match and in-play odds.

    13 min read3,051 words
    Share
    TL;DR

    Your sportsbook needs to support both pre-match and in-play betting. They seem similar—both are odds, both update, both need to be accurate. But they're fundamentally different systems with different requirements.

    Two Different Beasts: Pre-Match vs In-Play Odds

    Your sportsbook needs to support both pre-match and in-play betting. They seem similar—both are odds, both update, both need to be accurate. But they're fundamentally different systems with different requirements.

    This difference is why most sportsbooks run into problems around 6 months of operation: they built for pre-match (simple, predictable) and then in-play breaks everything.

    Let's understand the differences.

    Pre-Match Odds: The Stable Case

    Pre-match odds are set 2-7 days before an event. They update, but relatively slowly and predictably.

    Characteristics:

    AspectValue
    First odds set5-7 days before event
    Typical updatesEvery 5-30 seconds
    Peak velocity10-50 updates per minute per market
    Market changes triggering updatesBetting volume, injury news, weather
    User behaviorUsers bookmark odds, compare across sportsbooks, plan bets
    Typical user actionPlace bet, wait (don't need immediate odds confirmation)
    Risk toleranceCan accept 3-5 second stale odds

    Examples:

    • Monday morning: Manchester United vs Liverpool playoff on Friday set at 2.10 (United win)
    • Monday afternoon: £10M in betting volume comes in on United, odds move to 2.05
    • Tuesday: A key Liverpool player is injured, odds move to 2.15
    • Wednesday: Closer to game, odds stabilize at 2.08-2.12 range
    • Thursday: Final pre-match odds locked in

    Data requirements:

    • Latency: 5-10 seconds is acceptable
    • Accuracy: Very important (wrong odds cost money)
    • Update frequency: Every 5-30 seconds
    • Historical tracking: Important (need to know odds progression)
    • Market depth: Show top 5-10 markets (winner, over/under, player props)

    Integration approach:

    • REST API polling every 5-10 seconds is acceptable
    • Can aggregate from single bookmaker without redundancy issues
    • Database can be simple (no need for sub-second updates)
    • Risk management systems can react on 10-30 second intervals

    Cost: Low. Single provider, simple infrastructure, acceptable latency.

    In-Play Odds: The Complex Case

    In-play odds are updated during the event. This is where betting volume concentrates (60-70% of daily handle) and where latency matters.

    Characteristics:

    AspectValue
    First odds setAt match start (kick-off for soccer, tip-off for basketball)
    Typical updatesEvery 50-500ms
    Peak velocity500-2,000 updates per minute per market
    Market changes triggering updatesGoals, cards, fouls, time progression
    User behaviorWatch match, react in real-time, place quick bets
    Typical user actionSee goal, place bet immediately
    Risk tolerance200-300ms stale odds is noticeable; >1 second is bad

    Example timeline (Soccer match):

    00:00 - Kick-off
      Odds: Manchester City -0.5 (1.95) vs Liverpool (1.95)
      Updates: Every 1-2 seconds as play progresses
    
    10:23 - Manchester City scores
      Odds: Manchester City -1.5 (1.85) vs Liverpool (2.10)
      Updates: Happen within 500ms of goal
      Volume spike: 10x normal (users placing bets on City)
    
    15:30 - Settled, play resumes
      Odds: Back to dynamic updates every 1-2 seconds
      Volume: Returns to normal
    
    40:45 - Half-time
      Odds: Locked (half-time is break)
      Volume: Users discuss, place bets for second half
    
    45:00 - Second half starts
      Odds: Reopen to new odds for second half
      Updates: Resume at 1-2 second intervals
    
    85:00 - Liverpool scores
      Odds: Swings the other direction
      Updates: 500ms
    
    90:00 - Full-time whistle
      Odds: Locked (market suspended)
      Volume: Peak (everyone settling bets)
    
    90:30 - Final settle
      Odds: All markets closed, results confirmed
    

    Data requirements:

    • Latency: <300ms is necessary, <200ms is competitive
    • Accuracy: Critical (wrong odds mid-match cause chaos)
    • Update frequency: 50-500ms
    • Historical tracking: Essential (audit why odds moved)
    • Market depth: Show 20+ markets (all possible live bets)

    Integration approach:

    • WebSocket streaming is mandatory (REST polling is too slow)
    • Multiple bookmakers for redundancy (if one feed lags, you have backup)
    • Sub-second database latency required (cache layer)
    • Risk management needs real-time updates (seconds matter)

    Cost: High. Multiple providers, streaming infrastructure, real-time database, 24/7 monitoring.

    The Integration Challenge: Supporting Both Simultaneously

    Here's where most implementations struggle:

    Naive approach: Build one system, use it for both.

    This fails because:

    1. Pre-match optimisation (poll every 5 seconds) is too slow for in-play
    2. In-play optimisation (stream every 100ms) wastes resources on pre-match
    3. Risk management rules differ (can accept latency in pre-match, can't in in-play)
    4. Database load differs (pre-match is steady, in-play spikes 10x)

    Better approach: Two-tier system with different infrastructure for each.

    [Pre-Match Events]
        ↓
    [REST API polling layer - 5-10 second updates]
        ↓
    [Database - standard latency acceptable]
        ↓
    [Display to users]
    
    [In-Play Events]
        ↓
    [WebSocket streaming layer - real-time updates]
        ↓
    [Cache layer (Redis) - sub-second latency]
        ↓
    [Risk management + Display to users]
    

    Implementation Pattern 1: Event-Based Routing

    Route events to the appropriate infrastructure based on state:

    type EventRouter struct {
      preMatchSubscribers []chan OddsUpdate
      inPlaySubscribers   []chan OddsUpdate
    }
    
    func (er *EventRouter) RouteUpdate(event Event, update OddsUpdate) {
      switch event.State {
      case "PRE_MATCH":
        // Route to pre-match subscribers (polling-based)
        for _, sub := range er.preMatchSubscribers {
          select {
          case sub <- update:
          default:
            // Subscriber queue full, skip (pre-match can tolerate missed updates)
          }
        }
    
      case "LIVE":
        // Route to in-play subscribers (streaming-based)
        for _, sub := range er.inPlaySubscribers {
          select {
          case sub <- update:
          case <-time.After(10 * time.Millisecond):
            // In-play: if subscriber can't keep up, they're falling behind
            metrics.Increment("inplay.subscriber_backlog")
          }
        }
    
      case "CLOSED", "SUSPENDED":
        // No updates to subscribers
      }
    }
    

    Implementation Pattern 2: Graduated Latency

    Automatically degrade data freshness based on event state:

    type OddsCache struct {
      preMatchTTL time.Duration  // 10 seconds
      inPlayTTL   time.Duration  // 1 second
    }
    
    func (oc *OddsCache) Get(eventID string) (OddsUpdate, error) {
      event := getEvent(eventID)
      odds := oc.cache.Get(eventID)
    
      if odds == nil {
        return nil, ErrNotCached
      }
    
      // Check TTL based on event state
      var ttl time.Duration
      switch event.State {
      case "PRE_MATCH":
        ttl = oc.preMatchTTL
      case "LIVE":
        ttl = oc.inPlayTTL
      default:
        return nil, ErrEventClosed
      }
    
      age := time.Since(odds.Timestamp)
      if age > ttl {
        // Odds are stale, probably don't serve them in-play
        if event.State == "LIVE" {
          return nil, ErrStaleLiveOdds
        }
        // Pre-match, it's okay to serve slightly stale odds
        return odds, ErrOddsSlightlyStale
      }
    
      return odds, nil
    }
    

    Implementation Pattern 3: Dynamic Buffer Sizing

    Pre-match can use smaller buffers (less data); in-play needs larger buffers (more volume):

    type OddsBuffer struct {
      preMatchBuffer chan OddsUpdate
      inPlayBuffer   chan OddsUpdate
    }
    
    func NewOddsBuffer() *OddsBuffer {
      return &OddsBuffer{
        preMatchBuffer: make(chan OddsUpdate, 100),   // Small buffer, fine to drop
        inPlayBuffer:   make(chan OddsUpdate, 5000),  // Large buffer, can't drop
      }
    }
    

    Data Requirements by Market Type

    Different market types have different latency needs:

    Match Winner (Winner/Draw/Loss)

    Pre-match: Updates every 5-30 seconds OK In-play: Updates every 500ms critical Why: Users care about who's winning now, which changes dramatically in-play

    Total Goals (Over/Under 2.5)

    Pre-match: Updates every 5-30 seconds OK In-play: Updates every 100ms critical Why: Goals change probability massively; market reacts immediately

    Next Goal Scorer

    Pre-match: Updates every 30 seconds OK (odds don't change much without play) In-play: Updates every 1-2 seconds critical (changes constantly during play) Why: Player positioning changes, substitutions happen, injury status changes

    Correct Score (1-0, 2-1, etc.)

    Pre-match: Updates every 10 seconds OK In-play: Updates every 500ms critical Why: Goal changes probability of this exact score; market reprices immediately

    Minute of Next Goal

    Pre-match: Updates every 5 seconds OK In-play: Updates every 100-200ms critical Why: Constantly resets as time progresses; probability shifts every minute

    Implication: Your system needs market-specific latency targets, not one global latency SLA.

    Risk Management Differences

    Pre-match and in-play have different risk management needs:

    Pre-Match Risk Management

    Update every 10 seconds:
      - Check: is my exposure > $1M on any outcome?
      - Check: are my odds more than 10% out of line with market?
      - Check: is there an obvious arbitrage (users can beat me)?
    
    Action: Adjust odds manually if needed, takes 1-2 minutes
    Response time requirement: 10 minutes is acceptable
    

    In-Play Risk Management

    Update every 500ms:
      - Check: is my exposure > $500K on any outcome?
      - Check: did odds move >5% in last 100ms (might be data error)?
      - Check: is my live player exposure balanced?
    
    Action: Automatic odds adjustment if thresholds violated
    Response time requirement: 100-500ms (seconds matter)
    

    Database Strategy Differences

    Pre-Match Database

    SQL Database (PostgreSQL, MySQL)
      - Steady write rate: 10-20 inserts/sec
      - Latency requirement: 100-500ms OK
      - Historical retention: 1 year
      - Cost: $500-1,000/month
    
    Schema:
      odds (event_id, market_id, timestamp, decimal_odds, bookmaker)
      events (event_id, event_name, sport, start_time)
      markets (market_id, market_name, type)
    

    In-Play Database

    Cache + Time-Series Database
      - Redis (hot): 1,000-5,000 writes/sec, <10ms latency
      - TimescaleDB (historical): 500-1,000 writes/sec, 100-500ms latency
      - Cost: $5,000-10,000/month
    
    Schema (Redis):
      odds:{eventId}:{marketId} → latest odds update
      events:{eventId}:live → events currently in-play
    
    Schema (TimescaleDB):
      time-series table with hypertables for fast range queries
    

    Cost Breakdown: Pre-Match vs In-Play

    Deploying both simultaneously:

    Pre-Match Infrastructure:

    • REST API polling: $2-5K/month
    • Basic database: $1-2K/month
    • Monitoring: $500-1K/month
    • Subtotal: $3.5-8K/month

    In-Play Infrastructure:

    • WebSocket streaming: $10-20K/month (persistent connections are expensive)
    • Redis cache: $3-5K/month
    • Time-series database: $5-10K/month
    • Real-time monitoring/alerting: $2-3K/month
    • Subtotal: $20-38K/month

    Total: $23.5-46K/month for both pre-match and in-play support.

    This is why small sportsbooks often launch with pre-match only, then add in-play later. In-play is 5-10x more expensive.

    Real-World Scaling Examples

    Case Study 1: News Site (Pre-Match Focused)

    Architecture:

    • 50 concurrent users viewing odds
    • 10 sports, 100 events per day
    • Pre-match odds only
    • API polling every 10 seconds

    Infrastructure:

    • Single REST API endpoint
    • PostgreSQL database
    • Redis cache
    • No special infrastructure

    Costs:

    • API calls: 50 users × 100 events × 8,640 calls/day = 43.2M calls/day
    • At $0.001/call = $43K/month
    • Infrastructure: $5K/month
    • Total: ~$50K/month

    Scaling limits:

    • Can handle 10,000 concurrent users
    • At 10K users × $1 ARPU = $10K revenue/month
    • Not profitable on API costs alone (need affiliate revenue)

    Case Study 2: Sportsbook (In-Play Heavy)

    Architecture:

    • 100,000 concurrent users
    • 500 events per day (50 in-play simultaneously)
    • 50+ markets per event
    • In-play focused (70% of volume)
    • WebSocket streaming

    Infrastructure:

    • WebSocket servers (horizontal scaling)
    • Redis (cache + session store)
    • Kafka (event streaming)
    • TimescaleDB (historical)
    • 4 data centers (redundancy)

    Costs:

    • Infrastructure: $200K/month
    • Data provider (in-play) handling: $100K/month
    • Operations team: $150K/month
    • Total: ~$450K/month

    Revenue model:

    • 100K users × $5 ARPU = $500K/month
    • Profitable with <90% of revenue

    The difference: sportsbooks can afford high infrastructure costs because volume justifies it.

    Integration Testing Strategy

    Before going live, you need comprehensive testing:

    Load Testing

    Scenario 1: Normal load
      - 1,000 concurrent users
      - 10 updates per second
      - Should see <200ms latency
    
    Scenario 2: Peak load
      - 50,000 concurrent users
      - 1,000 updates per second
      - Should see <500ms latency
    
    Scenario 3: Breaking point
      - 100,000 concurrent users
      - 5,000 updates per second
      - Should see <2 second latency (or graceful degradation)
    
    Scenario 4: Provider failure
      - Primary provider goes offline at T=5m
      - Should see <10 second failover
      - Secondary provider takes over
    
    Scenario 5: Major event
      - Super Bowl kicks off
      - 10x normal volume of bets
      - Should maintain <500ms latency
    

    Testing Tools

    # Load testing
    k6 run load-test.js
    
    # Chaos testing (intentionally break things)
    chaos kill-pod odds-service-1
    chaos drop-packets odds-api
    chaos delay-network 2s
    
    # Monitoring during tests
    watch 'curl http://localhost:9090/metrics | grep latency'
    

    Hybrid Approach: Progressive Adoption

    Many operators start pre-match only, then add in-play:

    Month 1-2: MVP (pre-match only)
      - REST API
      - Basic caching
      - Single provider
      - Cost: $10-20K/month
    
    Month 3-4: Feature expansion
      - Add WebSocket for real-time
      - Expand to 5-10 providers
      - Add risk management
      - Cost: $30-50K/month (3x)
    
    Month 5-6: In-play launch
      - Full in-play market coverage
      - Distributed infrastructure
      - 24/7 monitoring
      - Cost: $100-150K/month (5x)
    
    Month 7-12: Scale
      - Optimise based on metrics
      - Expand to new sports/regions
      - Add player props, live markets
      - Cost: Stays at $100-150K/month (efficiency gains)
    
    Total capital spent: $300-400K over first year
    ROI: If you achieve $1-2M revenue by month 12, it's highly profitable
    

    Operational Playbook: What to Do When Things Break

    When API Latency Spikes to 2+ Seconds

    1. Check provider status page

      • Is provider having issues? (Usually yes)
      • Check their status.page.io or status endpoint
    2. Check your caching

      • Are you getting cache hits or missing?
      • If missing, why? (Cold cache at startup, etc.)
    3. Check your database

      • Is database query slow?
      • Is database CPU spiked?
    4. Implement circuit breaker

      • Stop calling slow API
      • Return cached data instead
      • Alert team that you're in degraded mode
    5. Coordinate with provider

      • Call their support line
      • Ask ETA for resolution
      • Prepare to switch to secondary provider

    When Sportsbook Users Report Stale Odds

    1. Verify with user

      • What odds did they see?
      • When did they see it?
      • What does provider show now?
    2. Calculate staleness

      • If 5 seconds old: Your polling interval is too long
      • If 30+ seconds old: Provider issue or network issue
    3. Adjust polling interval

      • If users seeing 5-10 second old odds
      • Reduce polling from 10s to 5s
      • Monitor cost increase
    4. Consider streaming

      • If continuously stale
      • Need to upgrade to WebSocket
      • Implement streaming layer

    When Cache Hit Rate Drops Below 80%

    1. Check if cold start

      • Did you restart service?
      • Cache is empty, needs to warm up (5-10 minutes)
    2. Check if cache is working

      • Are items being stored?
      • Are items expiring too fast?
    3. Increase cache TTL

      • Pre-match: Can extend from 10s to 30s (users won't notice)
      • In-play: Keep at 1-2s (must be fresh)
    4. Pre-warm cache

      • Load popular events at startup
      • Don't wait for requests to populate cache

    Moving Forward: Planning Your Integration

    1. Start with pre-match only if you're bootstrapping (cheaper, simpler, faster to market)
    2. Plan for in-play from day one (architecture decisions matter, retrofitting is expensive)
    3. Budget for in-play when you have the capital and user base (5-10x more expensive than pre-match)
    4. Choose a provider who supports both well (FairPlay does; most don't provide equal quality on both)
    5. Test thoroughly before launch (load testing is essential)
    6. Monitor religiously after launch (understand your actual performance)
    7. Have a playbook for common failure scenarios

    The operators winning today are those who have excellent in-play odds. Pre-match is table-stakes. In-play is the competitive advantage.

    FairPlay offers pre-match and in-play at the same quality level, which is rare. Most providers are good at one or the other, not both.


    Related Articles:

    Share

    Ready to explore BetTech for your business?

    Talk to the FairPlay team about how our platform can work for your business.

    Get Started

    Related Articles

    Explore More Insights