Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.fieldfunded.com/llms.txt

Use this file to discover all available pages before exploring further.

Build a Sports Betting Platform from Scratch

This guide walks through the full architecture of a betting platform: odds ingestion, bet placement, balance management, and automatic settlement.

What You’ll Use

Every major endpoint group:
SDK MethodPurpose
getSports() / getLeagues()Build the sports navigation
getEvents()List games for each sport
getEvent()Get 1,000+ markets for a game
getLive() / getScores()Real-time scores and live event tracking
search()Find events by team name
getEventOdds()Current odds for display
checkBet()Validate bet before placement
checkParlay()Validate parlay combinations
getSettlements()Automatic resolution after game ends
getEventResult()Final score
getUsage()Monitor API consumption

Architecture Overview

┌──────────────┐     ┌───────────────┐     ┌──────────────┐
│   Frontend   │────▶│   Backend     │────▶│  FieldFunded │
│   (React)    │◀────│   (Node.js)   │◀────│     API      │
└──────────────┘     └───────┬───────┘     └──────────────┘

                     ┌───────▼───────┐
                     │   Database    │
                     │  (Postgres)   │
                     └───────────────┘
Never expose your API key in frontend code. All FieldFunded API calls should go through your backend. The frontend calls your backend, and your backend calls FieldFunded.

Step 1: Sports Navigation

// Backend route
app.get('/api/sports', async (req, res) => {
  const data = await client.getSports();
  res.json(data.sports.filter(s => s.active_events > 0));
});

app.get('/api/leagues/:sport', async (req, res) => {
  const data = await client.getLeagues({ sport: req.params.sport });
  res.json(data.leagues);
});

Step 2: Event Listing

app.get('/api/events', async (req, res) => {
  const events = await client.getEvents({
    sport: req.query.sport as string,
    league: req.query.league as string,
    status: req.query.status as string, // 'prematch', 'live', 'ended'
    starts_within: '24h',
  });
  res.json(events.events);
});

// Event detail with ALL markets (1,000+)
app.get('/api/events/:id', async (req, res) => {
  const detail = await client.getEvent(req.params.id);
  res.json(detail);
});

Step 3: Bet Placement

The golden rule: lock odds at time of placement, not at time of display.
app.post('/api/bets/place', async (req, res) => {
  const { userId, eventId, market, outcome, odds, stake, marketId, selectionId } = req.body;

  // 1. Validate user has sufficient balance
  const user = await db.query('SELECT balance FROM users WHERE id = $1', [userId]);
  if (user.balance < stake) {
    return res.status(400).json({ error: 'Insufficient balance' });
  }

  // 2. Deduct stake atomically
  await db.query('BEGIN');
  try {
    await db.query(
      'UPDATE users SET balance = balance - $1 WHERE id = $2 AND balance >= $1',
      [stake, userId]
    );

    // 3. Store bet with the odds at time of placement
    await db.query(
      `INSERT INTO bets (user_id, event_id, market_name, outcome, odds, stake, market_id, selection_id, status)
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'pending')`,
      [userId, eventId, market, outcome, odds, stake, marketId, selectionId]
    );

    await db.query('COMMIT');
    res.json({ success: true });
  } catch (err) {
    await db.query('ROLLBACK');
    res.status(500).json({ error: 'Bet placement failed' });
  }
});

Step 4: Settlement (the magic)

This is where FieldFunded saves you weeks of development. See the dedicated Settlement Engine guide for the full implementation. The short version:
import cron from 'node-cron';

// Poll every 60 seconds
cron.schedule('* * * * *', async () => {
  const settlements = await client.getSettlements({ limit: 20 });

  for (const s of settlements.settlements) {
    const pendingBets = await db.query(
      'SELECT * FROM bets WHERE event_id = $1 AND status = $2',
      [s.event_id, 'pending']
    );

    for (const bet of pendingBets) {
      const result = await client.checkBet({
        selections: [{
          event_id: bet.event_id,
          market: bet.market_name,
          outcome: bet.outcome,
          odds: bet.odds,
          stake: bet.stake,
          market_id: bet.market_id,
          selection_id: bet.selection_id,
        }],
      });

      if (result.results[0].result !== 'pending') {
        // Process win/loss/refund — see Settlement Engine guide
      }
    }
  }
});

Step 5: Live Scores

Use getScores() for efficient live score polling:
// Lightweight endpoint — optimized for frequent polling
app.get('/api/scores', async (req, res) => {
  const data = await client.getScores({
    sport: req.query.sport as string,
  });
  res.json(data.scores);
});

Tech Stack Summary

LayerRecommendationWhy
FrontendNext.js / ReactSSR for SEO, React for interactivity
BackendNode.js + ExpressSDK is TypeScript-native
DatabasePostgreSQL (via Supabase)ACID transactions for balances
Odds DataFieldFunded API1,000+ markets, auto settlement
HostingVercel (frontend) + Railway (backend)Both have free tiers
Total infrastructure cost$0/month

MVP Timeline

WeekWhat to build
1User auth + sports navigation + event listing
2Event detail page with markets + bet slip
3Bet placement + balance management
4Settlement integration + bet history

Settlement Engine Guide

Deep dive into automatic settlement →

SDK Reference

All 25 SDK methods →

Get Your Free API Key

Start building in 5 minutes — 10,000 free requests/month