Display live sports odds and scores in FlutterFlow by fetching from the Odds API (the-odds-api.com) via a scheduled Cloud Function that writes to Firestore every 15 minutes. For live scores, use a second Cloud Function on a 30-second schedule. FlutterFlow reads from Firestore in real time. Build the bet slip as App State — an array of selections with stake and odds. This covers the DATA DISPLAY side only — real money wagering requires a gambling license in each jurisdiction.
Live odds display and bet slip UI via Odds API, Cloud Functions, and Firestore
Sports betting apps in FlutterFlow follow the same data architecture as stock market apps: third-party APIs with rate limits and paid tiers supply the data, Cloud Functions fetch and cache it in Firestore, and FlutterFlow reads from Firestore via real-time listeners. The Odds API (the-odds-api.com) provides odds from 40+ bookmakers across major sports. Live scores come from a separate polling Cloud Function. The FlutterFlow UI handles event display, odds format conversion (decimal/fractional/American), and a bet slip with payout calculation. This tutorial covers the data integration and UI — not the financial transaction layer, which requires a regulated gambling license.
Prerequisites
- A Firebase project connected to FlutterFlow with Blaze plan enabled for Cloud Functions
- A free Odds API key from the-odds-api.com (500 free requests per month)
- Basic understanding of Firebase Cloud Functions and Firestore
- Awareness of gambling regulations in your target jurisdiction before building any real-money features
Step-by-step guide
Set up the Odds API and Firestore schema for sports events
Set up the Odds API and Firestore schema for sports events
Register at the-odds-api.com for a free API key (500 requests/month, enough for development). In Firestore, create a sports_events collection with fields: eventId (String), sport (String), homeTeam (String), awayTeam (String), commenceTime (Timestamp), status ('upcoming' | 'live' | 'completed'), homeScore (Integer), awayScore (Integer), lastUpdated (Timestamp). Create a sub-collection odds under each event document with fields: bookmaker (String), market ('h2h' for match winner), homeOdds (Double), awayOdds (Double), drawOdds (Double, nullable). Also create a sports_config document in Firestore at config/sports with a watchedSports array: ['americanfootball_nfl', 'basketball_nba', 'soccer_epl', 'baseball_mlb'].
Expected result: Firestore schema is set up with sports_events collection and odds sub-collections ready to receive data from the Cloud Function.
Build the scheduled Cloud Function to fetch odds from the Odds API
Build the scheduled Cloud Function to fetch odds from the Odds API
Create a Firebase Cloud Function named fetchSportsOdds using the scheduler. Set schedule to 'every 15 minutes'. Store the Odds API key in Firebase Secret Manager. The function reads the watchedSports array from Firestore config/sports, then for each sport calls the Odds API endpoint: https://api.the-odds-api.com/v4/sports/{sport}/odds/?apiKey={KEY}®ions=us&markets=h2h&oddsFormat=decimal. Parse the response array of event objects. For each event, write to Firestore sports_events/{eventId} and write each bookmaker's odds to the odds sub-collection. Log the remaining API quota from the X-RateLimit-Requests-Remaining response header to monitor usage. Deploy the function and verify Firestore is populated after the first scheduled run.
1const { onSchedule } = require('firebase-functions/v2/scheduler');2const { defineSecret } = require('firebase-functions/params');3const admin = require('firebase-admin');4const axios = require('axios');56if (!admin.apps.length) admin.initializeApp();7const oddsApiKey = defineSecret('ODDS_API_KEY');89exports.fetchSportsOdds = onSchedule(10 { schedule: 'every 15 minutes', secrets: [oddsApiKey] },11 async () => {12 const db = admin.firestore();13 const config = await db.doc('config/sports').get();14 const sports = config.data()?.watchedSports || ['americanfootball_nfl'];1516 for (const sport of sports) {17 const url = `https://api.the-odds-api.com/v4/sports/${sport}/odds/` +18 `?apiKey=${oddsApiKey.value()}®ions=us&markets=h2h&oddsFormat=decimal`;19 const { data, headers } = await axios.get(url);20 console.log(`Remaining quota: ${headers['x-ratelimit-requests-remaining']}`);2122 const batch = db.batch();23 for (const event of data) {24 const eventRef = db.collection('sports_events').doc(event.id);25 batch.set(eventRef, {26 eventId: event.id,27 sport: event.sport_key,28 homeTeam: event.home_team,29 awayTeam: event.away_team,30 commenceTime: admin.firestore.Timestamp.fromDate(new Date(event.commence_time)),31 status: 'upcoming',32 lastUpdated: admin.firestore.FieldValue.serverTimestamp(),33 }, { merge: true });3435 for (const bm of event.bookmakers || []) {36 const h2h = bm.markets?.find(m => m.key === 'h2h');37 if (!h2h) continue;38 const oddsRef = eventRef.collection('odds').doc(bm.key);39 const outcomes = Object.fromEntries(h2h.outcomes.map(o => [o.name, o.price]));40 batch.set(oddsRef, {41 bookmaker: bm.title,42 homeOdds: outcomes[event.home_team] || null,43 awayOdds: outcomes[event.away_team] || null,44 updatedAt: admin.firestore.FieldValue.serverTimestamp(),45 });46 }47 }48 await batch.commit();49 }50 }51);Expected result: Cloud Function runs every 15 minutes and populates Firestore with upcoming sports events and their odds from multiple bookmakers.
Build the events ListView with sport selector and odds display
Build the events ListView with sport selector and odds display
Create an EventsPage in FlutterFlow. Add ChoiceChips at the top bound to a Page State sportFilter variable: NFL / NBA / Premier League / MLB. Below, add a Backend Query (real-time) querying sports_events where sport equals sportFilter and status equals 'upcoming', ordered by commenceTime. Add a ListView bound to the query. Each item is a Container (Card style) with: a Row for team names (homeTeam vs awayTeam), a Row for odds from a nested Backend Query on the odds sub-collection filtered to one bookmaker (e.g., 'draftkings'). For each odds item, show a Column with team name and odds formatted based on a Page State oddsFormat variable ('decimal', 'american', 'fractional'). Use Custom Functions for format conversion. Add a Toggle at the top for odds format switching.
Expected result: Events page shows upcoming sports events with live odds. Switching the sport filter updates the list. Toggling odds format converts between decimal, American, and fractional displays.
Build the bet slip with App State and payout calculation
Build the bet slip with App State and payout calculation
Create an App State variable betSlip (List<JSON>) where each item has: eventId, homeTeam, awayTeam, selection (team name), odds (Double), stake (Double). Add a bet slip icon in the top-right of the EventsPage showing the count of betSlip items as a badge. When the user taps an odds button, add an Action Flow: check if that eventId is already in betSlip → if yes, remove it (toggle off) → if no, add a new betSlip item with the selection and odds. Navigate to a BetSlipPage to review selections. On BetSlipPage: ListView of betSlip items, each with a TextField for stake amount. Create a Custom Function calculatePayout that takes stake and odds and returns stake * odds formatted as currency. Show total potential payout at the bottom by summing each selection's payout. Add a Place Bet button that (for display-only) shows a success dialog.
Expected result: Users can tap odds to add selections to the bet slip. The bet slip shows each selection with stake input and calculates potential payout automatically.
Add live score polling with a high-frequency Cloud Function
Add live score polling with a high-frequency Cloud Function
Create a second Cloud Function named updateLiveScores. Use a 1-minute schedule (Cloud Scheduler minimum — for 30-second updates, deploy two scheduled functions offset by 30 seconds, or use Cloud Tasks). The function calls the Odds API scores endpoint: https://api.the-odds-api.com/v4/sports/{sport}/scores/?apiKey={KEY}&daysFrom=1&dateFormat=iso. This endpoint returns scores for in-progress and recently completed games. For each score entry, update the corresponding Firestore sports_events document: set status to 'live' if in progress, update homeScore and awayScore fields, set status to 'completed' and final scores when completed. In FlutterFlow, the real-time Backend Query listener on the EventsPage updates score displays automatically as Firestore documents change.
Expected result: Live game scores update in the FlutterFlow app within 1-2 minutes of actual score changes. In-progress events show a Live badge.
Complete working example
1// Cloud Function: Fetch odds from The Odds API2const functions = require('firebase-functions');3const admin = require('firebase-admin');4const axios = require('axios');5admin.initializeApp();67exports.fetchSportsOdds = functions.pubsub8 .schedule('every 5 minutes')9 .onRun(async () => {10 const API_KEY = functions.config().odds.api_key;11 const sports = ['americanfootball_nfl', 'basketball_nba', 'soccer_epl'];12 13 for (const sport of sports) {14 const res = await axios.get(15 `https://api.the-odds-api.com/v4/sports/${sport}/odds`,16 { params: { apiKey: API_KEY, regions: 'us', markets: 'h2h,spreads,totals', oddsFormat: 'american' } }17 );18 19 for (const event of res.data) {20 await admin.firestore()21 .collection('sports_events')22 .doc(event.id)23 .set({24 sport: event.sport_key,25 homeTeam: event.home_team,26 awayTeam: event.away_team,27 commenceTime: new Date(event.commence_time),28 bookmakers: event.bookmakers.map(b => ({29 name: b.title,30 markets: b.markets.map(m => ({31 key: m.key,32 outcomes: m.outcomes.map(o => ({33 name: o.name,34 price: o.price,35 point: o.point || null,36 })),37 })),38 })),39 lastUpdated: admin.firestore.FieldValue.serverTimestamp(),40 }, { merge: true });41 }42 }43 console.log('Sports odds updated');44 });4546// Bet slip calculation (FlutterFlow Custom Function)47// Input: List<JSON> betSlipItems [{odds: int, stake: double}]48// Output: {totalStake, potentialPayout}49// For American odds: positive odds = stake * (odds/100), negative = stake * (100/abs(odds))Common mistakes
Why it's a problem: Building real-money betting features (accepting wagers, processing payouts) without a gambling license
How to avoid: This tutorial covers data DISPLAY only: showing odds, displaying scores, and simulating a bet slip UI for entertainment or fantasy purposes. If you want to build a real money wagering product, work with a licensed gambling platform (DraftKings Marketplace, BetConstruct white label, etc.) that provides the licensed backend. Never build the wagering layer yourself without legal counsel and licensing.
Why it's a problem: Calling the Odds API directly from FlutterFlow on every page load
How to avoid: Always use the scheduled Cloud Function + Firestore caching pattern. Fetch odds once per 15 minutes server-side, write to Firestore, and let all client instances read from Firestore's cache. This converts 500 requests/month with 20 users into 500 server-side requests/month regardless of user count.
Why it's a problem: Storing the Odds API key in FlutterFlow's API Manager headers or app code
How to avoid: Store the Odds API key exclusively in Firebase Secret Manager and access it only from Cloud Functions. FlutterFlow never calls the Odds API directly — all calls go through your Cloud Function which adds the key server-side.
Best practices
- Monitor the X-RateLimit-Requests-Remaining header in your Cloud Function logs and set up a Firebase alert when it drops below 50 — prevents data outages when quota is nearly exhausted
- Cache the bet slip in Firestore (not just App State) so users do not lose their selections if they close the app before placing
- Show event odds from at least 3 bookmakers side-by-side for comparison — this is the primary value proposition of odds display apps over single-bookmaker sites
- Add a market status indicator: when an event's commenceTime has passed and no live scores are available, show 'Odds unavailable' rather than stale data
- Use Decimal odds as your internal data format and convert to American or Fractional for display — decimal is the simplest format for payout calculations (payout = stake × decimal odds)
- Include a clear disclaimer in your app that it is for entertainment purposes only if you are not a licensed gambling operator — this is both a legal protection and an App Store requirement
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building a FlutterFlow sports data app. Write me: (1) A Firebase Cloud Function that fetches live sports odds from the Odds API v4 for NFL and NBA events and stores them in Firestore, (2) How to convert decimal odds to American odds format in a Flutter Custom Function, (3) How to build an App State-based bet slip that accumulates selections and calculates potential payout, and (4) A second Cloud Function that polls live scores and updates Firestore event documents.
Create a sports events page with ChoiceChips for sport selection (NFL, NBA, EPL) and a ListView of upcoming events. Each event card shows both teams, the match time, and three odds buttons (home win, draw, away win) with decimal odds. Tapping an odds button should add the selection to App State betSlip and highlight the button in green.
Frequently asked questions
Which sports does the Odds API support?
The Odds API covers 80+ sports including NFL, NBA, MLB, NHL, Premier League, La Liga, Serie A, Bundesliga, Champions League, UFC, boxing, tennis (ATP/WTA), golf, cricket, and more. Each sport has a unique sport_key identifier. Check the full list at the-odds-api.com/liveapi/guides/v4/#sport-keys. You can also call the /sports endpoint to get the current list of available sports and their keys.
How do I convert decimal odds to American odds format in FlutterFlow?
Create a Custom Function in FlutterFlow named convertToAmerican that takes a decimal odds Double. If decimal >= 2.0: American odds = (decimal - 1) * 100, formatted as +150. If decimal < 2.0: American odds = -100 / (decimal - 1), formatted as -200. For fractional odds: decimal = (numerator / denominator) + 1, so fractional = decimal - 1 expressed as a simplified fraction. Use the intl package for number formatting.
Can I build a legal sports betting app using FlutterFlow?
You can build the user interface and data integration layers of a sports betting app in FlutterFlow. The legal gambling layer — accepting wagers, holding funds, processing payouts — requires a gambling license from each jurisdiction you operate in. In the US, you need a license in each state separately (Nevada, New Jersey, Pennsylvania, etc.). The simplest legal path is to white-label a licensed betting platform's frontend and connect to their licensed backend API, rather than building the wagering backend yourself.
How do I handle events that have already started (in-play odds)?
The Odds API provides in-play odds only on certain paid plans (Professional tier and above, starting at $79/month). For the free tier, odds are only available for upcoming events. To display live scores during in-play events, use the scores endpoint which is available on paid tiers. For the free tier, mark events as live based on commenceTime passing and show the last available pre-game odds with a 'Pre-game odds' label.
How often do sports odds change and how does that affect my Firestore data?
Odds can change significantly in the hours before an event (based on betting volume, injury news, and weather). For pre-game odds, updating every 15 minutes is sufficient and stays within free API quotas. For in-play odds, updates happen every 30-60 seconds — this requires a paid Odds API plan and a more frequent Cloud Function schedule (or WebSocket connection on higher-tier plans).
What compliance steps do I need for an App Store submission with betting content?
Apple App Store requires gambling apps to have a valid gambling license for each country they operate in, or to be offered only in countries where gambling is legal and regulated. You must submit your license documentation during App Store review. For entertainment or fantasy sports apps with no real-money gambling, use a clear disclaimer in app description and UI. Google Play has similar requirements under its gambling policy. Both stores can reject or remove apps that simulate real-money wagering without the appropriate license paperwork.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation