Building a Claude sports betting assistant that actually helps you find edges requires more than throwing prompts at an LLM. You need structured tool integration, smart prompt design, and real-time odds data. Here's how to build one that works.
Most "AI betting assistants" are glorified chatbots that regurgitate public information. The ones that make money integrate live odds APIs, reason about market inefficiencies, and present actionable analysis. We'll build exactly that using Claude 3.5 Sonnet, Anthropic's tool calling framework, and the MoneyLine API.
Architecture: Claude + Tools + Live Data
The assistant needs three components working together:
Claude 3.5 Sonnet handles reasoning, market analysis, and user interaction. It's consistently the strongest model for complex analytical tasks, especially when you need it to reason step-by-step through betting logic.
Anthropic's tool calling system lets Claude fetch live odds, calculate edges, and access historical data on demand. No hardcoded responses—the model decides when it needs fresh data.
MoneyLine API provides the odds feed, edge calculations, and market context. Unlike scraping sportsbooks or using stale data feeds, you get structured, reliable betting data designed for programmatic access.
Core Tool Schema
import anthropic
import requests
from typing import Dict, Any, List
class BettingAnalyst:
def __init__(self, anthropic_key: str, moneyline_key: str):
self.client = anthropic.Anthropic(api_key=anthropic_key)
self.ml_headers = {"Authorization": f"Bearer {moneyline_key}"}
self.ml_base = "https://mlapi.bet"
def get_tools(self) -> List[Dict[str, Any]]:
return [
{
"name": "get_current_edges",
"description": "Fetch current positive EV opportunities from MoneyLine API",
"input_schema": {
"type": "object",
"properties": {
"sport": {"type": "string", "enum": ["nba", "nhl", "mlb", "nfl"]},
"min_edge": {"type": "number", "description": "Minimum edge percentage (e.g. 0.05 for 5%)"},
"max_edge": {"type": "number", "description": "Maximum edge percentage to filter noise"}
},
"required": ["sport"]
}
},
{
"name": "analyze_specific_game",
"description": "Get detailed odds and analysis for a specific matchup",
"input_schema": {
"type": "object",
"properties": {
"team_a": {"type": "string"},
"team_b": {"type": "string"},
"sport": {"type": "string"}
},
"required": ["team_a", "team_b", "sport"]
}
},
{
"name": "get_sportsbook_comparison",
"description": "Compare odds across sportsbooks for arbitrage opportunities",
"input_schema": {
"type": "object",
"properties": {
"event_id": {"type": "string"},
"market_type": {"type": "string", "enum": ["moneyline", "spread", "total"]}
},
"required": ["event_id", "market_type"]
}
}
]
Prompt Engineering for Betting Analysis
The system prompt determines how Claude approaches betting analysis. Generic "be helpful" prompts produce generic analysis. You need specific reasoning frameworks:
SYSTEM_PROMPT = """You are a professional sports betting analyst with access to live odds data and edge calculations. Your job is to help users identify profitable betting opportunities through rigorous analysis.
ANALYSIS FRAMEWORK:
1. Always fetch current data before making any recommendations
2. Calculate implied probability vs. true probability when evaluating edges
3. Consider bankroll management - never recommend betting more than 2-5% on any single play
4. Explain your reasoning step-by-step, including what data influenced your analysis
5. Flag when you don't have sufficient data or when markets look efficient
TOOL USAGE:
- Use get_current_edges to find systematic opportunities
- Use analyze_specific_game when users ask about particular matchups
- Use get_sportsbook_comparison when looking for arbitrage spots
RESPONSE STYLE:
- Lead with the recommendation (bet/pass/wait)
- Show your math (implied odds, true probability, edge calculation)
- Include risk assessment and position sizing
- Never guarantee outcomes - betting involves risk
Current date: {current_date}
"""
def get_betting_advice(self, user_message: str) -> str:
messages = [
{"role": "user", "content": user_message}
]
response = self.client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=2000,
system=SYSTEM_PROMPT.format(current_date="2026-05-22"),
messages=messages,
tools=self.get_tools()
)
# Handle tool calls
if response.stop_reason == "tool_use":
return self.process_tool_calls(response, messages)
return response.content[0].text
Tool Implementation and API Integration
The tool functions bridge Claude's reasoning with live betting data. Each function needs error handling and data validation:
def execute_tool(self, tool_name: str, tool_input: Dict[str, Any]) -> str:
if tool_name == "get_current_edges":
sport = tool_input["sport"]
min_edge = tool_input.get("min_edge", 0.03) # Default 3% minimum
max_edge = tool_input.get("max_edge", 0.20) # Cap at 20% to filter obvious errors
response = requests.get(
f"{self.ml_base}/v1/edge",
headers=self.ml_headers,
params={
"sport": sport,
"min_edge": min_edge,
"max_edge": max_edge,
"limit": 20
}
)
if response.status_code != 200:
return f"Error fetching edges: {response.status_code}"
edges = response.json()
if not edges.get("opportunities"):
return f"No edges found for {sport} with minimum {min_edge*100:.1f}% edge"
# Format for Claude's analysis
formatted = []
for opp in edges["opportunities"][:10]: # Limit to top 10
formatted.append({
"matchup": f"{opp['away_team']} @ {opp['home_team']}",
"bet": f"{opp['side']} {opp['line']}",
"odds": opp["odds"],
"sportsbook": opp["sportsbook"],
"edge": f"{opp['edge']*100:.2f}%",
"game_time": opp["start_time"]
})
return json.dumps({"current_edges": formatted}, indent=2)
elif tool_name == "analyze_specific_game":
team_a = tool_input["team_a"]
team_b = tool_input["team_b"]
sport = tool_input["sport"]
response = requests.get(
f"{self.ml_base}/v1/events",
headers=self.ml_headers,
params={
"sport": sport,
"team": team_a # Search by one team, API returns matchups
}
)
if response.status_code != 200:
return f"Error fetching game data: {response.status_code}"
events = response.json()
game = None
for event in events.get("events", []):
if team_b.lower() in event.get("matchup", "").lower():
game = event
break
if not game:
return f"Could not find matchup between {team_a} and {team_b}"
# Get detailed odds for this game
odds_response = requests.get(
f"{self.ml_base}/v1/odds/{game['event_id']}",
headers=self.ml_headers
)
if odds_response.status_code == 200:
game["detailed_odds"] = odds_response.json()
return json.dumps(game, indent=2)
elif tool_name == "get_sportsbook_comparison":
event_id = tool_input["event_id"]
market_type = tool_input["market_type"]
response = requests.get(
f"{self.ml_base}/v1/odds/{event_id}",
headers=self.ml_headers,
params={"market": market_type}
)
if response.status_code != 200:
return f"Error fetching odds comparison: {response.status_code}"
return response.text
return "Unknown tool"
Real User Experience: Query to Analysis
Here's what the user actually sees when they ask: "Any good NBA plays tonight?"
User Input:
Any good NBA plays tonight?
Claude's Internal Process:
- Calls
get_current_edgeswith sport="nba", min_edge=0.03 - Receives 8 current opportunities from MoneyLine API
- Analyzes each for risk/reward and game context
- Formats recommendation with reasoning
User Output:
**TOP NBA PLAYS - May 22, 2026**
🟢 **STRONG PLAY: Cavaliers ML +165 (DraftKings)**
- Edge: 6.2%
- My Analysis: DraftKings has Cleveland at 37.7% implied probability, but my model shows 43.9% true probability based on:
- Home court advantage (4.1% boost)
- Rest advantage (Cavs had 1 extra day)
- Recent form (4-1 vs spread last 5)
- Risk Assessment: Medium - playoff games are volatile, but this line looks soft
- **Recommended bet: 2-3% of bankroll**
🟡 **MODERATE PLAY: Lakers -3.5 +102 (BetMGM)**
- Edge: 4.1%
- Line looks 1-2 points off fair value, but Lakers road playoff performance has been inconsistent
- **Recommended bet: 1-2% of bankroll**
❌ **PASS: Warriors +8.5 -110 (Multiple books)**
- Only 2.8% edge and Warriors have injury concerns with Curry questionable
- Market looks efficient here
**Bankroll Note**: These are +EV plays, not guarantees. Never bet more than you can afford to lose.
The assistant fetched live data, calculated edges, and provided specific position sizing—exactly what a sharp bettor needs.
Advanced Features: Context and Memory
For production use, add conversation memory and bet tracking:
class EnhancedBettingAnalyst(BettingAnalyst):
def __init__(self, anthropic_key: str, moneyline_key: str):
super().__init__(anthropic_key, moneyline_key)
self.conversation_history = []
self.user_preferences = {}
def track_bet_outcome(self, bet_details: Dict[str, Any], outcome: str):
"""Track recommended bets for performance analysis"""
bet_record = {
**bet_details,
"outcome": outcome,
"timestamp": datetime.now().isoformat(),
"roi": self.calculate_roi(bet_details, outcome)
}
# Store in database or file for analysis
def get_personalized_prompt(self, user_id: str) -> str:
prefs = self.user_preferences.get(user_id, {})
bankroll_size = prefs.get("bankroll_size", "moderate")
risk_tolerance = prefs.get("risk_tolerance", "medium")
sports_focus = prefs.get("sports", ["nba", "nfl"])
return f"""
{SYSTEM_PROMPT}
USER PREFERENCES:
- Bankroll Size: {bankroll_size}
- Risk Tolerance: {risk_tolerance}
- Preferred Sports: {', '.join(sports_focus)}
- Adjust position sizing accordingly
"""
Deployment and Performance Monitoring
Deploy via FastAPI for web integration or Discord.py for bot deployment. Monitor both Claude API costs and MoneyLine API usage:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
analyst = BettingAnalyst(anthropic_key="...", moneyline_key="...")
class BettingQuery(BaseModel):
message: str
user_id: str
@app.post("/analyze")
async def get_analysis(query: BettingQuery):
try:
response = analyst.get_betting_advice(query.message)
return {"analysis": response, "status": "success"}
except Exception as e:
return {"error": str(e), "status": "error"}
@app.get("/health")
async def health_check():
# Test both APIs are responsive
ml_health = requests.get(f"{analyst.ml_base}/v1/health",
headers=analyst.ml_headers)
return {
"claude": "operational",
"moneyline_api": "operational" if ml_health.status_code == 200 else "error"
}
The key is treating this as a product, not a demo. Track usage, monitor accuracy, and iterate based on user feedback. The MoneyLine API provides the reliable odds data foundation—Claude provides the reasoning layer that turns data into decisions.
Frequently Asked Questions
Q: How accurate are Claude's betting predictions compared to traditional models? A: Claude doesn't predict outcomes—it analyzes market inefficiencies. The assistant finds mispriced lines by comparing bookmaker odds to fair value estimates. Success depends on the quality of your edge detection system, not Claude's predictive ability.
Q: What's the cost to run this Claude sports betting assistant? A: Claude 3.5 Sonnet costs ~$3 per million input tokens and ~$15 per million output tokens. A typical betting analysis query costs $0.02-0.05. MoneyLine API starts free with 1k credits/month. Budget $50-100/month for moderate usage.
Q: Can I integrate this with my existing betting bot or Discord server? A: Yes. The tool calling architecture works with any Python environment. For Discord integration, use discord.py and call the analyst in your message handlers. For web apps, wrap it in FastAPI or Flask endpoints.
Q: How do I prevent the assistant from making bad recommendations? A: Set strict tool parameters (minimum edge thresholds, maximum bet sizes), validate all API responses, and include risk warnings in every output. Never let the assistant recommend betting more than 5% of bankroll on any play.
Q: Does this work for live betting or just pre-game? A: The MoneyLine API includes live odds, so Claude can analyze in-game opportunities. However, live betting requires faster execution—consider WebSocket connections and automated position sizing for time-sensitive edges.