Skip to content

Guides

Building a Pinnacle odds bot with Claude or Cursor using MCP

7 min readPinnRadar team

Connect Claude, Cursor, VS Code or Codex to PinnRadar's MCP server, learn its eight odds tools and their quota cost, and script it with the Claude API.

You can wire an AI assistant to live Pinnacle prices without writing an API client. Connect Claude, Cursor, VS Code or Codex to PinnRadar's MCP server and ask in plain language: "which NBA games are live", "show the totals ladder for this fixture", "what fell more than 8% in the last ten minutes". The assistant picks the tool and the parameters and reads the answer back.

This post covers what MCP is, how to connect each client, the eight tools and their parameters, prompts that work well, and a short script that runs the same tools from the Claude API.

What MCP is, briefly

The Model Context Protocol (MCP) is an open protocol for connecting AI applications to external tools and data. A server publishes a list of tools, each with a name, a description and an input schema. A client such as Claude Code or Cursor shows that list to the model. When the model decides a tool would help, the client calls it and passes the result back into the conversation.

PinnRadar's server is remote, so there's nothing to install:

  • URL: https://mcp.pinnradar.com/mcp
  • Transport: Streamable HTTP, stateless, POST only
  • Auth: OAuth 2.1 for clients that support it (claude.ai), or your API key as a Bearer token for everything else

Calls run on your own key and plan and show up in your dashboard next to your REST traffic. There is no separate MCP charge, and every plan includes it, Free too. The MCP page has the overview.

Connecting your client

First get a free key from the dashboard and export it:

export PINNRADAR_KEY="paste-your-key-here"

Claude Code

claude mcp add -t http -H "Authorization: Bearer $PINNRADAR_KEY" pinnradar https://mcp.pinnradar.com/mcp

Run /mcp in a session. pinnradar should be listed with 8 tools. For a team, commit the entry to .mcp.json and let each developer's key come from their own environment:

{
  "mcpServers": {
    "pinnradar": {
      "type": "http",
      "url": "https://mcp.pinnradar.com/mcp",
      "headers": { "Authorization": "Bearer ${PINNRADAR_KEY}" }
    }
  }
}

Cursor

Add this to ~/.cursor/mcp.json (global) or .cursor/mcp.json (per project):

{
  "mcpServers": {
    "pinnradar": {
      "headers": { "Authorization": "Bearer PASTE_KEY_HERE" },
      "url": "https://mcp.pinnradar.com/mcp"
    }
  }
}

Claude Desktop uses the same block with "type": "http" added.

VS Code

In .vscode/mcp.json, an input prompt keeps the key out of the file:

{
  "inputs": [
    { "id": "radar-key", "type": "promptString", "description": "Your PinnRadar key", "password": true }
  ],
  "servers": {
    "pinnradar": {
      "type": "http",
      "headers": { "Authorization": "Bearer ${input:radar-key}" },
      "url": "https://mcp.pinnradar.com/mcp"
    }
  }
}

Codex CLI

In ~/.codex/config.toml:

[mcp_servers.pinnradar]
bearer_token_env_var = "PINNRADAR_KEY"
url = "https://mcp.pinnradar.com/mcp"

claude.ai

Open Settings, then Connectors, then Add custom connector. Use the name pinnradar and the URL https://mcp.pinnradar.com/mcp, and leave the OAuth client fields empty. Click Add, then Connect, then approve with your key on the PinnRadar authorisation page. After that the connector runs on a short-lived token that refreshes itself. Removing the connector revokes access.

Does the model see my key?

No. The key travels in the connection's HTTP header, never in the chat. No tool takes an identity argument, and my_usage returns plan and usage only, with no email or billing data.

The eight tools

ToolWhat it returnsMain parametersQuota
board_sportsSports and their numeric idsnoneFree
find_matchesLive or prematch events of one sport, one short row each, with the full-game moneylinesport (id or name, for example 3, "tennis", "NHL"), mode (live/prematch, default live), query (all terms must match league, home or away), limit (1-100, default 25), response_format1 request
match_pricesMoneyline, spreads, totals and team totals for one matchevent_id, mode, markets (money_line, spreads, totals, team_total), periods (default [0]), response_format (concise keeps the five lines around the main line)1 request
upcoming_linesEvery alternate line of one market on a prematch matchevent_id, market_type (default totals)1 request
price_movesRecent sharp falls (steam moves), most recent firstmode, sport, min_drop_pct (default 5), max_age_sec (default 600, max 10800), markets (moneyline, spread, total, team_total), periods, limit, response_format1 request
feed_healthFeed status and how current the data isnone1 request
my_usagePlan, limits, expiry, today's request countnone1 request
search_pinnradar_docsMatching sections of the API referencequery, limit (1-5)Free

Note the naming difference. match_prices and upcoming_lines use the board's market names (money_line, spreads), while price_moves uses the drop-buffer names (moneyline, spread). The model reads the schemas, so you rarely type these yourself.

upcoming_lines works on prematch matches only. For an in-play match, ask for match_prices with response_format: "detailed".

Prompts that work well

Each prompt below says which tools it will most likely trigger, so you can predict the quota cost.

  • "Which basketball games are live right now? Show the moneyline." One find_matches call. board_sports is free if the model needs to look up the id.
  • "Totals for tonight's Serie A games." find_matches with mode: prematch and query: "serie a", then one match_prices call per game. A ten-game slate is about 11 requests.
  • "For Real Betis v Villarreal, list every handicap line, not just the main ones." find_matches plus upcoming_lines with market_type: "spreads".
  • "What soccer prices fell 8% or more in the last 15 minutes? Only full-game totals." One price_moves call with sport: 1, min_drop_pct: 8, max_age_sec: 900, markets: ["total"], periods: [0].
  • "The board looks empty. Is the feed OK?" feed_health. It tells a quiet market apart from a feed that is held back because data went stale.
  • "How many requests do I have left today?" my_usage.
  • "How do I get player props over REST?" search_pinnradar_docs, which is free.

Tips:

  • Name the league or team. A full soccer board is large, so find_matches without a query returns capped rows plus a note on what was left out.
  • Reuse ids. Ask the assistant to keep event ids from earlier in the conversation instead of listing the board again.
  • Ask for concise output unless you need the full ladder.

Quota: the one thing to plan for

Every data tool call fetches live prices with no cache in between and counts as one request on your plan, exactly like a REST call. Identical calls made in the same instant are merged. board_sports and search_pinnradar_docs are free.

On the Free plan, that means 20 requests a minute and 100 a day. A long exploratory session can use up the daily allowance. Once you hit rate_limited, the server stops calling upstream until your window resets, and my_usage tells you where you stand. On REST or Edge (10 requests a second) or Scale (30), the limits rarely matter for chat use. See the plan guide.

MCP can't stream. A tool returns the state at the moment it is called. For pushed updates, connect directly to the SSE drop streams or the WebSocket feed.

A small scheduled "bot" with the Claude API

The same server works through the Claude API's MCP connector, which turns it into a scripted report you can run from cron. This example asks for a summary of recent prematch soccer moves. It enables only the two tools it needs, which also caps the quota it can spend. It uses the official anthropic Python SDK and reads ANTHROPIC_API_KEY and PINNRADAR_KEY from the environment.

import os

import anthropic

client = anthropic.Anthropic()

response = client.beta.messages.create(
    model="claude-opus-5-5",
    max_tokens=4000,
    betas=["mcp-client-2025-11-20"],
    mcp_servers=[{
        "type": "url",
        "url": "https://mcp.pinnradar.com/mcp",
        "name": "pinnradar",
        "authorization_token": os.environ["PINNRADAR_KEY"],
    }],
    tools=[{
        "type": "mcp_toolset",
        "mcp_server_name": "pinnradar",
        "default_config": {"enabled": False},
        "configs": {"price_moves": {"enabled": True}, "board_sports": {"enabled": True}},
    }],
    messages=[{
        "role": "user",
        "content": (
            "Using price_moves once (mode prematch, sport soccer, min_drop_pct 4, "
            "max_age_sec 3600), list the five largest falls as a table: match, "
            "market, old price, new price, percent. Then add one neutral sentence on "
            "which leagues appear most. No betting advice."
        ),
    }],
)

if response.stop_reason == "refusal":
    print("request declined")
else:
    for block in response.content:
        if block.type == "text":
            print(block.text)

The MCP connector needs both halves: the server in mcp_servers and a matching mcp_toolset entry in tools. default_config with configs works as an allowlist. Check Anthropic's MCP connector documentation for the current beta header before you deploy.

Run it every hour and it uses one or two PinnRadar requests per run. Send the output wherever you like: a file, an email, a chat webhook.

Troubleshooting

  • Tools don't appear. Check the URL is exactly https://mcp.pinnradar.com/mcp and the header is Authorization: Bearer <key>. A pasted key with a trailing space is a common cause.
  • rate_limited. You've used a window. Ask my_usage, or wait for the reset (the daily window resets at 00:00 UTC).
  • Empty board. Ask feed_health before assuming there are no matches.
  • Still stuck. Message @PinnRadarSupport on Telegram or write to [email protected] with your client name and the error.

For the underlying endpoints the tools call, see the API docs and the Python REST guide.

Questions readers ask

What is the MCP server URL?

https://mcp.pinnradar.com/mcp. It uses Streamable HTTP, is stateless, and accepts POST only. Authenticate with OAuth 2.1 (claude.ai) or with Authorization: Bearer <your key>.

Is MCP charged separately?

No. It comes with every key, the Free plan included. Each data tool call counts as one request on your plan. board_sports and search_pinnradar_docs are free.

Which AI clients are supported?

Claude Code, Claude Desktop, claude.ai, Cursor, VS Code, the Codex CLI and the MCP connector in the Claude API. In general, any client that can reach a remote Streamable HTTP MCP server works.

Can the assistant stream odds?

No. MCP is request and response, so each tool returns the state at the moment it is called. For pushed updates, use the SSE drop streams or the WebSocket feed directly.

Does the model see my API key?

No. The key travels in the connection's HTTP header, not in the conversation, and no tool takes an identity argument.

Pull Pinnacle prices into your own code

The PinnRadar odds APIcarries Pinnacle’s live and prematch markets, drop alerts included, over REST, SSE and WebSocket. Compare the plans, or start on a free key: just an email, and no Pinnacle account needed.

Get a free key →