BettingLab

Claude Sports Betting Assistant Tool Integration Tutorial

Marcus Hale
Marcus Hale

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:

  1. Calls get_current_edges with sport="nba", min_edge=0.03
  2. Receives 8 current opportunities from MoneyLine API
  3. Analyzes each for risk/reward and game context
  4. 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.

Build with the same data we use.

MoneyLine API powers BettingLab's edge calculations. Free tier, 1k credits/month.