1xBet API: Which of 8 Feeds Quotes the Tighter Line?
If you searched for a 1xBet API, you want 1xBet’s lines as JSON, next to the books you already price against. OddsPapi carries the book under the 1xbet slug, in the same schema as Pinnacle and 350+ other bookmakers. One call returns it. Read it with care, because 1xbet is one of eight slugs on our board that carry the same 1xBet event ID.
The other seven link to 22bet.com: 22bet, 1xbit, betwinner, fairpari, megapari, melbet and spelet.lv. Those seven quote byte-identical prices. 1xbet runs a second price book on the same event, and on 19 live boards (7 October 2026) it priced the main line at a 4.16% median margin against 5.40% on 22bet. By the close the gap narrows: on 54 played fixtures the two books closed identical on 19. The 22bet line carries the bigger menu. This guide shows you how to find the eight slugs, collapse them to two price books, and decide which one to read for each market.
Why a 1xBet scraper gives you one feed out of eight
A scraper pointed at one 1xBet site gives you one price book and one site’s HTML to maintain. It does not tell you that seven more slugs carry the same event, or that two of the eight price it differently. If you add all eight to a consensus, one price counts seven times. Our consensus odds guide covers why that skews a fair-value line.
OddsPapi gives every book a bookmakerFixtureId, the event ID from the bookmaker’s own site, and a fixturePath deep link. Those two fields group the eight slugs for you. We used the same method for the Kambi feeds in the Unibet API guide and for Bwin’s regional sites in the Bwin API guide.
| Scrape one 1xBet site | OddsPapi 1xbet and 22bet |
|
|---|---|---|
| Feeds | The one site you scrape | 8 slugs on one event ID, plus 350+ other books in the same call |
| Duplicate detection | You match events across sites yourself | bookmakerFixtureId and fixturePath on every book |
| Schema | The site’s own markup | One JSON shape for every book, with native market IDs for spreads, totals and props |
| Played fixtures | Only what your recorder saved | /v4/historical-odds on the free tier |
| Live updates | Your polling loop | REST or WebSocket |
Step 1: find the 1xBet slugs in the catalogue
Authentication is one query parameter, apiKey. The helper waits out a 429 for as long as the error body says, backs off on a 5xx, and returns None on a 404.
import os, time, requests
from datetime import datetime, timedelta, timezone
from statistics import median
API_KEY = os.environ["ODDSPAPI_API_KEY"]
BASE = "https://api.oddspapi.io/v4"
def get(path, **params):
params["apiKey"] = API_KEY
for _ in range(8):
r = requests.get(f"{BASE}/{path}", params=params, timeout=180)
if r.status_code == 429: # rate limited: wait as told
time.sleep(r.json().get("error", {}).get("retryMs", 2000) / 1000 + 0.5)
continue
if r.status_code >= 500: # transient: back off and retry
time.sleep(2)
continue
if r.status_code == 404: # empty window
return None
r.raise_for_status()
return r.json()
raise RuntimeError(f"gave up on {path}")
books = get("bookmakers")
print(f"{len(books)} bookmakers in the catalogue\n")
# 1xbet itself, 22bet, and every slug the catalogue flags as a 22bet clone
family = [b for b in books
if b["slug"] in ("1xbet", "22bet") or b.get("cloneOf") == "22bet"]
for b in family:
print(f'{b["slug"]:<10} {b["bookmakerName"]:<10} '
f'liveOdds={b["liveOdds"]!s:<5} cloneOf={b["cloneOf"]}')
365 bookmakers in the catalogue
1xbet 1xBet liveOdds=True cloneOf=None
1xbit 1xBit liveOdds=True cloneOf=22bet
22bet 22Bet liveOdds=True cloneOf=None
betwinner BetWinner liveOdds=True cloneOf=22bet
fairpari FairPari liveOdds=True cloneOf=22bet
megapari MegaPari liveOdds=True cloneOf=22bet
melbet MelBet liveOdds=True cloneOf=22bet
spelet.lv Spelet LV liveOdds=True cloneOf=22bet
The catalogue flags six slugs as clones of 22bet and leaves 1xbet unflagged. Nothing in /v4/bookmakers links 1xbet to the other seven. The event ID does that, in Step 2.
Step 2: group the eight slugs by event ID
This step takes the next NHL fixture, asks for the eight slugs, and groups them twice: first by bookmakerFixtureId and the host in fixturePath, then by the set of active prices each slug quotes.
# Reuses get() and the imports from Step 1.
import json
from collections import defaultdict
FAMILY = ["1xbet", "22bet", "1xbit", "betwinner", "fairpari",
"megapari", "melbet", "spelet.lv"]
NHL = 234 # tournamentId, from /v4/fixtures
def active_prices(book):
"""Every active price a book quotes, keyed by (market, outcome, player)."""
return {(mid, oid, pid): p["price"]
for mid, m in book["markets"].items()
for oid, o in m["outcomes"].items()
for pid, p in (o.get("players") or {}).items()
if p.get("active") and p.get("price")}
now = datetime.now(timezone.utc)
fixtures = get("fixtures", tournamentId=NHL,
**{"from": now.strftime("%Y-%m-%d"),
"to": (now + timedelta(days=4)).strftime("%Y-%m-%d")})
fx = next(f for f in fixtures if f["hasOdds"] and f["startTime"] > now.isoformat())
print(fx["participant1Name"], "v", fx["participant2Name"], fx["startTime"])
odds = get("odds", fixtureId=fx["fixtureId"], bookmakers=",".join(FAMILY))
board = odds["bookmakerOdds"]
# 1. group the slugs by the bookmaker's own event ID and the host they link to
events = defaultdict(list)
for slug, book in board.items():
host = book["fixturePath"].split("/")[2]
events[book["bookmakerFixtureId"]].append(f"{slug} ({host})")
for event_id, slugs in events.items():
print(f"\nevent ID {event_id}: {len(slugs)} slugs")
for s in slugs:
print(" ", s)
# 2. collapse slugs that quote byte-identical prices
books = defaultdict(list)
for slug, book in board.items():
prices = active_prices(book)
books[json.dumps(sorted(prices.items()))].append((slug, len(prices),
sum(1 for m in book["markets"].values()
if any(p.get("active") for o in m["outcomes"].values()
for p in (o.get("players") or {}).values()))))
print(f"\n{len(board)} slugs, {len(books)} distinct price books")
for members in books.values():
slugs = [m[0] for m in members]
print(f" {len(slugs)} slug(s), {members[0][2]} active markets, "
f"{members[0][1]} active prices: {', '.join(slugs)}")
Washington Capitals v Pittsburgh Penguins 2026-10-07T23:30:00.000Z
event ID 351619128: 8 slugs
1xbet (1xbet.com)
1xbit (22bet.com)
22bet (22bet.com)
betwinner (22bet.com)
fairpari (22bet.com)
megapari (22bet.com)
melbet (22bet.com)
spelet.lv (22bet.com)
8 slugs, 2 distinct price books
1 slug(s), 54 active markets, 332 active prices: 1xbet
7 slug(s), 118 active markets, 574 active prices: 1xbit, 22bet, betwinner, fairpari, megapari, melbet, spelet.lv
Eight slugs, one event ID, two price books. The seven 22bet.com slugs matched price for price on all 15 boards we compared on 7 October, across the NFL, MLB, NHL, EuroLeague, ATP Shanghai and the EFL Championship. Keep one of them in a consensus and drop the other six.
The 1xbet book is not a copy. On the NHL boards, most of its active prices matched 22bet, but the moneyline did not. Across the 7 October sample, the 22bet line carried 1.05 to 6.3 times as many active markets on 29 boards: 346 against 193 on West Ham v QPR, 363 against 271 on Cowboys v Buccaneers. Read the menu from 22bet and the headline price from 1xbet.
Step 3: compare the main-line margin across a slate
This step reads the main market (moneyline, winner or 1X2) for the next three fixtures in seven tournaments and prints the margin for 1xbet, 22bet and pinnacle. A market counts only when every leg is active.
# Reuses get() and the imports from Step 1.
# tournamentId -> the market that carries the headline price
SLATE = {31: "141", # NFL, Winner (incl. overtime)
234: "151", # NHL, Winner (incl. overtime and penalties)
109: "131", # MLB, Winner (incl. extra innings)
138: "111", # EuroLeague, Winner (incl. overtime)
3085: "121", # ATP Shanghai singles, Winner
325: "101", # Brasileiro Serie A, Full Time Result
18: "101"} # EFL Championship, Full Time Result
BOOKS = ["1xbet", "22bet", "pinnacle"]
def overround(book, market_id):
"""Margin in % on one market, or None unless every leg is active."""
m = book["markets"].get(market_id) if book else None
if not m:
return None
legs = [o["players"]["0"] for o in m["outcomes"].values()]
if not all(l.get("active") and l.get("price") for l in legs):
return None
return (sum(1 / l["price"] for l in legs) - 1) * 100
now = datetime.now(timezone.utc)
window = {"from": now.strftime("%Y-%m-%d"),
"to": (now + timedelta(days=4)).strftime("%Y-%m-%d")}
rows = []
for tid, market_id in SLATE.items():
fixtures = get("fixtures", tournamentId=tid, **window) or []
upcoming = [f for f in fixtures
if f["hasOdds"] and f["startTime"] > now.isoformat()][:3]
for f in upcoming:
odds = get("odds", fixtureId=f["fixtureId"], bookmakers=",".join(BOOKS))
board = (odds or {}).get("bookmakerOdds", {})
m = {s: overround(board.get(s), market_id) for s in BOOKS}
if m["1xbet"] is None or m["22bet"] is None:
continue
rows.append(m)
name = f'{f["participant1Name"]} v {f["participant2Name"]}'[:34]
pin = f'{m["pinnacle"]:.2f}%' if m["pinnacle"] is not None else "-"
print(f'{f["tournamentName"][:14]:<14} {name:<34} '
f'1xbet {m["1xbet"]:.2f}% 22bet {m["22bet"]:.2f}% pinnacle {pin}')
time.sleep(1)
tighter = sum(1 for r in rows if r["1xbet"] < r["22bet"] - 0.005)
same = sum(1 for r in rows if abs(r["1xbet"] - r["22bet"]) <= 0.005)
print(f"\n{len(rows)} fixtures: 1xbet tighter on {tighter}, "
f"identical on {same}, wider on {len(rows) - tighter - same}")
for s in BOOKS:
vals = [r[s] for r in rows if r[s] is not None]
print(f"median {s:<8} {median(vals):.2f}% ({len(vals)} fixtures)")
NFL Dallas Cowboys v Tampa Bay Buccane 1xbet 4.29% 22bet 5.42% pinnacle 3.77%
NHL Washington Capitals v Pittsburgh P 1xbet 3.09% 22bet 5.21% pinnacle 2.46%
NHL Winnipeg Jets v Colorado Avalanche 1xbet 3.12% 22bet 5.41% pinnacle 2.66%
NHL Anaheim Ducks v Edmonton Oilers 1xbet 3.12% 22bet 5.18% pinnacle 2.61%
MLB Chicago White Sox v Cleveland Guar 1xbet 2.03% 22bet 5.25% pinnacle 2.01%
MLB Atlanta Braves v Los Angeles Dodge 1xbet 2.03% 22bet 5.29% pinnacle 1.95%
MLB New York Yankees v Tampa Bay Rays 1xbet 2.06% 22bet 5.27% pinnacle 2.02%
Euroleague Paris Basketball v ASVEL Lyon-Vill 1xbet 3.63% 22bet 5.39% pinnacle 3.78%
Euroleague Maccabi Tel-Aviv v Olimpia Milano 1xbet 3.62% 22bet 5.15% pinnacle 3.80%
Euroleague Dubai Basketball v KK Crvena zvezd 1xbet 3.65% 22bet 5.30% pinnacle 3.89%
ATP Shanghai, Borges, Nuno v Diaz Acosta, Facund 1xbet 4.19% 22bet 5.43% pinnacle 2.94%
ATP Shanghai, Munar, Jaume v Brooksby, Jenson 1xbet 4.18% 22bet 5.29% pinnacle 2.87%
ATP Shanghai, Coppejans, Kimmer v Tsitsipas, Ste 1xbet 4.24% 22bet 5.51% pinnacle 3.74%
Brasileiro Ser Red Bull Bragantino SP v Mirassol 1xbet 4.16% 22bet 5.45% pinnacle 3.01%
Brasileiro Ser SC Internacional RS v SC Corinthia 1xbet 4.23% 22bet 5.40% pinnacle 2.73%
Brasileiro Ser Clube do Remo PA v Gremio FB Porto 1xbet 4.21% 22bet 5.45% pinnacle 2.80%
Championship West Ham United v Queens Park Rang 1xbet 7.07% 22bet 7.07% pinnacle 5.68%
Championship Charlton Athletic v Bristol City 1xbet 7.01% 22bet 7.01% pinnacle 5.03%
Championship Swansea City v Norwich City 1xbet 7.44% 22bet 7.44% pinnacle 4.85%
19 fixtures: 1xbet tighter on 16, identical on 3, wider on 0
median 1xbet 4.16% (19 fixtures)
median 22bet 5.40% (19 fixtures)
median pinnacle 2.94% (19 fixtures)
We ran this on 7 October 2026 at 13:25 UTC. 1xbet priced the main line tighter than 22bet on 16 of 19 fixtures and matched it on the three Championship boards. The gap runs about 1.1 to 3.3 points. On MLB, 1xbet sat within 0.08 points of Pinnacle. On the EuroLeague, it priced the winner market tighter than Pinnacle on all three fixtures.
Check the price age before you trust a gap. On 18 of 29 pre-match boards we read that day, the 1xbet book carried bookmakerIsActive: false, and on 10 of those 18 its newest price had changed inside the last 30 minutes. For this book, read the price-level active flag and each price’s changedAt. Our line shopping guide shows how to pick the best price per leg once you have both books.
Step 4: check the closing line on played fixtures
A live margin tells you about one moment. The close tells you what the book settled on. This step pulls every played NFL Week 5 fixture from /v4/historical-odds and takes the last active price before kickoff on each side. Pass outcomeId to cut the payload: one Brasileiro fixture came back at 14.8 MB for three books, and 0.1 MB for one outcome.
# Reuses get() and the imports from Step 1.
BOOKS = "1xbet,22bet,pinnacle"
NFL, MONEYLINE = 31, ["141", "142"] # Winner (incl. overtime), both legs
def closing_prices(fixture_id, kickoff, outcome_id):
"""Last active price before kickoff, per book, for one outcome."""
hist = get("historical-odds", fixtureId=fixture_id,
bookmakers=BOOKS, outcomeId=outcome_id)
time.sleep(4.6) # /historical-odds needs a longer gap
close = {}
for slug, book in (hist or {}).get("bookmakers", {}).items():
for market in book["markets"].values():
for o in market["outcomes"].values():
snaps = [s for s in o["players"].get("0", [])
if s["active"] and s["createdAt"] < kickoff]
if snaps:
close[slug] = snaps[-1]["price"]
return close
played = get("fixtures", tournamentId=NFL,
**{"from": "2026-10-02", "to": "2026-10-07"})
played = [f for f in played if f["statusName"] == "Finished"]
margins = {s: [] for s in BOOKS.split(",")}
pairs = []
for f in played:
kickoff = f["trueStartTime"] or f["startTime"]
legs = [closing_prices(f["fixtureId"], kickoff, oid) for oid in MONEYLINE]
row = {}
for slug in margins:
if all(slug in leg for leg in legs):
row[slug] = (sum(1 / leg[slug] for leg in legs) - 1) * 100
margins[slug].append(row[slug])
if "1xbet" in row and "22bet" in row:
pairs.append((row["1xbet"], row["22bet"]))
print(f'{f["participant1Abbr"] or "":>4} v {f["participant2Abbr"] or "":<4}',
" ".join(f"{s} {v:.2f}%" for s, v in row.items()))
print(f"\n{len(played)} played NFL Week 5 fixtures, closing moneyline:")
for slug, vals in margins.items():
print(f" {slug:<8} median {median(vals):.2f}% ({len(vals)} fixtures)")
print(f" 1xbet tighter than 22bet at the close on "
f"{sum(1 for a, b in pairs if a < b - 0.005)} of {len(pairs)}, "
f"identical on {sum(1 for a, b in pairs if abs(a - b) <= 0.005)}")
CLE v PIT 1xbet 5.33% 22bet 5.33% pinnacle 2.51%
WAS v IND 1xbet 5.91% 22bet 5.39% pinnacle 4.91%
BAL v TEN 1xbet 5.26% 22bet 5.29% pinnacle 3.21%
BUF v NE 1xbet 5.19% 22bet 5.26% pinnacle 3.00%
CHI v NYJ 1xbet 5.21% 22bet 5.21% pinnacle 2.71%
CIN v JAX 1xbet 5.23% 22bet 5.23% pinnacle 2.48%
HOU v DAL 1xbet 5.23% 22bet 5.23% pinnacle 2.58%
NYG v ARI 1xbet 5.25% 22bet 5.25% pinnacle 2.44%
PHI v LA 1xbet 5.21% 22bet 5.21% pinnacle 2.69%
TB v GB 1xbet 5.23% 22bet 5.23% pinnacle 2.44%
MIN v MIA 1xbet 5.18% 22bet 5.22% pinnacle 3.20%
LV v KC 1xbet 4.34% 22bet 5.33% pinnacle 2.72%
SEA v LAC 1xbet 4.56% 22bet 5.34% pinnacle 2.99%
SF v DEN 1xbet 4.09% 22bet 5.20% pinnacle 2.51%
CAR v DET 1xbet 4.44% 22bet 5.26% pinnacle 2.64%
NO v ATL 1xbet 5.31% 22bet 5.31% pinnacle 2.41%
16 played NFL Week 5 fixtures, closing moneyline:
1xbet median 5.22% (16 fixtures)
22bet median 5.25% (16 fixtures)
pinnacle median 2.66% (16 fixtures)
1xbet tighter than 22bet at the close on 7 of 16, identical on 8
On the NFL, the gap mostly closes by kickoff. The two books closed identical on 8 of 16 moneylines, and 1xbet stayed tighter on 7. We ran the same check on 54 played fixtures across seven tournaments between 2 and 6 October:
| Tournament (played, 2 to 6 Oct 2026) | Fixtures | 1xbet tighter at the close |
Identical | Median close, 1xbet |
Median close, 22bet |
|---|---|---|---|---|---|
| NFL | 12 | 5 | 6 | 5.21% | 5.25% |
| MLB | 8 | 6 | 2 | 2.05% | 5.27% |
| NHL | 10 | 5 | 5 | 4.71% | 4.77% |
| ATP Shanghai singles | 10 | 3 | 5 | 7.92% | 8.04% |
| UEFA Nations League | 9 | 4 | 1 | 4.25% | 5.25% |
| WTA Beijing singles | 3 | 2 | 0 | 4.85% | 4.69% |
| Brasileiro Serie A | 2 | 1 | 0 | 4.17% | 4.14% |
| All | 54 | 26 | 19 |
MLB is where the second book earns its slot. 1xbet closed six of eight postseason moneylines between 2.04% and 2.06%, while 22bet held 5.2% to 5.4%. On the Nations League, the two books split. So route per market and per tournament, and re-run the check on your own slate. The NFL sample in the table is the 12 fixtures from our bulk run; the snippet above reads all 16 Week 5 games.
Snapshots record polling, so collapse identical consecutive prices before you count line moves. For a full strategy test on these closes, start from our backtesting guide. For the speed question, our soft book lag study timed how fast 1xbet follows a Pinnacle move. To read the book live without a polling loop, use the WebSocket feed.
For a one-page view of the slug, see the 1xBet sportsbook page. For Stake and the other crypto and offshore sportsbooks, read the crypto bookmaker API guide.
1xBet API FAQ
How do I get 1xBet odds in Python?
Call /v4/odds with a fixtureId and bookmakers=1xbet, and pass your key as the apiKey query parameter. Add 22bet to the same call to get the second 1xBet price book, which carries the larger market menu.
Which bookmaker feeds share 1xBet’s event ID?
On 7 October 2026, eight slugs carried the same bookmakerFixtureId: 1xbet, plus 22bet, 1xbit, betwinner, fairpari, megapari, melbet and spelet.lv, which all link to 22bet.com.
Do 22Bet, MelBet and BetWinner quote the same odds as 1xBet?
22bet, 1xbit, betwinner, fairpari, megapari, melbet and spelet.lv quoted byte-identical prices on every board we checked. 1xbet runs its own price book on the same event, and priced the main line at a 4.16% median margin against 5.40% on 22bet across 19 live boards.
Does cloneOf tell me which books copy 1xBet?
It flags six slugs as clones of 22bet and leaves 1xbet unflagged. Group books on bookmakerFixtureId and on identical price sets to find the full family.
Can I get historical 1xBet odds?
Yes. /v4/historical-odds returns every 1xbet and 22bet snapshot on a played fixture, on the free tier. Across 54 played fixtures from 2 to 6 October 2026, 1xbet closed the main line tighter than 22bet on 26 and identical on 19.
Read both 1xBet price books from one key
Every call in this guide runs on the free key: 350+ bookmakers in the catalogue, free historical odds for the closing-line check, and a WebSocket feed when polling gets too slow. Get a free API key and run Step 3 on tonight’s slate.