> ## 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 Chrome Extension for Sports Odds

> Build a Chrome extension that detects team names on ESPN/BBC Sport and shows real-time odds inline. Manifest V3 + FieldFunded API.

# Build a Chrome Extension That Shows Odds on Sports Articles

Reading a match preview on ESPN or BBC Sport? This extension detects team names and injects real-time odds directly into the page.

## What You'll Use

| SDK Method       | Endpoint                   | Purpose                            |
| ---------------- | -------------------------- | ---------------------------------- |
| `search()`       | `GET /v1/search`           | Match team names found in articles |
| `getEventOdds()` | `GET /v1/events/{id}/odds` | Get odds for matched events        |

## How It Works

```
User reads ESPN article about "Barcelona vs Real Madrid"
       ↓
Content script detects team names
       ↓
Background script calls search("Barcelona")
       ↓
Gets odds for the matched event
       ↓
Injects tooltip next to team names showing odds
```

## Step 1: Manifest V3

```json theme={null}
{
  "manifest_version": 3,
  "name": "Sports Odds Overlay",
  "version": "1.0",
  "description": "See live odds while reading sports articles",
  "permissions": ["storage"],
  "host_permissions": [
    "https://api.fieldfunded.com/*"
  ],
  "content_scripts": [
    {
      "matches": [
        "*://*.espn.com/*",
        "*://*.bbc.com/sport/*",
        "*://*.skysports.com/*"
      ],
      "js": ["content.js"],
      "css": ["overlay.css"]
    }
  ],
  "background": {
    "service_worker": "background.js"
  },
  "options_page": "options.html"
}
```

## Step 2: Background Script (API Calls)

```typescript theme={null}
// background.js — handles all API communication
const API_KEY = ''; // Set via options page
const BASE_URL = 'https://api.fieldfunded.com/v1';

// Cache to avoid repeated API calls
const cache = new Map<string, any>();

chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  if (msg.type === 'SEARCH_TEAM') {
    const cached = cache.get(msg.team);
    if (cached) {
      sendResponse(cached);
      return true;
    }

    fetch(`${BASE_URL}/search?q=${encodeURIComponent(msg.team)}&limit=1`, {
      headers: { 'X-API-Key': API_KEY },
    })
      .then(r => r.json())
      .then(async (data) => {
        if (data.events && data.events.length > 0) {
          const event = data.events[0];

          // Fetch odds
          const oddsRes = await fetch(`${BASE_URL}/events/${event.id}/odds`, {
            headers: { 'X-API-Key': API_KEY },
          });
          const odds = await oddsRes.json();

          const result = { event, odds };
          cache.set(msg.team, result);

          // Cache expires after 5 minutes
          setTimeout(() => cache.delete(msg.team), 5 * 60 * 1000);

          sendResponse(result);
        } else {
          sendResponse(null);
        }
      });

    return true; // Async response
  }
});
```

## Step 3: Content Script (Team Detection + Tooltip)

```typescript theme={null}
// content.js — detects team names and injects tooltips

// Common team names to look for
const TEAMS = [
  'Barcelona', 'Real Madrid', 'Manchester United', 'Manchester City',
  'Liverpool', 'Arsenal', 'Chelsea', 'Juventus', 'Bayern Munich',
  'PSG', 'AC Milan', 'Inter Milan', 'Atletico Madrid', 'Dortmund',
  'Lakers', 'Warriors', 'Celtics', 'Patriots', 'Chiefs',
  // Add more or load dynamically from getSports()/getLeagues()
];

function scanForTeams() {
  const textNodes: Node[] = [];
  const walker = document.createTreeWalker(
    document.body,
    NodeFilter.SHOW_TEXT,
    null
  );

  while (walker.nextNode()) {
    textNodes.push(walker.currentNode);
  }

  const processed = new Set<string>();

  for (const node of textNodes) {
    const text = node.textContent || '';
    for (const team of TEAMS) {
      if (text.includes(team) && !processed.has(team)) {
        processed.add(team);

        // Ask background script for odds
        chrome.runtime.sendMessage(
          { type: 'SEARCH_TEAM', team },
          (result) => {
            if (result && result.odds?.markets?.length > 0) {
              injectTooltip(node, team, result);
            }
          }
        );
      }
    }
  }
}

function injectTooltip(textNode: Node, team: string, data: any) {
  const parent = textNode.parentElement;
  if (!parent) return;

  const html = parent.innerHTML;
  const market = data.odds.markets[0];
  const oddsStr = market.selections
    .map((s: any) => `${s.name}: ${s.odds}`)
    .join(' • ');

  const tooltip = `<span class="ff-odds-trigger">${team}<span class="ff-odds-tooltip">
    <strong>${data.event.home_team} vs ${data.event.away_team}</strong><br>
    ${market.name}: ${oddsStr}
  </span></span>`;

  parent.innerHTML = html.replace(team, tooltip);
}

// Run on page load
scanForTeams();
```

## Step 4: Tooltip CSS

```css theme={null}
/* overlay.css */
.ff-odds-trigger {
  position: relative;
  border-bottom: 2px dotted #53FC18;
  cursor: pointer;
}

.ff-odds-tooltip {
  display: none;
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  background: #1a1a2e;
  color: #ffffff;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 13px;
  white-space: nowrap;
  z-index: 10000;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  border: 1px solid #53FC18;
}

.ff-odds-trigger:hover .ff-odds-tooltip {
  display: block;
}
```

## Rate Limit Math

* Each unique team name found = 2 requests (search + odds)
* Aggressive caching (5 min) means repeat visits cost 0
* A typical article mentions 2-4 teams = 4-8 requests per page
* Free tier handles **\~1,000 article reads per month**

<Card title="Search API Reference" icon="magnifying-glass" href="/api-reference/events/search-events">
  See search endpoint docs →
</Card>

***

## Related Guides

* [Build a Discord Bot](/guides/build-discord-bot) — odds via Discord slash commands
* [Build a Twitch Overlay](/guides/build-twitch-overlay) — odds overlay for streams
* [For Startups](/compare/for-startups) — build your MVP for \$0/month

<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>
