> ## 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 Twitch Esports Odds Overlay for OBS

> Build a Twitch stream overlay showing live esports odds for CS2, LoL, Dota 2, and Valorant. OBS Browser Source integration.

# Build a Twitch Esports Overlay with Live Odds

Show real-time esports odds directly on your stream. Viewers see odds updating live as the game progresses.

## What You'll Use

| SDK Method       | Endpoint                   | Purpose                  |
| ---------------- | -------------------------- | ------------------------ |
| `getEvents()`    | `GET /v1/events`           | Find esports events      |
| `getLive()`      | `GET /v1/live`             | Get live esports matches |
| `getEventOdds()` | `GET /v1/events/{id}/odds` | Real-time odds           |
| `getScores()`    | `GET /v1/scores`           | Current map/round scores |
| `getSports()`    | `GET /v1/sports`           | List esports categories  |

## How Twitch Overlays Work

OBS Studio has a "Browser Source" that renders any URL as a transparent overlay on your stream. You build a web page → OBS renders it on top of your game.

## Step 1: Find Esports Events

FieldFunded covers esports under specific sport keys. Check available esports:

```typescript theme={null}
const sports = await client.getSports();
const esports = sports.sports.filter(s => s.is_esport);
// e.g. 'esports-csgo', 'esports-lol', 'esports-dota2', 'esports-valorant'
```

Get live esports matches:

```typescript theme={null}
const liveEsports = await client.getLive({
  sport: 'esports-csgo', // CS2
});
```

## Step 2: Build the Overlay Page

```html theme={null}
<!DOCTYPE html>
<html>
<head>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      background: transparent;
      font-family: 'Inter', sans-serif;
      color: #fff;
    }

    .overlay {
      position: fixed;
      bottom: 20px;
      right: 20px;
      background: rgba(10, 10, 30, 0.85);
      border: 1px solid #53FC18;
      border-radius: 12px;
      padding: 16px;
      min-width: 280px;
      backdrop-filter: blur(10px);
    }

    .match-title {
      font-size: 14px;
      font-weight: 600;
      margin-bottom: 8px;
      color: #53FC18;
    }

    .teams {
      display: flex;
      justify-content: space-between;
      margin-bottom: 12px;
    }

    .team {
      text-align: center;
      flex: 1;
    }

    .team-name { font-size: 13px; font-weight: 500; }

    .odds {
      font-size: 18px;
      font-weight: 700;
      padding: 4px 8px;
      background: rgba(83, 252, 24, 0.15);
      border-radius: 6px;
      margin-top: 4px;
      display: inline-block;
    }

    .score {
      text-align: center;
      font-size: 24px;
      font-weight: 700;
      color: #53FC18;
    }

    .map-score { font-size: 11px; color: #aaa; margin-top: 2px; }

    .vs { color: #666; font-size: 12px; }

    @keyframes pulse {
      0%, 100% { opacity: 1; }
      50% { opacity: 0.7; }
    }

    .live-dot {
      width: 8px;
      height: 8px;
      background: #ff4444;
      border-radius: 50%;
      display: inline-block;
      animation: pulse 1.5s infinite;
      margin-right: 6px;
    }
  </style>
</head>
<body>
  <div class="overlay" id="overlay">
    <div class="match-title">
      <span class="live-dot"></span>
      <span id="league">Loading...</span>
    </div>
    <div class="teams">
      <div class="team">
        <div class="team-name" id="team1">—</div>
        <div class="odds" id="odds1">—</div>
      </div>
      <div class="score">
        <span id="score1">0</span>
        <span class="vs"> - </span>
        <span id="score2">0</span>
        <div class="map-score" id="mapScore"></div>
      </div>
      <div class="team">
        <div class="team-name" id="team2">—</div>
        <div class="odds" id="odds2">—</div>
      </div>
    </div>
  </div>

  <script>
    const API_KEY = 'YOUR_API_KEY';
    const BASE = 'https://api.fieldfunded.com/v1';
    const SPORT = 'esports-csgo'; // Change for LoL, Dota, Valorant
    const POLL_INTERVAL = 15000; // 15 seconds

    async function api(path) {
      const res = await fetch(`${BASE}${path}`, {
        headers: { 'X-API-Key': API_KEY },
      });
      return res.json();
    }

    async function update() {
      try {
        const live = await api(`/live?sport=${SPORT}`);
        if (!live.events || live.events.length === 0) {
          document.getElementById('league').textContent = 'No live match';
          return;
        }

        const event = live.events[0]; // First live match
        const odds = await api(`/events/${event.id}/odds`);

        document.getElementById('league').textContent = event.league;
        document.getElementById('team1').textContent = event.home_team;
        document.getElementById('team2').textContent = event.away_team;

        // Score
        if (event.scoreboard) {
          document.getElementById('score1').textContent = event.scoreboard.home || '0';
          document.getElementById('score2').textContent = event.scoreboard.away || '0';
        }

        // Main market odds
        if (odds.markets && odds.markets.length > 0) {
          const main = odds.markets[0];
          if (main.selections.length >= 2) {
            document.getElementById('odds1').textContent = main.selections[0].odds;
            document.getElementById('odds2').textContent = main.selections[1].odds;
          }
        }
      } catch (err) {
        console.error('Update failed:', err);
      }
    }

    update();
    setInterval(update, POLL_INTERVAL);
  </script>
</body>
</html>
```

## Step 3: Add to OBS

1. Open OBS Studio
2. Sources → Add → **Browser Source**
3. Point to `http://localhost:3000/overlay.html` (or host it online)
4. Set width/height (300×120 works well)
5. Check "Shutdown source when not visible"

## Rate Limit Math

* Polling every 15 seconds = 2 requests per poll (live + odds)
* Per hour: 240 requests
* 4-hour stream: \~960 requests
* Free tier (10K/month) supports **\~10 full streams per month**
* Starter (\$29): **\~300 streams/month** — more than enough

<Card title="Live Events Reference" icon="bolt" href="/api-reference/live/get-live-events">
  See live endpoint docs →
</Card>

***

## Related Guides

* [Build a Discord Bot](/guides/build-discord-bot) — serve odds in Discord
* [Build a Chrome Extension](/guides/build-chrome-extension) — odds overlay on sports articles
* [FieldFunded vs The Odds API](/compare/vs-theoddsapi) — why we have more esports data

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

  <Card title="See Pricing" icon="dollar-sign" href="/compare/pricing">
    All plans compared side by side
  </Card>
</CardGroup>
