A sports betting bot, done sensibly, is an alerting program. It reads odds on a schedule or from a stream, applies your rules (a price above fair value, a line that moved, an arbitrage across two books), and tells you once when something matches. You decide whether to bet, and you place it yourself.
The other kind, a bot that places bets automatically, can break the terms of the sportsbooks it would bet with. Read each book's terms before you go anywhere near it. ZippyOddsAPI is read-only: it doesn't take bets, place them, or offer bet links. This page is about the alerting kind, which is where nearly all the engineering is anyway.
The loop
Every alerting bot has the same four parts:
- Fetch. Get the current prices or computed rows.
- Filter. Apply your rules and drop anything stale.
- De-duplicate. Decide whether this match is new or one you've already sent.
- Notify. Send it somewhere you'll see it, with enough detail to act or ignore.
The fetch step is where your data source matters most. If you're reading pages yourself, the fetch is most of the work and most of the breakage: our piece on the odds scraper route covers why. With an API, the fetch is one request and the rest is your logic.
Polling or streaming
Polling is a request on a timer. It's simple, it restarts cleanly, and every answer is a complete picture. With ZippyOddsAPI you could poll /v1/ev?league=nba&min_ev=2 and get rows already priced against a no-vig consensus. The cost is easy to work out because each request has a fixed price: /v1/ev is 3 credits whatever the size of the answer. Poll it every 30 seconds and that's 2 requests a minute, 2,880 a day, or 8,640 credits a day for one league. Poll every 2 minutes and it's a quarter of that.
Streaming keeps a WebSocket open at /v1/stream and receives changes as they happen. Deltas go out when a collector's read changes something, so the rate follows how often each venue is read rather than a fixed tick. A stream sends raw market changes, not EV rows, so your bot does its own sums on each delta. It also needs a plan that includes streaming: the pricing page shows which. Handle gaps: every message that matters has a sequence number, and if one is skipped you ask for a fresh snapshot.
Don't expect milliseconds from either. The docs say plainly that prices are polled from each venue, so they're minutes old rather than milliseconds. A rule that only works if you're first to a price won't work.
Rules worth writing
Good rules are narrow and check freshness before value:
- +EV above a threshold. Take rows from
/v1/evwhereev_percentis above your floor andstaleis false. Look atobserved_atandreference_age_secondstoo: an edge measured against an old reference may already be gone. - Line moves. Compare each market against the last value you stored and alert on a move of a set size. Remember that a moved line is a new market, not a changed price.
- Arbitrage.
/v1/arbitragelists two-way opportunities across different price families.
An arbitrage betting bot sounds like free money and isn't. Both legs have to be placed at the listed prices, and either can move or be refused between your first bet and your second. That's execution risk, and it's real. A book can also limit an account that keeps taking its weakest prices.
De-duplicating alerts
A bot that pings you every 30 seconds about the same price gets muted within a day. Give each alert a key built from what makes it the same opportunity:
key = event_id + book + market + period + line + side + player_id
All of those are fields on a /v1/ev row. Then keep a small table of keys you've sent, and alert again only when:
- the key is new,
- the edge has improved by more than a set margin since the last alert, or
- the key dropped out of the results and came back.
Expire keys once the game starts. The line is in the key on purpose: a move from 224.5 to 225.5 is a different bet and deserves its own alert.
Rate limits and failure
Rate limits are per account, and all of an account's keys share them. ZippyOddsAPI sends X-RateLimit-Remaining and X-RateLimit-Reset (seconds until the window ends) on each answer that passes the rate check. Over the limit you get 429 rate_limited with a Retry-After header. Wait that many seconds, and don't retry in a tight loop. Refused requests cost no credits, but they don't get you data either.
Branch on the error's code, not its message. insufficient_credits means stop polling, not retry harder. plan_feature_unavailable means the endpoint isn't on your plan. The API docs list every code.
Log every alert you send with the price and its observation time. When you later ask whether the bot was any good, that log is your evidence, and closing line value on the alerted prices is the measure to check it against.
Where AI fits
A sports betting ai bot is usually one of two things. Either it's a predictive model that produces probabilities, which then need prices to be useful, or it's a language model wrapped around odds data to summarize or explain alerts. The second is fine for presentation. A language model doesn't know current prices unless you hand them over, and it shouldn't be the thing deciding an edge. Keep the maths in code where you can test it.
Real money, real risk
Whatever the bot finds, a bet is still a bet. A genuine edge loses often, sometimes for long stretches, and only shows up as an average over many bets. Stake only money you can afford to lose, and check that betting is legal where you are.
If you're turning the bot into something other people use, the guide to sports betting app development covers the parts around it. For what an API returns before you write a line, start with what a sports betting API gives you.