Examples · Node.js

Build a Sports Betting Discord Bot With Live Odds (Node.js)

This tutorial builds a Discord bot with an /odds command. Type /odds league:NFL team:Packers and it replies with upcoming games and the best moneyline for each team, plus the book offering it.

Cost: 18 credits per league lookup, cached for 5 minutes. The free plan includes 1,000 credits a month. Full project on GitHub.

1. Fetch and cache the odds

The odds.js module calls ml.odds() for the league's moneylines from sportsbooks only, and reuses the result for 5 minutes. A busy server then costs no more than one call per league every 5 minutes.

2. Find the best price per team

For each game, bestPrice checks every book's price for a team and keeps the highest. The message uses Discord timestamps, so each reader sees the start time in their own time zone.

3. Register the command and start the bot

Create a bot in the Discord Developer Portal, invite it to your server, then run npm run register once to add /odds and npm start to bring the bot online. The bot defers its reply while the odds load, so Discord doesn't time out.

The code

discord-odds-bot/odds.js, straight from the repository.

odds.js
import { MoneyLine } from 'moneyline-sports-api'

const ml = new MoneyLine() // reads MONEYLINE_API_KEY
const CACHE_MS = 5 * 60 * 1000 // one odds call costs 18 credits, so share results for 5 minutes
const cache = new Map()

export const LEAGUES = { nfl: 'NFL', nba: 'NBA', mlb: 'MLB', nhl: 'NHL', ncaa_football: 'College Football', ncaa_basketball: 'College Basketball', soccer_epl: 'Premier League', soccer_mls: 'MLS' }

async function moneylines(league) {
  const hit = cache.get(league)
  if (hit && Date.now() - hit.at < CACHE_MS) return hit.games
  const games = await ml.odds({ league, market: 'moneyline', sourceType: 'sportsbook' })
  cache.set(league, { at: Date.now(), games })
  return games
}

// Best price for one team across every sportsbook.
function bestPrice(game, team) {
  let best = null
  for (const book of game.bookmakers) {
    for (const market of book.markets) {
      const outcome = market.outcomes.find((o) => o.name === team)
      if (outcome && (!best || outcome.price > best.price)) best = { price: outcome.price, book: book.bookmakerName }
    }
  }
  return best
}

const fmt = (p) => (p > 0 ? `+${p}` : String(p))
const side = (game, team) => {
  const best = bestPrice(game, team)
  return best ? `**${team}** ${fmt(best.price)} (${best.book})` : `**${team}** no price yet`
}

// Returns the message text for /odds: the next few games, optionally for one team.
export async function oddsMessage(league, team, limit = 5) {
  const now = Date.now()
  const games = (await moneylines(league))
    .filter((g) => new Date(g.startTime).getTime() > now)
    .filter((g) => !team || `${g.homeTeamName} ${g.awayTeamName}`.toLowerCase().includes(team.toLowerCase()))
    .sort((a, b) => new Date(a.startTime) - new Date(b.startTime))
    .slice(0, limit)
  if (!games.length) return team ? `No upcoming ${LEAGUES[league]} games found for "${team}".` : `No upcoming ${LEAGUES[league]} games are priced right now.`

  const lines = games.map((g) => {
    const when = `<t:${Math.floor(new Date(g.startTime).getTime() / 1000)}:f>` // Discord shows it in each reader's time zone
    return `${when}\n${side(g, g.awayTeamName)} at ${side(g, g.homeTeamName)}`
  })
  return `**Best ${LEAGUES[league]} moneylines**\n\n${lines.join('\n\n')}\n\n-# Odds from MoneyLine Sports API`
}

Run it

Get a free API key, clone the example, then run:

Terminal
npm install
export DISCORD_TOKEN=your-bot-token DISCORD_CLIENT_ID=your-application-id MONEYLINE_API_KEY=your-key
npm run register
npm start

More examples