MoviOdds Docs
API docs Sports Bet Builder Python moviodds.com

bet365 odds feed

Every bet365 price for 24 sports, pre-match and in-play, as one JSON feed. Free and no key needed

What you get

GET /odds gives you the whole board, every match bet365 lists with every market it prices, from the match winner to handicaps, totals, correct score, corners, cards and player props. Pick a sport with sport= or grab them all with sport=all, and cut it down with groups= (market ids) or in_play=1

curl --compressed "https://moviodds.com/odds?sport=soccer&groups=40"

Big boards are big so ask for gzip (--compressed in curl, Python requests does it by itself)

The JSON

Each match has its teams, league, kickoff and a quotes list, one entry per price:

{
  "id": "202905871",
  "home": "Lee Man FC",
  "away": "Kitchee",
  "league": "Hong Kong Premier League",
  "kickoff_utc": "2026-10-19T10:30:00Z",
  "sport": "soccer",
  "quotes": [
    {"group_id": "40", "group_name": "Full Time Result", "sel_id": "...", "label": "Lee Man FC", "odds_dec": 2.3, "odds_frac": "13/10"},
    {"group_id": "40", "group_name": "Full Time Result", "sel_id": "...", "label": "Draw", "odds_dec": 3.9, "odds_frac": "29/10"},
    …
  ]
}

Market and selection ids stay the same across matches so you can filter and store on them. Every market id is on the market list

CSV and XML

We serve JSON only, there's no XML endpoint. If your tool wants CSV or XML (Excel, an old bookmaker system, a spreadsheet) it's a few lines of Python:

import csv
import requests
import xml.etree.ElementTree as ET

r = requests.get("https://moviodds.com/odds", params={"sport": "soccer", "groups": "40"}, timeout=60)
r.raise_for_status()
matches = r.json()["matches"]

# CSV, one row per price
with open("bet365_odds.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.writer(f)
    w.writerow(["match_id", "league", "home", "away", "kickoff_utc", "market", "selection", "odds"])
    for m in matches:
        for q in m["quotes"]:
            w.writerow([m["id"], m["league"], m["home"], m["away"], m.get("kickoff_utc"),
                        q["group_name"], q.get("label") or q.get("side"), q["odds_dec"]])

# XML, one <match> with its <price> elements
root = ET.Element("odds", source="bet365")
for m in matches:
    e = ET.SubElement(root, "match", id=m["id"], league=m["league"], home=m["home"], away=m["away"])
    for q in m["quotes"]:
        ET.SubElement(e, "price", market=q["group_name"], selection=q.get("label") or q.get("side") or "",
                      odds=str(q["odds_dec"]))
ET.ElementTree(root).write("bet365_odds.xml", encoding="utf-8", xml_declaration=True)
print(len(matches), "matches saved")
751 matches saved

You end up with bet365_odds.csv, one row per price, and bet365_odds.xml. Run it on a schedule and you have a bet365 XML feed

Keep it live

Polling works but the WebSocket is better for anything live. Connect to wss://moviodds.com/ws, send {"op":"subscribe"} and you get every price change within about a second of bet365. See the WebSocket guide

FAQ

Is the bet365 odds feed free?

Yes, no sign-up and no key. /odds allows 300 requests a minute per IP

Do you have a bet365 XML feed?

The feed is JSON. The Python above turns it into XML or CSV, run it on a schedule if you need a file feed

Which sports are in the feed?

24 sports, soccer, tennis, basketball, American football, ice hockey, baseball, cricket, rugby, esports and more. See all sports

Is it live?

Yes, pre-match and in-play. In-play prices come over the WebSocket within about a second of bet365 changing them

Are these really bet365 odds?

Yes, only bet365's own prices. MoviOdds is an independent project and is not affiliated with bet365

More: live WebSocket · historical odds · all 24 sports · Python · API docs