> ## 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 Betting Platform

> Step-by-step guide to building a sports betting platform from scratch. Architecture, odds ingestion, bet placement, settlement. Full stack tutorial.

# 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 Method                     | Purpose                                  |
| ------------------------------ | ---------------------------------------- |
| `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

```mermaid theme={null}
graph LR
    A["Frontend<br/>(React)"] -->|API calls| B["Backend<br/>(Node.js)"]
    B -->|responses| A
    B -->|API requests| C["FieldFunded<br/>API"]
    C -->|data| B
    B -->|read/write| D["Database<br/>(Postgres)"]
```

<Warning>
  **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.
</Warning>

## Step 1: Sports Navigation

```typescript theme={null}
// 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

```typescript theme={null}
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.

```typescript theme={null}
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](/guides/build-settlement-engine) for the full implementation.

The short version:

```typescript theme={null}
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:

```typescript theme={null}
// 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

| Layer                         | Recommendation                        | Why                                  |
| ----------------------------- | ------------------------------------- | ------------------------------------ |
| Frontend                      | Next.js / React                       | SSR for SEO, React for interactivity |
| Backend                       | Node.js + Express                     | SDK is TypeScript-native             |
| Database                      | PostgreSQL (via Supabase)             | ACID transactions for balances       |
| Odds Data                     | FieldFunded API                       | 1,000+ markets, auto settlement      |
| Hosting                       | Vercel (frontend) + Railway (backend) | Both have free tiers                 |
| **Total infrastructure cost** |                                       | **\$0/month**                        |

## MVP Timeline

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

<CardGroup cols={2}>
  <Card title="Settlement Engine Guide" icon="gears" href="/guides/build-settlement-engine">
    Deep dive into automatic settlement →
  </Card>

  <Card title="SDK Reference" icon="code" href="/sdk">
    All 25 SDK methods →
  </Card>
</CardGroup>

***

## Related Guides

* [Build a Parlay Calculator](/guides/build-parlay-calculator) — validate parlays with real odds
* [Build a Discord Bot](/guides/build-discord-bot) — serve odds to your community
* [For Startups](/compare/for-startups) — ship your MVP this week for \$0

<Card title="Get Your Free API Key" icon="key" href="/quickstart">
  Start building in 5 minutes — 10,000 free requests/month
</Card>
