How to Build a Betting App: Odds Data, Architecture and Compliance
A practical guide to building an odds comparison or bet tracking app, not a sportsbook. The data you need, a stack and architecture that keep costs flat as users grow, live updates, deep links into sportsbooks, grading, costs and compliance basics.
This guide covers how to build a betting app in the sense most developers mean it: an app that shows odds from several sportsbooks, tracks a user's bets, sends alerts and links out to the books where the bets are placed. It walks through the data you need, an architecture that keeps your data bill flat as users grow, live updates, deep links into sportsbooks, grading, costs and the compliance basics.
It does not cover building a sportsbook, an app that accepts wagers. That is a licensed, regulated business with payments, identity checks and risk management, and it is a different project entirely.
Odds app or sportsbook: decide first
The line between the two matters for everything that follows:
- An odds or tracking app displays prices, lets users record bets they placed elsewhere, and links to licensed sportsbooks. It never accepts a wager, holds a user's money or pays out winnings.
- A sportsbook takes wagers. It needs gaming licenses in every jurisdiction it operates in, payment processing, identity and location checks, and a trading operation that sets and manages prices.
PreciseAPI is a data provider for the first kind. It sells access to odds data only: no wagering, no picks, no funds. If you are building a sportsbook, you need a licensed platform and legal counsel first, and odds data comes much later.
The data a betting app needs
Most betting app development work sits around a small set of data needs. Here is how they map to endpoints and what each request costs:
| App feature | Endpoint | Credits per request |
|---|---|---|
| Schedules and game cards | /v1/events | 1 |
| Odds on every game | /v1/odds | 2 |
| Best price per outcome | /v1/best-lines | 3 |
| Player props | /v1/props | 3 |
| Futures | /v1/futures | 2 |
| Line movement charts | /v1/history | 3 |
| Live scores and grading | /v1/scores | 1 |
| Book list, sites and link support | /v1/sportsbooks | 0 |
| Live updates | /v1/stream (WebSocket) | 1 per connection-minute |
Every one of these uses the same event ids, so a game card keyed on an event id gets its schedule, prices, score and final result without stitching providers together. Events, odds and scores requests cover a whole league, or several at once, rather than a single game.
Good odds data for apps is also filtered data. Users only care about books they can use, so map each user's region to a list of source ids and pass it as books=. Keep a sharp book such as Pinnacle in your backend requests even if you don't display it; it is the reference for judging whether a price is genuinely good.
How to build a betting app: the architecture
The single most important rule: your app never calls the odds API directly. It calls your backend, and your backend calls the API.
iOS / Android / web client ──▶ your backend (auth, cache, user data) ──▶ PreciseAPI (REST, WebSocket)
│
└──▶ database (users, tracked bets, alert settings)
This does three things. It keeps your API key on a server, where nobody can copy it out of an app bundle. It lets one backend request serve every user who opens the same league, so your data cost scales with how often you refresh, not with how many people use the app. And it lets you shape responses for your client: a game card needs a few prices, not every market from every book.
A caching proxy in Node.js
A minimal version of that backend, with a 30-second cache per request and the last good copy served if the API is unreachable:
import express from 'express';
const API = 'https://api.preciseapi.com/v1';
const LEAGUES = new Set(['nfl', 'nba', 'mlb', 'nhl', 'ncaaf', 'ncaab']);
const cache = new Map(); // url -> { at, body }
async function cached(path, params, ttlMs) {
const url = `${API}${path}?${new URLSearchParams(params)}`;
const hit = cache.get(url);
if (hit && Date.now() - hit.at < ttlMs) return hit.body;
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.PRECISEAPI_KEY}` } });
if (!res.ok) {
if (hit) return hit.body; // keep serving the last good copy
throw new Error(`PreciseAPI ${res.status}`);
}
const body = await res.json();
cache.set(url, { at: Date.now(), body });
return body;
}
const app = express();
app.get('/api/games/:league', async (req, res) => {
if (!LEAGUES.has(req.params.league)) return res.sendStatus(404);
try {
const odds = await cached('/odds', {
league: req.params.league,
markets: 'moneyline,spread,total',
books: 'draftkings,fanduel,betmgm,caesars,pinnacle',
}, 30_000);
// Shape it for the client: one card per game, prices per book.
res.json(odds.data.map((ev) => ({
id: ev.id,
start: ev.start_time,
status: ev.status,
home: ev.home.abbr,
away: ev.away.abbr,
books: ev.bookmakers.map((b) => ({
id: b.id,
markets: b.markets.map((m) => ({ key: m.key, outcomes: m.outcomes.map((o) => ({ id: o.id, side: o.side, point: o.point, price: o.price })) })),
})),
})));
} catch (err) {
res.status(502).json({ error: 'odds unavailable' });
}
});
app.listen(3000);
In production, put the cache in Redis so several backend instances share it, and make concurrent misses for the same URL wait on one request instead of each calling the API.
Live updates
On the Edge and Sharp plans, odds are pre-game: refresh the cache every 30 to 60 seconds and games drop out of odds responses once they start, while their scores keep updating. For prices during games, Syndicate adds live in-play odds and the WebSocket stream. Hold one stream connection in your backend, apply each delta to your copy of the board, and push changes to clients over your own WebSocket or Server-Sent Events channel. The live odds API page and the streaming docs cover the snapshot, sequence numbers and resync.
A sensible stack
There is no single right stack for how to build a betting app, because the data layer is plain HTTPS and JSON. A common shape for a sports betting app API backend:
- Client: React Native or Flutter for mobile, or a web app with Next.js.
- Backend: Node.js or Python, with the official SDKs listed in the SDK docs or plain HTTP.
- Database: Postgres for users, tracked bets and alert preferences.
- Cache and fan-out: Redis for the shared odds cache and pub/sub to your client connections.
- Jobs: a scheduler for refreshes, grading and push notifications.
Store prices as they were when a user tracked a bet. Odds change constantly, and "what price did I get?" is the question a tracker exists to answer.
Deep links into sportsbooks
A betting app's main action is sending the user to a book with the right bet ready. Markets and outcomes can carry the source's own ids and ready-made links, documented in deep links:
betslip_urlon an outcome opens the book or app with that side on the slip.event_urlon a market or bookmaker block opens the book's page for the game.league_urlon the bookmaker block opens the book's board for the league.urlfrom/v1/sportsbooksis the book's site, the last fallback.
// The best available link for one outcome, in the order the docs recommend.
function betLink(outcome, market, bookmaker, sites) {
return outcome.betslip_url || market.event_url || bookmaker.event_url || bookmaker.league_url || sites[bookmaker.id]?.url || null;
}
Pass your user's two-letter state as state= so Caesars and BetParx links open that state's site. Treat every link field as optional, and re-read the price before you send someone to a book, because it may have moved.
Tracking and grading bets
/v1/scores (1 credit) returns live and final games with score and clock, on the same event ids as the odds. When an event is final, grade the tracked bets on it. For two-way full-game markets the logic is short:
// bet: { market: 'moneyline' | 'spread' | 'total', side: 'home' | 'away' | 'over' | 'under', point }
function grade(bet, event) {
if (event.status !== 'final' || !event.score) return 'pending';
const { home, away } = event.score;
let margin;
if (bet.market === 'total') {
margin = bet.side === 'over' ? home + away - bet.point : bet.point - (home + away);
} else {
margin = (bet.side === 'home' ? home - away : away - home) + (bet.market === 'spread' ? bet.point : 0);
}
return margin > 0 ? 'won' : margin < 0 ? 'lost' : 'push';
}
Three-way soccer markets add a draw side, and house rules on ties and voids vary by book, so show the result as a suggestion the user can correct. /v1/history gives you the closing price to report each bet's closing line value.
What it costs
Because the backend caches, your API cost depends on refresh rate and features, not on user count:
| Backend refresh plan | Credits per 30 days | Fits |
|---|---|---|
/v1/odds for four leagues in one call, every 60 s | 86,400 | Edge |
The same, plus /v1/scores every 60 s | 129,600 | Edge |
/v1/odds and /v1/scores every 30 s | 259,200 | Sharp (unlimited) |
| Live in-play prices over WebSocket | Unlimited plan | Syndicate |
Edge is $49.99 a month with 150,000 credits; Sharp is $199.99 with unlimited credits, pre-game only; Syndicate is $249.99 with unlimited credits, in-play odds and the stream. Compare them on the odds API pricing page.
Compliance basics
Compliance is the part of how to build a betting app that teams most often leave until the end. This is an overview, not legal advice: talk to a lawyer who knows gaming law in the markets you launch in.
- Don't take wagers. Make it clear in the product that bets are placed with the sportsbook, and never hold user funds.
- Responsible gaming. Age-gate at the legal betting age where your users are, link to problem gambling help resources, and give users ways to set limits on notifications and alerts. Our responsible gaming page lists the principles we follow.
- No guarantees. Don't describe prices, edges or alerts as sure wins. Odds data is information, not advice.
- Advertising and affiliates. If you earn money from sportsbook links, state rules on gambling advertising apply, and some states require marketing affiliates to be licensed or registered.
- App stores. Apple and Google have their own policies for gambling-related apps. Read them before you design features around them.
- Data license. Commercial use is allowed on any paid PreciseAPI plan; redistributing the raw data as a competing feed is not. The terms have the details.
Launch checklist
- API key on the server only, with a monthly credit cap per key from the key controls.
- One shared cache per league and refresh, not one API call per user.
- Stale prices hidden or greyed out using
updated_at; suspended markets skipped. - Every link optional, with a fallback.
- Responsible gaming copy, age gate and help links in place.
For the product side of this pattern, see our odds API for betting apps page and the odds comparison API page.
Frequently asked questions
Do I need a license to build a betting app?
An app that only shows odds and links to licensed sportsbooks doesn't take wagers, which is what sportsbook licenses cover. Advertising and affiliate rules can still apply in some states, and a sportsbook needs gaming licenses everywhere it operates. Get legal advice for your markets.
Can my mobile app call the odds API directly?
It shouldn't. A key inside an app bundle can be extracted and used by anyone. Call the API from your backend, cache shared responses, and serve your app from your own endpoints.
How do I show live odds in a betting app?
Live in-play odds and the WebSocket stream are on the Syndicate plan. Hold one stream connection in your backend, apply deltas to your copy of the board, and push changes to your clients. On Edge and Sharp, refresh pre-game odds every 30 to 60 seconds.
Can users place bets inside my app?
Not through PreciseAPI, which provides data only. Deep links (betslip_url, event_url, league_url) send users to the sportsbook's own site or app, where the bet is placed and the book's own checks apply.
Which plan does a betting app need?
A pre-game odds and tracking app usually fits Edge (150,000 credits) or Sharp (unlimited credits), depending on refresh rate. If you need prices during games, you need Syndicate for in-play odds and the stream.