The Brazil Betting Boom: The Best Odds API for EstrelaBet, Betano & Brasileirão
The Brazil Betting Boom is Here—But Where’s the API?
Brazil just became the world’s hottest regulated sports betting market. On January 1, 2025, federal regulation kicked in, turning a gray market into a $10+ billion opportunity. Analysts project Brazil will hit $10B in Gross Gaming Revenue by 2029—putting it on track to rival the US market.
The local players are massive: Betano holds roughly 23% market share. EstrelaBet, Blaze, Stake BR, and Pixbet are all fighting for the rest. These aren’t small-time operators—they’re billion-dollar businesses with odds data that arbitrageurs and quants desperately want.
The problem? None of them offer a public API.
If you’re a developer trying to pull odds from EstrelaBet or Betano, your options are grim: scrape their sites (and risk getting banned), pay enterprise rates ($500+/month) for partial coverage, or give up entirely.
There’s a third option. OddsPapi aggregates odds from 300+ bookmakers—including all major Brazilian operators—into a single API. Free tier included. Historical data for backtesting. No scraping required.
Old Way vs. OddsPapi
| Old Way | OddsPapi |
|---|---|
| Scrape EstrelaBet (risk ban, legal gray area) | One API call with estrelabet slug |
| No Betano API exists publicly | betano.bet.br available in responses |
| Pay $500+/month for enterprise data | Free tier + historical data for backtesting |
| Most APIs cover ~40 bookmakers max | 300+ bookmakers including all Brazilian locals |
| No sharp book benchmarks | Pinnacle, Singbet included for value detection |
Brazilian Bookmakers Available via OddsPapi
These are the Brazilian-licensed operators you can access with a single API call:
| Bookmaker | API Slug | Coverage |
|---|---|---|
| EstrelaBet | estrelabet |
Full odds |
| Stake BR | stake.bet.br |
Full odds |
| Sportingbet BR | sportingbet.bet.br |
Full odds |
| Superbet BR | superbet.bet.br |
Full odds |
| Blaze | blaze.bet.br |
Full odds |
| KTO | kto |
Full odds |
| Pixbet | pixbet |
Full odds |
| Brazino777 | brazino777.bet.br |
Full odds |
Tutorial: Build a Brazilian Bookmaker Arbitrage Scanner
Let’s build a Python script that compares odds across EstrelaBet, Stake BR, and Sportingbet to identify arbitrage opportunities on Brazilian football fixtures.
Step 1: Authentication
OddsPapi uses query parameter authentication—not headers. This is important.
import requests
API_KEY = "YOUR_API_KEY" # Get free key at oddspapi.io
BASE_URL = "https://api.oddspapi.io/v4"
# Always pass apiKey as query parameter
params = {"apiKey": API_KEY}
response = requests.get(f"{BASE_URL}/sports", params=params)
print(response.status_code) # Should be 200
Step 2: Get Brazilian Football Fixtures
First, pull upcoming soccer fixtures and filter for Brazil. Note: date range must be under 10 days.
from datetime import datetime, timedelta
# Date range (max 10 days apart)
today = datetime.now().strftime('%Y-%m-%d')
next_week = (datetime.now() + timedelta(days=7)).strftime('%Y-%m-%d')
# Fetch soccer fixtures (sportId=10)
response = requests.get(f"{BASE_URL}/fixtures", params={
"apiKey": API_KEY,
"sportId": 10,
"from": today,
"to": next_week
})
fixtures = response.json()
# Filter for Brazilian fixtures with odds
brazil_fixtures = [
f for f in fixtures
if "brazil" in f.get("categorySlug", "").lower()
and f.get("hasOdds", False)
]
print(f"Found {len(brazil_fixtures)} Brazilian fixtures with odds")
# Sample output
for f in brazil_fixtures[:5]:
print(f"{f['participant1Name']} vs {f['participant2Name']}")
Step 3: Fetch Odds from Multiple Brazilian Bookmakers
The odds response is nested JSON. Here’s how to parse it correctly:
# Get odds for a specific fixture
fixture_id = brazil_fixtures[0]["fixtureId"]
response = requests.get(f"{BASE_URL}/odds", params={
"apiKey": API_KEY,
"fixtureId": fixture_id
})
data = response.json()
bookmaker_odds = data.get("bookmakerOdds", {})
# Target Brazilian bookmakers
target_slugs = ["estrelabet", "stake.bet.br", "sportingbet.bet.br", "superbet.bet.br"]
# Market 101 = Full Time Result (1X2)
# Outcomes: 101 = Home, 102 = Draw, 103 = Away
for slug in target_slugs:
if slug not in bookmaker_odds:
continue
markets = bookmaker_odds[slug].get("markets", {})
if "101" not in markets:
continue
outcomes = markets["101"]["outcomes"]
# Extract prices (nested path: outcomes -> outcomeId -> players -> "0" -> price)
home = outcomes.get("101", {}).get("players", {}).get("0", {}).get("price", "-")
draw = outcomes.get("102", {}).get("players", {}).get("0", {}).get("price", "-")
away = outcomes.get("103", {}).get("players", {}).get("0", {}).get("price", "-")
print(f"{slug}: Home {home} | Draw {draw} | Away {away}")
Step 4: Arbitrage Detection Logic
An arbitrage exists when the sum of implied probabilities across the best odds is less than 100%.
def calculate_arb(home_odds, draw_odds, away_odds):
"""
Calculate if there's an arbitrage opportunity.
Returns:
tuple: (total_implied_probability, profit_percentage)
- If total_implied < 1.0, there's an arb opportunity
- profit_percentage shows potential gain
"""
implied_home = 1 / home_odds
implied_draw = 1 / draw_odds
implied_away = 1 / away_odds
total_implied = implied_home + implied_draw + implied_away
if total_implied < 1.0:
profit = (1 - total_implied) * 100
return total_implied, profit
return total_implied, 0
# Example
total, profit = calculate_arb(2.90, 3.15, 2.85)
print(f"Total implied: {total:.4f}")
print(f"Profit margin: {profit:.2f}%")
Step 5: Complete Brazilian Arb Scanner
Here's the full working script that scans Brazilian fixtures for arbitrage opportunities:
import requests
from datetime import datetime, timedelta
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.oddspapi.io/v4"
def get_brazil_fixtures():
"""Fetch upcoming Brazilian football fixtures."""
today = datetime.now().strftime('%Y-%m-%d')
next_week = (datetime.now() + timedelta(days=7)).strftime('%Y-%m-%d')
response = requests.get(f"{BASE_URL}/fixtures", params={
"apiKey": API_KEY,
"sportId": 10,
"from": today,
"to": next_week
})
fixtures = response.json()
return [f for f in fixtures
if "brazil" in f.get("categorySlug", "").lower()
and f.get("hasOdds", False)]
def get_best_odds(fixture_id, target_bookies):
"""Get best odds across specified bookmakers for 1X2 market."""
response = requests.get(f"{BASE_URL}/odds", params={
"apiKey": API_KEY,
"fixtureId": fixture_id
})
if response.status_code != 200:
return None
bookmaker_odds = response.json().get("bookmakerOdds", {})
best = {
"home": 0, "draw": 0, "away": 0,
"home_bookie": "", "draw_bookie": "", "away_bookie": ""
}
for slug in bookmaker_odds:
if target_bookies and slug not in target_bookies:
continue
markets = bookmaker_odds[slug].get("markets", {})
if "101" not in markets:
continue
outcomes = markets["101"]["outcomes"]
home = outcomes.get("101", {}).get("players", {}).get("0", {}).get("price", 0)
draw = outcomes.get("102", {}).get("players", {}).get("0", {}).get("price", 0)
away = outcomes.get("103", {}).get("players", {}).get("0", {}).get("price", 0)
if home and home > best["home"]:
best["home"] = home
best["home_bookie"] = slug
if draw and draw > best["draw"]:
best["draw"] = draw
best["draw_bookie"] = slug
if away and away > best["away"]:
best["away"] = away
best["away_bookie"] = slug
return best
def calculate_arb(home, draw, away):
"""Calculate arbitrage opportunity."""
total = (1/home) + (1/draw) + (1/away)
profit = (1 - total) * 100 if total < 1 else 0
return total, profit
# Brazilian bookmakers to compare
BRAZILIAN_BOOKIES = [
"estrelabet", "stake.bet.br", "sportingbet.bet.br",
"superbet.bet.br", "kto", "pinnacle"
]
# Run the scanner
print("=== Brazilian Bookmaker Arbitrage Scanner ===\n")
fixtures = get_brazil_fixtures()
print(f"Scanning {len(fixtures)} Brazilian fixtures...\n")
arb_count = 0
for f in fixtures:
best = get_best_odds(f["fixtureId"], BRAZILIAN_BOOKIES)
if not best or best["home"] == 0:
continue
total, profit = calculate_arb(best["home"], best["draw"], best["away"])
if profit > 0:
arb_count += 1
print(f"🚨 ARB FOUND: {f['participant1Name']} vs {f['participant2Name']}")
print(f" Home: {best['home']:.2f} @ {best['home_bookie']}")
print(f" Draw: {best['draw']:.2f} @ {best['draw_bookie']}")
print(f" Away: {best['away']:.2f} @ {best['away_bookie']}")
print(f" Profit: {profit:.2f}%\n")
print(f"Scan complete. Found {arb_count} arbitrage opportunities.")
Bonus: Value Bet Detection (Soft vs. Sharp)
Arbitrage is rare. Value betting is more realistic. The idea: compare soft book odds (EstrelaBet, Stake BR) against Pinnacle (the sharp benchmark). When soft odds exceed sharp by a meaningful margin, you've found value.
def find_value_bets(fixture_id, soft_bookies, threshold=2.0):
"""
Find value bets by comparing soft books vs Pinnacle.
Args:
fixture_id: The fixture to analyze
soft_bookies: List of soft bookmaker slugs
threshold: Minimum edge % to flag as value (default 2%)
Returns:
List of value bet opportunities
"""
response = requests.get(f"{BASE_URL}/odds", params={
"apiKey": API_KEY,
"fixtureId": fixture_id
})
if response.status_code != 200:
return []
bookmaker_odds = response.json().get("bookmakerOdds", {})
# Get Pinnacle (sharp) prices
if "pinnacle" not in bookmaker_odds:
return []
sharp_markets = bookmaker_odds["pinnacle"].get("markets", {})
if "101" not in sharp_markets:
return []
sharp = sharp_markets["101"]["outcomes"]
sharp_prices = {
"home": sharp.get("101", {}).get("players", {}).get("0", {}).get("price", 0),
"draw": sharp.get("102", {}).get("players", {}).get("0", {}).get("price", 0),
"away": sharp.get("103", {}).get("players", {}).get("0", {}).get("price", 0)
}
value_bets = []
for soft_slug in soft_bookies:
if soft_slug not in bookmaker_odds:
continue
soft_markets = bookmaker_odds[soft_slug].get("markets", {})
if "101" not in soft_markets:
continue
soft = soft_markets["101"]["outcomes"]
for outcome_id, outcome_name in [("101", "home"), ("102", "draw"), ("103", "away")]:
soft_price = soft.get(outcome_id, {}).get("players", {}).get("0", {}).get("price", 0)
sharp_price = sharp_prices[outcome_name]
if soft_price > 0 and sharp_price > 0:
edge = ((soft_price / sharp_price) - 1) * 100
if edge >= threshold:
value_bets.append({
"outcome": outcome_name,
"soft_bookie": soft_slug,
"soft_price": soft_price,
"sharp_price": sharp_price,
"edge": edge
})
return value_bets
# Find value bets
SOFT_BOOKIES = ["estrelabet", "stake.bet.br", "sportingbet.bet.br", "superbet.bet.br"]
print("=== Value Bet Scanner: Soft Books vs Pinnacle ===\n")
for f in fixtures[:20]:
value_bets = find_value_bets(f["fixtureId"], SOFT_BOOKIES, threshold=3.0)
if value_bets:
print(f"{f['participant1Name']} vs {f['participant2Name']}")
for vb in value_bets:
print(f" {vb['outcome'].upper()}: {vb['soft_bookie']} @ {vb['soft_price']:.2f}")
print(f" vs Pinnacle @ {vb['sharp_price']:.2f} (+{vb['edge']:.1f}% edge)")
print()
Why OddsPapi for Brazilian Markets?
Three reasons this matters for Brazilian market developers:
1. 300+ Bookmakers in One API
The-Odds-API covers about 40 bookmakers. OddsPapi covers 300+, including every major Brazilian operator plus global sharps like Pinnacle and Singbet for value detection.
2. Free Historical Data
Want to backtest your Brasileirão model? Other providers charge hundreds per month for historical odds. OddsPapi includes it on the free tier.
3. Real-Time WebSockets
For live arbitrage, polling isn't fast enough. OddsPapi offers WebSocket connections for real-time odds updates during matches.
Get Started
Stop scraping. Stop paying enterprise rates for incomplete data. Get your free API key and access every Brazilian bookmaker in one call.