Skip to article

Sports data article

Sportsbook data feed: REST API vs streaming

In the world of data, everyone wants fast data, and that same logic applies to sports data — whether it’s fixtures, stats or even odds.

The answer to getting your sports data faster is usually through SSE or streaming. RESTful requesting is synonymous with APIs, and most people overlook the whole sector of streaming, despite the fact that it can be a much better fit for data that changes frequently.

Streaming comes with the benefit of having an open connection to the server at all times. Therefore, when the data provider has an update, you can be notified almost immediately rather than waiting until your application makes its next request.

It’s worth mentioning early on that streaming is a more premium option because of that open server connection, which naturally costs more bandwidth and server resources. If you’re price-conscious, REST may still be the better option. Quick plug, but we give hours upon hours of streaming time in our plans!

So what actually is the difference?

The easiest way of looking at it is that REST makes you ask for an update, while streaming gives you the update when it happens. With a normal REST API, your application might request the latest odds every 60 seconds, receive the response, wait another 60 seconds, and then request the same endpoint again.

There’s absolutely nothing wrong with that, and for a lot of sports data it makes perfect sense. If you’re requesting tomorrow’s Premier League fixtures, does it really matter if your application checks every 30 minutes instead of keeping a permanent connection open waiting for Liverpool vs Arsenal to suddenly disappear? Probably not.

Odds are different because odds move, sometimes a lot, and this is where streaming starts to make considerably more sense. Instead of constantly checking whether a price has changed, you can keep the connection open and simply receive the change when it happens.

REST vs streaming with actual data

Rather than just saying “streaming is faster”, we can look at the actual scale of a sports odds feed and why constantly polling it becomes inefficient.

Across Odds API, our current coverage consists of:

  • 123 bookmakers
  • 10 sports
  • 174 leagues
  • 21,062 recently observed bookmaker, sport, league and market combinations

That last number is probably the most important one. Once you start dealing with tens of thousands of different market combinations, constantly asking the API whether something has changed can become pretty inefficient.

Let’s say you’re following 10 events and requesting each one every minute. That’s 600 requests an hour and 14,400 requests every day, and a large number of those requests could return exactly the same data you already received one minute earlier.

Streaming flips that around. You take the current data once, open your connection, and from that point forward you mainly deal with changes. If Bookmaker A moves from $2.10 to $2.15, you receive the change. If Bookmaker B hasn’t changed anything, there is nothing new to send.

That becomes a much cleaner model when you actually care about the movement of the data rather than occasionally checking what the data looks like.

But you shouldn't completely replace REST

This is probably one of the biggest mistakes people make when they first start looking at streaming. Streaming shouldn’t really replace REST because the two approaches solve different parts of the same problem.

The cleanest implementation is:

REST snapshot → open stream → receive changes

You first request the entire current state of an event. This gives you the bookmakers, markets, odds, timestamps and everything else you need to establish what the event currently looks like.

Once you have that state, you open either an SSE or WebSocket connection and start applying changes to the original snapshot. If something goes wrong later, you simply request another snapshot and begin again.

This is considerably safer than opening a stream and hoping you can somehow reconstruct the entire event from whichever updates happen to arrive after you connect.

For example, our event snapshot endpoint returns a resume value. Your application can store that value alongside the snapshot and then start the streaming connection from that point, which means there is a clear relationship between the data you initially loaded and the updates that arrive afterwards.

SSE vs WebSockets

This is another area where people tend to make things sound considerably more complicated than they actually are. Both SSE and WebSockets allow the server to keep a connection open and send updates to you, but the communication model is slightly different.

SSE is mainly designed for server-to-client communication. Your application connects to the server, and the server continues sending events down that connection as they happen.

WebSockets allow communication in both directions. The client and server can continuously send messages to each other over the same connection.

For something like an odds feed, your application generally isn’t having some deep two-way conversation with the server. Most of the time you’re effectively saying, “Give me updates for this event,” and then waiting for the server to send them.

Because of that, SSE is actually an extremely good fit. WebSockets are still useful, particularly if your existing infrastructure already relies heavily on them, but don’t assume WebSockets automatically means faster.

In our case, SSE and WebSocket connections receive the same underlying odds updates. Choosing WebSocket doesn’t magically make the bookmaker produce the price update any faster.

Question REST SSE WebSocket
How updates arrive Your application asks The server sends updates to the client The client and server can both send
Best fit Snapshots and colder data One-way sports odds updates Existing WebSocket infrastructure
Connection Request, response, finished Open HTTP connection Open two-way connection
Odds API billing API requests Stream-hours Stream-hours

How fast is streaming actually?

Now we get to the interesting part. We monitor the performance of our REST API and our update streams separately, which gives us a useful look at how the different delivery methods behave in production.

During one five-minute production monitoring window on August 18, our REST API recorded 100% uptime, a 0% error rate, 339.3ms P50 response latency and 1,960.8ms P95 response latency.

During that same style of monitoring, our update streams recorded 100% uptime, a 0% error rate, 100ms P50 latency and 468.8ms P95 latency.

Delivery method Uptime Error rate P50 P95
REST API 100% 0% 339.3ms 1,960.8ms
Update streams 100% 0% 100ms 468.8ms

For anyone who doesn’t spend their life looking at latency percentiles, P50 basically means half of the measured responses were faster than that number. P95 means 95% were faster than that number, so it gives you a better idea of what latency looks like towards the slower end of normal traffic.

Those numbers shouldn't be interpreted as a perfect head-to-head laboratory benchmark because REST and streaming are measured differently, and they represent an operational snapshot rather than some guaranteed SLA. They are still useful because they show exactly why maintaining an already-open connection can dramatically reduce the amount of time spent establishing requests and waiting for responses.

There is one catch

Streaming cannot give you data that your data provider doesn’t have yet, and this distinction gets overlooked a lot.

I sometimes see streaming described as though opening a WebSocket somehow means every bookmaker price is instantly teleported from the bookmaker into your application. That isn’t how it works.

There are really two different types of speed involved in a sports data feed: source freshness and delivery latency.

Source freshness is how recently the sports data provider actually collected the bookmaker price. Delivery latency is how quickly that provider sends the information to you once the change has entered its system.

Streaming massively improves the second measurement because your application no longer has to wait until its next scheduled REST request. However, it cannot magically solve the first measurement.

Our odds collection infrastructure, for example, uses a mixed refresh model. Some sources can provide changes through streaming connections, while others are checked using adaptive requests based on things such as how close the event is to starting, league priority, source health, response time and overall system load.

Our fastest published polling target is currently once per minute. Once a change reaches our update system, the streaming connection can then deliver it to the client without the client waiting another minute before asking for it.

That distinction matters a lot when comparing sports data providers. A provider could technically offer WebSockets while still refreshing the underlying bookmaker once every five minutes, which means the transport is fast but the actual source data is not particularly fresh.

What about batching?

Sending a separate network message every single time one tiny field changes can create a ridiculous amount of unnecessary traffic, especially when several bookmakers are moving at roughly the same time.

Because of that, our event streaming system currently coalesces changes for 200 milliseconds before emitting a delta. In other words, if several changes happen extremely close together, they can be bundled into the same update instead of hammering the client with a separate message for every tiny movement.

Two hundred milliseconds is 0.2 seconds, so the system remains extremely responsive while reducing the amount of message churn considerably.

Streams also send heartbeats. Our default heartbeat interval is 15 seconds, but that does not mean your odds only update every 15 seconds. A heartbeat is simply the server confirming that the connection is still alive.

Actual odds changes are sent separately whenever they are available.

Streaming everything would be stupid

Sports data doesn’t all have the same urgency, and this is where people can very easily waste money.

You absolutely do not need a permanent connection open to your list of supported sports. Your bookmaker list doesn't need to be streamed either, and most fixture lists don’t need second-by-second updates.

Even plenty of odds don’t necessarily need streaming. If you’re building a simple comparison page and being 60 seconds behind is perfectly acceptable, polling the event every minute may be much simpler and considerably cheaper.

A reasonable starting setup might be checking sports, leagues and bookmaker coverage once a day. Event lists might be refreshed every 15 to 60 minutes, while events getting close to their start time might be refreshed every few minutes.

General odds that aren’t especially important could be requested through REST every 60 to 120 seconds. Important pre-match events that your customers are actively watching could instead be loaded once and then streamed.

Data Starting cadence
Sports, leagues and bookmaker coverageOnce a day
Event listsEvery 15 to 60 minutes
Events close to their start timeEvery few minutes
General odds through RESTEvery 60 to 120 seconds
Important pre-match eventsLoaded once and then streamed

This is what I’d call a hot-and-cold approach. The important stuff is hot, so you stream it. Everything else is cold, so you request it occasionally.

You end up getting most of the benefits of a streaming architecture without maintaining hundreds or thousands of pointless open connections.

Streaming does cost more

Remember what I mentioned at the start? Keeping a connection open costs resources.

With REST, the server receives your request, produces the response, sends it back and the interaction is finished. With streaming, the server has to maintain your connection for as long as you want to continue receiving updates.

That’s why our streaming usage is measured in stream-hours rather than normal API requests.

At the moment, our public plans include:

  • Starter — $40/month — 50,000 API requests + 150 stream-hours
  • Builder — $90/month — 2,000,000 API requests + 1,500 stream-hours
  • Live — $250/month — 20,000,000 API requests + 6,000 stream-hours
  • Pro — $500/month — 75,000,000 API requests + 20,000 stream-hours

A stream-hour is fairly literal. If you keep one event stream open for six hours, that consumes six stream-hours. If you keep 10 event streams open for those same six hours, that consumes 60 stream-hours.

This is why you probably shouldn't open 500 streams and leave them running for a month because you felt like it.

If your customer is currently looking at three events, stream those three. When they leave those events, close the connections. If you’re running a betting model, stream the events that actually matter to that model and use REST for everything else.

What happens when the stream disconnects?

It will disconnect eventually. Networks fail, servers restart, deployments happen, laptops go to sleep and proxies sometimes decide that your connection has existed for long enough.

A good streaming integration needs to assume that disconnects are completely normal.

This is another reason why REST remains important. Your application should always have a recovery path back to a complete snapshot rather than relying entirely on the stream.

With our implementation, the basic process is straightforward. You request the snapshot, store the returned resume token, open the stream using that token and continue saving newer resume values as updates arrive.

If the connection drops, you can reconnect using the latest resume value. If the server determines that your resume position is too old and some updates are no longer available, it can tell your application to resynchronise.

At that point, you simply fetch another complete snapshot and start again.

It might sound like extra complexity, but this is actually what makes streaming reliable. The dangerous system is the one that assumes every update will arrive perfectly forever.

Streaming also reduces wasted requests

The speed difference gets most of the attention, but one of the less obvious advantages of streaming is simply how much unnecessary requesting it can remove.

Imagine you are monitoring a market where the price changes once every five minutes. If you poll that event every second because you desperately want to see the next movement immediately, you could send roughly 300 requests between two actual changes.

The vast majority of those requests return absolutely nothing new.

With a stream, the connection can simply sit there. When there is nothing to tell you, almost nothing needs to happen beyond connection maintenance and heartbeats. When something changes, you receive it.

This becomes increasingly important as your application scales because polling one event every second isn't particularly dramatic. Polling thousands of events every second is a completely different infrastructure problem.

Streaming is particularly useful for odds

There are sports data categories where streaming provides very little practical benefit, but odds are one of the strongest use cases because the value of the data can depend heavily on how quickly you receive the change.

If you’re running an odds comparison application, you want the displayed prices to represent what is actually available as closely as possible.

If you’re running an alerting product, sending an alert based on a price that changed 45 seconds ago isn't particularly useful.

If you’re running a pricing model, outdated inputs can produce outdated outputs.

If you’re identifying arbitrage opportunities, receiving one side of a price movement significantly later than another can create an opportunity that technically no longer exists.

This is exactly where streaming earns its place. It isn't necessary because the technology sounds impressive; it is useful because the underlying data has a short shelf life.

The snapshot-and-stream pattern

If there is one thing I would recommend taking from this article, it is that you should not think about REST and streaming as competitors.

The most reliable design uses both.

You start with REST because you need to know the full current state. That gives your application a clean baseline containing the event, markets, bookmakers and odds that exist right now.

Once that snapshot is stored, you connect to the stream and begin applying smaller changes as they happen.

Your local state might start with Sportsbet at $2.00 and Ladbrokes at $2.05. A streaming delta then arrives telling you Sportsbet has moved to $2.10.

You don't need the entire event again. Your application already knows everything else. It just changes that single relevant piece of state.

If something eventually goes wrong, request the complete state again.

Snapshot.

Stream.

Resnapshot when necessary.

It is an extremely simple concept once you stop trying to make streaming responsible for absolutely everything.

SSE or WebSocket?

For most people building a one-way sports odds application, I would probably start with SSE unless there is a particular reason not to.

The implementation is generally simpler, it uses normal HTTP infrastructure and it naturally fits a system where the server is continuously sending updates down to the client.

If your backend already has extensive WebSocket infrastructure, then using WebSockets can make complete sense as well.

The important thing is that you shouldn't select WebSockets simply because they sound more advanced. If both transports are connected to exactly the same underlying data source, the underlying bookmaker price does not become any fresher because it travelled through a WebSocket.

Transport matters, but source collection matters just as much.

So which one should you actually use?

If you're building a basic fixture application, research tool or anything where being 60 seconds behind doesn’t really matter, use REST.

If you're building an odds comparison screen, price movement tracker, alerting tool or betting model where receiving changes quickly actually affects the product, use REST plus SSE.

If your entire backend already revolves around WebSockets, use REST plus WebSocket.

If you're processing an enormous amount of sports data, the answer will probably be all of them. Stream the important data, poll the colder data and periodically use REST snapshots to make sure your local state still matches the server.

The real answer isn't REST or streaming. It’s REST and streaming.

Take a snapshot so you know exactly where you are, stream the pieces of data where speed actually matters, and use REST for everything that doesn't justify keeping a connection open.

Most importantly, make sure your application can always get back to a clean snapshot when something inevitably disconnects. Because it will disconnect.

A good sports data integration isn't one that assumes the stream stays connected forever. It’s one that doesn’t care when it doesn’t.