You are building something that needs live data. A scoreboard, a chat box, a price ticker, a delivery tracker. You search "how to get real-time data" and two terms keep fighting for your attention: REST API and WebSocket.
Every article explains them with a wall of jargon. This one will not. By the end you will know exactly what each does, why they behave differently, how to write both, and how to decide which one your project needs. You will also build a small project that pushes live updates to a web page without exposing your API key.
What you will learn:
What REST and WebSocket are, using simple analogies
How a request travels in each one
Side-by-side code in Python and JavaScript
Why polling wastes requests and adds delay (with real maths)
How to handle reconnects, partial updates and heartbeats
When to use REST, when to use WebSocket, and when to use both
How webhooks and Server-Sent Events fit in
How to build a live relay server for a browser page
What you need: Node.js 18+, optionally Python 3.9+, and a free API key. For practice data we will use a sports API, because sports data is easy to understand and changes constantly, which makes the difference between the two styles easy to see.
- The 30-second answer REST: you ask, the server answers. Every piece of data needs a new request. WebSocket: you open a line, the server talks whenever it has news. One connection, many messages.
REST for pages. WebSocket for live screens.
That one line is correct about 90% of the time. The rest of this article explains the other 10% and shows you the code.
- Two analogies that make it click The restaurant vs the phone call
REST is ordering at a restaurant. You call the waiter, ask for something, get it, and the conversation ends. If you want something else, you call the waiter again. If you want to know whether the kitchen has finished your dish, you have to keep asking: "Is it ready? Is it ready now?"
WebSocket is a phone call. You dial once, the line stays open, and either side can speak at any moment. The kitchen simply tells you "your dish is ready" the second it is.
The mailbox vs the group chat
REST is checking your mailbox. Nothing arrives unless you go and look.
WebSocket is a chat window. Messages appear on their own.
Both are correct tools. A mailbox is perfect for a bank statement. A chat is perfect for a conversation. Using the wrong one is where problems begin.
- Beginner vocabulary (read once, save hours) Term Meaning HTTP The protocol behind most web requests Request / response A question and its single answer Endpoint A URL that returns specific data, like /v1/football/live REST A style of API built on HTTP requests to URLs WebSocket A persistent two-way connection, written ws:// or wss:// Polling Asking again and again on a timer Push The server sends data without being asked Latency The delay between an event and you seeing it Payload The data inside a message JSON The text format most APIs use for data Handshake The first exchange that opens a WebSocket connection Heartbeat / ping A tiny message that proves the connection is still alive Backoff Waiting longer after each failed retry
If you later meet sports terms like xG, the glossary explains them in plain language.
- How a REST request works Your code sends a request: method, URL, headers. The server processes it. The server sends one response with a status code and (usually) JSON. The connection's job is done. Client ──GET /v1/football/live──▶ Server Client ◀──200 OK + JSON ────────── Server (done. Want fresh data? Ask again.)
A real-looking request:
GET https://api.orbistats.com/v1/football/live
Authorization: Bearer your_key_here
And the answer:
json
{
"match_id": "match_50231",
"status": "live",
"minute": 72,
"home": { "name": "Manchester City", "score": 2 },
"away": { "name": "Arsenal", "score": 1 }
}
That shape follows the live-score example on the Orbistats Live Scores API page.
Key traits of REST:
Stateless. Each request stands alone. The server does not remember your last one.
Cacheable. The same answer can be reused, which saves time and requests.
Simple. Works in a browser, curl, any language, any tool.
Client-driven. The server can never speak first.
- How a WebSocket works Your code opens a connection with a handshake (it starts as HTTP, then upgrades). The connection stays open. Either side can send a message at any time. It ends when someone closes it or the network drops. Client ──handshake──────────────▶ Server Client ◀──connection open──────── Server Client ◀──{ "home": {"score": 1}} Server (goal!) Client ◀──{ "minute": 73 } ────── Server Client ◀──{ "home": {"score": 2}} Server (another goal!) ...connection stays open...
Key traits of WebSocket:
Stateful. The connection persists, so context can be kept.
Push-based. The server sends data the moment something changes.
Low overhead per message. No need to re-send headers with every update.
Two-way. The client can send messages too (for example, to subscribe to a sport).
More moving parts. You manage connection health, reconnects and partial data.
The real WebSocket URL, authentication method and message format are in the WebSocket API docs. Always copy them from there.
- REST vs WebSocket: the side-by-side comparison REST WebSocket Who speaks first? Always the client Either side Connection Opens and closes per request Stays open Data flow Pull Push (and two-way) Best for Fixtures, standings, stats, history Live scoreboards, live odds, chat Freshness As fresh as your last request As fresh as the last pushed message Wasted requests High if you poll Very low Caching Easy Not applicable Complexity Low Medium Works in curl Yes Not directly If connection fails Next request just works You must reconnect and resync Typical beginner mistake Polling too fast Not handling reconnects
Neither is "better." They solve different problems.
- The polling problem (with real maths)
Polling means asking REST for live data on a timer. It is the first thing every beginner tries, and it has two hidden costs.
Cost 1: Wasted requests
Suppose you poll one endpoint every 5 seconds:
86,400 seconds per day ÷ 5 = 17,280 requests per day
For one endpoint. Now imagine you poll all 13 sports once a minute:
13 sports × 1,440 minutes = 18,720 requests per day
At the time of writing, Orbistats documents roughly 150 requests per day on the free tier, but always check the live pricing page for current limits. A polling loop can exhaust a small allowance in minutes, and most of those requests would return "nothing changed."
Cost 2: Built-in delay
If a goal happens right after your last poll, you will not see it until the next one. On average, polling adds delay of half your interval:
Poll every 10 s → average delay about 5 s
Poll every 60 s → average delay about 30 s
WebSocket removes both costs: the server tells you when something changes, so there are no wasted asks and no waiting for the next tick.
Step 1: Get your free API key
Open the sign-up page and create a free account. No card is needed to start.
Copy your API key.
Store it as an environment variable, never in your code.
macOS or Linux:
bash
export ORBISTATS_API_KEY="your_key_here"
Windows PowerShell:
powershell
$env:ORBISTATS_API_KEY = "your_key_here"
Want to see real responses before coding? Open the public sandbox. The quickstart and documentation explain authentication, and the API reference lists endpoints and fields.
Step 2: A REST request in Python
bash
pip install requests python-dotenv
Create a .env file (and add it to .gitignore):
ORBISTATS_API_KEY=your_key_here
python
import os
import requests
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("ORBISTATS_API_KEY")
BASE_URL = "https://api.orbistats.com/v1"
def get_live(sport="football"):
res = requests.get(
f"{BASE_URL}/{sport}/live",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=10,
)
res.raise_for_status()
return res.json().get("data", [])
for m in get_live("football"):
print(f'{m["minute"]}\' {m["home"]["name"]} {m["home"]["score"]}'
f' - {m["away"]["score"]} {m["away"]["name"]}')
Run it once and you get one snapshot. That is REST: one question, one answer.
Step 3: The polling version (and why it hurts)
Here is the beginner's first attempt at "live":
python
import time
while True:
for m in get_live("football"):
print(m["home"]["name"], m["home"]["score"], "-",
m["away"]["score"], m["away"]["name"])
time.sleep(5) # 17,280 requests a day if left running
It works, but now you know the cost. If you must poll, do it responsibly:
python
import time
from datetime import datetime, timezone, timedelta
def is_near_live(start_iso, window_minutes=15):
start = datetime.fromisoformat(start_iso.replace("Z", "+00:00"))
now = datetime.now(timezone.utc)
return start - timedelta(minutes=window_minutes) <= now <= start + timedelta(hours=4)
def poll_loop(fixtures):
while True:
active = [f for f in fixtures if is_near_live(f["start_time"])]
if active:
print("polling live matches...")
time.sleep(20) # poll faster only when a match is on
else:
time.sleep(300) # otherwise barely poll at all
The idea: use fixtures (from the Sports Data API) to know when to poll fast. The start_time field above is illustrative, so check real field names in the docs.
Step 4: A WebSocket client in Node.js
bash
mkdir ws-demo && cd ws-demo
npm init -y
npm pkg set type=module
npm install ws
client.js:
javascript
import WebSocket from "ws";
const API_KEY = process.env.ORBISTATS_API_KEY;
const WS_URL = "PASTE_URL_FROM_WEBSOCKET_DOCS"; // copy it from the docs
const ws = new WebSocket(WS_URL, {
headers: { Authorization: Bearer ${API_KEY} },
});
ws.on("open", () => console.log("Connected. Waiting for updates..."));
ws.on("message", (raw) => {
const msg = JSON.parse(raw);
console.log("Update:", msg);
});
ws.on("close", (code) => console.log("Closed:", code));
ws.on("error", (err) => console.error("Error:", err.message));
Run it:
bash
node client.js
You will not see a request/response pair. You will see messages arrive on their own. Note that the URL and authentication details above are placeholders, because the exact connection method must come from the provider's WebSocket API docs.
Step 5: Real-world WebSocket rules (the part tutorials skip)
A WebSocket that works in a demo can fail in production. Four rules prevent most problems.
Rule 1: Start with a REST snapshot
A WebSocket tells you what changes. It does not necessarily tell you what the world looks like right now. Fetch the current state over REST first, then apply updates.
Rule 2: Merge updates, never replace
Live messages are often partial. You might receive only:
json
{ "match_id": "match_50231", "home": { "score": 3 } }
If you replace your stored match with that, the team names and minute vanish. Merge instead:
javascript
const matches = new Map();
function merge(update) {
const prev = matches.get(update.match_id) || {};
const next = {
...prev,
...update,
home: { ...prev.home, ...update.home },
away: { ...prev.away, ...update.away },
};
matches.set(update.match_id, next);
return next;
}
Rule 3: Reconnect with backoff
Networks drop. Phones switch from Wi-Fi to mobile data. Servers restart. Your client must reconnect, waiting longer after each failure so it does not hammer the server:
javascript
let attempts = 0;
function connect() {
const ws = new WebSocket(WS_URL, {
headers: { Authorization: Bearer ${API_KEY} },
});
ws.on("open", async () => {
attempts = 0;
await loadSnapshot(); // Rule 4: resync after every (re)connect
});
ws.on("message", (raw) => {
const msg = JSON.parse(raw);
const items = Array.isArray(msg.data) ? msg.data : [msg.data ?? msg];
items.forEach((u) => console.log("update:", merge(u)));
});
ws.on("close", () => {
const delay = Math.min(30_000, 1000 * 2 ** attempts++) + Math.random() * 500;
console.log(Reconnecting in ${Math.round(delay)} ms);
setTimeout(connect, delay);
});
ws.on("error", (e) => console.error("WS error:", e.message));
}
connect();
The small random number added to the delay is called jitter. It stops thousands of clients reconnecting at the exact same moment after an outage.
Rule 4: Resync after reconnecting
You may have missed messages while disconnected. After every reconnect, refetch the REST snapshot and replace your state with it:
javascript
async function loadSnapshot() {
const res = await fetch("https://api.orbistats.com/v1/football/live", {
headers: { Authorization: Bearer ${API_KEY} },
});
if (!res.ok) return;
const json = await res.json();
matches.clear();
(json.data ?? []).forEach((m) => matches.set(m.match_id, m));
}
Notice how REST and WebSocket cooperate here. That is not an accident, and we will come back to it.
Step 6: Heartbeats (detecting dead connections)
Sometimes a connection dies silently. The socket looks open, but nothing is arriving. A heartbeat catches it:
javascript
let lastMessageAt = Date.now();
ws.on("message", () => { lastMessageAt = Date.now(); });
const watchdog = setInterval(() => {
if (Date.now() - lastMessageAt > 45_000) {
console.warn("No data for 45s, forcing reconnect");
ws.terminate(); // 'close' handler will reconnect
}
}, 10_000);
Check the docs for whether the provider sends its own ping messages, and what the quiet-period expectations are. The 45-second number here is an example you should tune. A quiet period is normal when no matches are live.
Step 7: Mini project, live updates in the browser (without leaking your key)
Here is a problem every beginner hits. You want your web page to show live updates, but browser WebSocket code cannot hide secrets, and the browser's built-in WebSocket API does not let you set custom headers like Authorization. If you put your key in the page, anyone can steal it.
The fix is a relay server:
Provider ──WebSocket──▶ Your server ──WebSocket──▶ Browser pages
(key lives here) (no key needed)
Your server holds the secret and the upstream connection. Browsers connect to your server.
Setup
bash
mkdir live-relay && cd live-relay
npm init -y
npm pkg set type=module
npm install express ws
mkdir public
server.js
javascript
import http from "node:http";
import express from "express";
import { WebSocket, WebSocketServer } from "ws";
const KEY = process.env.ORBISTATS_API_KEY;
const UPSTREAM_URL = process.env.UPSTREAM_WS_URL; // from the WebSocket docs
const REST_BASE = "https://api.orbistats.com/v1";
const DEMO = !KEY || !UPSTREAM_URL; // runs without a key
const app = express();
app.use(express.static("public"));
const server = http.createServer(app);
const wss = new WebSocketServer({ server, path: "/live" });
const matches = new Map();
function merge(update) {
const prev = matches.get(update.match_id) || {};
const next = {
...prev,
...update,
home: { ...prev.home, ...update.home },
away: { ...prev.away, ...update.away },
};
matches.set(update.match_id, next);
return next;
}
function broadcast(match) {
const text = JSON.stringify({ type: "update", match });
for (const client of wss.clients) {
if (client.readyState === WebSocket.OPEN) client.send(text);
}
}
async function loadSnapshot() {
const res = await fetch(${REST_BASE}/football/live, {
headers: { Authorization: Bearer ${KEY} },
});
if (!res.ok) throw new Error(Snapshot failed: ${res.status});
const json = await res.json();
matches.clear();
(json.data ?? []).forEach((m) => matches.set(m.match_id, m));
}
// New browser connects: send the current state first
wss.on("connection", (client) => {
client.send(JSON.stringify({
type: "snapshot",
demo: DEMO,
matches: [...matches.values()],
}));
});
// ---- Upstream connection (or demo generator) ----
let attempts = 0;
function connectUpstream() {
const ws = new WebSocket(UPSTREAM_URL, {
headers: { Authorization: Bearer ${KEY} },
});
ws.on("open", async () => {
attempts = 0;
try { await loadSnapshot(); } catch (e) { console.error(e.message); }
});
ws.on("message", (raw) => {
try {
const msg = JSON.parse(raw);
const items = Array.isArray(msg.data) ? msg.data : [msg.data ?? msg];
items.filter((u) => u?.match_id).forEach((u) => broadcast(merge(u)));
} catch { /* ignore malformed messages */ }
});
ws.on("close", () => {
const delay = Math.min(30_000, 1000 * 2 ** attempts++) + Math.random() * 500;
setTimeout(connectUpstream, delay);
});
ws.on("error", (e) => console.error("Upstream error:", e.message));
}
function startDemo() {
const demo = {
match_id: "demo_1", status: "live", minute: 60,
home: { name: "Blue Rovers", score: 0 },
away: { name: "Red City", score: 0 },
};
matches.set(demo.match_id, demo);
setInterval(() => {
const update = { match_id: "demo_1", minute: demo.minute++ };
if (Math.random() < 0.15) {
const side = Math.random() < 0.5 ? "home" : "away";
demo[side].score++;
update[side] = { score: demo[side].score };
}
broadcast(merge(update));
}, 2000);
}
DEMO ? startDemo() : connectUpstream();
server.listen(3000, () => console.log("Open http://localhost:3000"));
Study three details:
Snapshot on connect. A new browser immediately receives the current state, using the same snapshot-then-updates idea from earlier.
Merge before broadcast. Browsers always receive complete match objects, so the page code stays simple.
Demo mode. With no key or upstream URL, a fake generator produces goals and minutes so you can build the whole UI on day one.
public/index.html
html
<!doctype html>
<br> body { font-family: system-ui, sans-serif; max-width: 640px; margin: 2rem auto; padding: 0 1rem; }<br> .card { border: 1px solid #ddd; border-radius: 8px; padding: .75rem 1rem;<br> margin: .5rem 0; display: flex; justify-content: space-between; }<br> .clock { color: #c00; font-weight: 600; }<br> #status { font-size: .85rem; color: #666; }<br>
Live Matches
Connecting...
<br>
const list = document.getElementById("list");<br>
const statusEl = document.getElementById("status");<br>
const state = new Map();<br>
let retry = 0;</p>
<div class="highlight"><pre class="highlight plaintext"><code>function render() {
const cards = [...state.values()].map((m) => {
const el = document.createElement("div");
el.className = "card";
const teams = document.createElement("span");
teams.textContent =
`${m.home?.name ?? "?"} ${m.home?.score ?? 0} - ` +
`${m.away?.score ?? 0} ${m.away?.name ?? "?"}`;
const clock = document.createElement("span");
clock.className = "clock";
clock.textContent = m.status === "live" && m.minute ? `${m.minute}'` : (m.status ?? "");
el.append(teams, clock);
return el;
});
if (cards.length) {
list.replaceChildren(...cards);
} else {
const p = document.createElement("p");
p.textContent = "No live matches right now.";
list.replaceChildren(p);
}
}
function connect() {
const proto = location.protocol === "https:" ? "wss" : "ws";
const ws = new WebSocket(`${proto}://${location.host}/live`);
ws.onopen = () => { retry = 0; statusEl.textContent = "Live"; };
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.type === "snapshot") {
state.clear();
msg.matches.forEach((m) => state.set(m.match_id, m));
if (msg.demo) statusEl.textContent = "Live (demo data)";
} else if (msg.type === "update") {
state.set(msg.match.match_id, msg.match);
}
render();
};
ws.onclose = () => {
statusEl.textContent = "Disconnected. Reconnecting...";
setTimeout(connect, Math.min(15000, 1000 * 2 ** retry++));
};
}
connect();
</code></pre></div>
<p>
We use textContent, not innerHTML, so a strange team name containing HTML would display as plain text instead of running as code. That one habit prevents a whole class of security bugs.
Run it
bash
Demo mode, no key needed:
node server.js
With real data (copy the upstream URL from the WebSocket docs):
ORBISTATS_API_KEY="your_key_here" UPSTREAM_WS_URL="wss://..." node server.js
Open http://localhost:3000 and watch scores change without a single page refresh, and without a single request from the browser after the connection opens.
Honest limitations: this relay shows football-style home-vs-away scores, uses in-memory state (restarting the server loses it until the next snapshot) and has no authentication of its own, so do not expose it publicly as-is. For other sports, write one small adapter per sport, because cricket, tennis and golf have different shapes.
Step 8: Use both together (the hybrid pattern)
Real products almost never choose just one. The winning pattern:
- REST → load initial state (snapshot, fixtures, standings)
- WebSocket → receive live changes
- REST → resync after any disconnect
- REST → stats, history and everything that is not live
In product terms:
Sports Data API (REST): fixtures, results, standings.
Live Scores API (REST snapshot plus WebSocket): live matches.
Sports Statistics API (REST): team and player numbers.
Odds API (REST for pre-match, WebSocket for live prices): odds are informational data, not predictions or advice.
Historical Sports Data API (REST): past seasons.
Think of REST as the foundation and WebSocket as the live layer on top.
- Where do webhooks and Server-Sent Events fit?
Two other tools often appear in this conversation.
Webhooks: "call me when it happens"
Instead of you holding a connection, the provider sends an HTTP POST to a URL on your server when an event occurs. Great for alerts and automation (for example, "a match just ended, send a Discord message"). You need a public URL to receive them. See the Webhooks API.
javascript
import express from "express";
const app = express();
app.use(express.json());
const seen = new Set();
app.post("/webhooks/orbistats", (req, res) => {
const event = req.body;
// TODO: verify the signature as described in the docs
if (event.id && seen.has(event.id)) return res.sendStatus(200); // duplicate
if (event.id) seen.add(event.id);
console.log("event:", event.type, event);
res.sendStatus(200); // reply FAST, process later
});
app.listen(3001);
The event names and payload here are illustrative, so use the real ones from the docs. Golden rules: reply with 200 quickly, process later, and ignore duplicates, because providers retry slow responses.
Server-Sent Events (SSE): one-way push
SSE is a simpler cousin of WebSocket where the server pushes text to the browser over a normal HTTP connection, one direction only. It is handy for simple feeds, but it is a separate technology. Whether a given provider offers it is something to check in its docs.
The quick map
REST = pull on demand
WebSocket = live two-way stream
Webhooks = server calls you on events
SSE = simple one-way stream to browsers
- How to decide: a simple flowchart in words
Ask these questions in order:
Does the data change while the user is looking at it?
No → REST. Yes → continue.
Is a delay of 30+ seconds acceptable?
Yes → REST with smart polling and caching. No → continue.
Do you need to react to events when no user is watching (alerts, automation)?
Yes → Webhooks.
Do users need to see updates the moment they happen?
Yes → WebSocket (plus a REST snapshot).
Do you only need it on one page for a short time?
Consider whether simple polling every 15 to 30 seconds is enough. Not every project needs a persistent connection.
Quick examples
Feature Best choice
League table page REST + cache
Match schedule REST + cache
Player profile with stats REST
Live scoreboard on screen REST snapshot + WebSocket
Live odds board WebSocket
"Goal!" notification Webhooks
Historical charts REST
Chat or collaboration WebSocket
Daily email digest REST (scheduled)
- Which sports does this apply to?
Everything above works the same way across the sports a unified API covers. Orbistats currently lists 13 sports:
Football, Basketball, American Football, Cricket, Tennis, Baseball, Esports, Combat Sports, Volleyball, Handball, Ice Hockey, Golf and Horse Racing.
The URL pattern is sport, then resource, so switching sports is usually a one-word change:
/v1/football/live
/v1/basketball/live
/v1/tennis/live
One warning: the response shape may be consistent, but the meaning of fields differs. A football match has minutes, tennis has sets and golf has a leaderboard. Read each sport's page before assuming a field exists.
Want a scoreboard with almost no code? Ready-made widgets handle the whole thing for you.
The business angle: why this choice costs (or saves) money
Choosing between REST and WebSocket is not just a technical decision. It changes your bill, your speed and your users' experience.
Cost. Polling burns requests. WebSocket, used well, uses far fewer. Providers price by volume, so efficient delivery can mean a smaller plan.
User experience. Live products feel dead when they lag. A scoreboard that updates instantly feels alive, and engaged users return more often.
Infrastructure. WebSocket servers keep many open connections, which needs planning at scale. REST is easier to scale and cache. That is why the hybrid pattern is popular.
Who cares most:
Media and publishers add live scoreboards to keep readers on the page. See Media & Publishers.
Fantasy and AI products need instant player updates and deep history. See Fantasy & AI Data.
Sportsbooks and trading desks react to live events within seconds, so latency is money. See Sportsbooks and Trading Desks.
Analytics and data science teams build models from live and historical data. See Analytics & Data Science.
Clubs, federations and leagues use data for performance analysis and fan engagement. See Teams & Clubs and Federations & Leagues.
For you as a developer: knowing when not to use WebSocket is as valuable as knowing how. A clean hybrid architecture, with caching and graceful reconnects, is exactly what makes a portfolio project stand out. Good ways to use the skill:
Portfolio project. A relay server with snapshots and reconnects shows real engineering judgment.
Niche apps. One sport, one league, one audience.
Freelance work. Small clubs and blogs often need live widgets or dashboards.
Content and tools. Explainers and calculators attract search traffic. The Orbistats tools and guides pages show the idea.
No guarantees on income. The point is that the barrier is now your idea, not the plumbing.
How to choose a data provider: a 10-point checklist
If you are building on a real-time API, check:
Delivery options. REST, WebSocket and webhooks all available?
Latency. Orbistats advertises a sub-50ms live feed, but always measure from your own region.
Sport coverage. Does it cover what you need now and later? Orbistats covers 13 sports.
Consistent schema. One shape across sports saves weeks.
Snapshot support. Can you fetch current state to resync after a reconnect?
Docs and examples. Look for documentation, examples and SDKs.
Reliability signals. A changelog and status page show a provider takes uptime seriously.
Free tier and sandbox. Can you test before paying?
Licensing. Read the data licensing terms before building a commercial product.
Price at scale. Orbistats lists Free at $0, Starter at $19/month, Growth at $79/month and custom Enterprise. See the pricing page for current details, and compare options on the comparison and alternatives pages.
Common mistakes (and the fix)
Polling every second. It burns your quota and still lags. Use WebSocket or longer intervals with caching.
Using WebSocket for everything. Standings and history do not need a live connection. Use REST and cache.
Replacing state with partial updates. Merge instead.
No reconnect logic. Add backoff and jitter.
Not resyncing after reconnect. Refetch a REST snapshot.
Putting the API key in browser code. Use a relay or backend.
No heartbeat or watchdog. Silent dead connections will fool you.
Ignoring rate limits and status codes. Check 401, 403, 404 and 429 before using data.
Trusting a live score as final. Scores can be corrected until full time.
Assuming all sports share the same fields. Read each sport's docs.
Where to go next
Add a second sport with its own adapter.
Add a REST endpoint for standings with a 10-minute cache.
Add a webhook alert for finished matches.
Add authentication to your relay before putting it online.
Log connection drops to see how often reconnects happen.
Read the WebSocket API and Webhooks API docs end to end.
For more code to learn from, Orbistats' team has published tutorials on a live scoreboard in 50 lines of JavaScript with WebSocket, consuming a sports API in Python with FastAPI, consuming it in .NET Core and building a live dashboard with Next.js.
FAQ
What is the main difference between REST and WebSocket?
REST is request and response: you ask, the server answers, and the exchange ends. WebSocket keeps a connection open so the server can push data whenever it wants.
Is WebSocket faster than REST?
For live updates, yes in practice, because you do not wait for your next poll and you skip repeated request overhead. For one-off lookups, REST is just as good and simpler.
Can I replace REST with WebSocket entirely?
You can, but you usually should not. REST is better for cacheable data like fixtures and history, and for getting a clean snapshot after a reconnect.
Why can't I use my API key in browser WebSocket code?
Anything in browser code is visible to users, and the browser WebSocket API does not let you set custom headers such as Authorization. Use a backend or relay server that holds the key.
What happens if a WebSocket connection drops?
Nothing automatically. You must detect it, reconnect with backoff and resync state via REST.
When should I use webhooks instead?
When you need to react to events without a user watching, like sending an alert when a match ends.
Is there a free API to practice both?
Many providers offer a free tier. Orbistats offers a free key with no card required and a public sandbox. Check which delivery methods your plan includes on the pricing page.
Which sports does Orbistats cover?
13: Football, Basketball, American Football, Cricket, Tennis, Baseball, Esports, Combat Sports, Volleyball, Handball, Ice Hockey, Golf and Horse Racing.
Can I use the data in a commercial product?
Check the provider's data licensing and terms first.
Final thoughts
REST and WebSocket are not rivals. They are two tools with two jobs.
REST is the dependable foundation: simple, cacheable, easy to debug, perfect for anything you can ask for on demand. WebSocket is the live layer: efficient, instant and a little more demanding to run well.
The recipe is short. Use REST for pages and snapshots. Use WebSocket for screens that must feel live. Use webhooks for alerts. Merge partial updates. Reconnect with backoff and jitter. Resync after every reconnect. Keep your key on the server. And never poll faster than you need.
Now run the demo relay, open two browser tabs and watch them update together. That small moment is when the difference between "asking" and "listening" finally clicks.
👉 Get a free API key and try both styles today.
📚 Explore the developer hub for docs, guides and examples.
🔬 Like data-driven reads? Bookmark the research section.
🏢 Curious who is behind it? Visit the About page, or explore the full platform at Orbistats.
Which one are you reaching for in your next project: REST, WebSocket or both? Tell me in the comments.
Top comments (0)