# SportMkt API Documentation

> Real-time sports prediction market data. No signup required — every visitor gets an API key automatically.

**Base URL:** `https://sportmkt.live/api/v1`

---

## Authentication

Every request returns your API key in the `X-Api-Key` response header. For persistent identity, send it back on subsequent requests:

```http
X-Api-Key: sd_your_key_here
```

Browsers receive a cookie automatically. Bots and AI agents should store and reuse the `X-Api-Key` header value from the first response.

---

## Token System

Every API call costs **1 token**. Your tier determines how many tokens you get:

| Tier | Price | Tokens/min | Tokens/day |
|------|-------|-----------|-----------|
| Free | $0 | 30 | 2,000 |
| Basic | $19/mo | 60 | 10,000 |
| Pro | $79/mo | 300 | 100,000 |
| Enterprise | Custom | 1,000 | 1,000,000 |

Check your current usage and limits: `GET /api/v1/me`

---

## Endpoints

### GET /api/v1/me

Your API key, tier, token usage, and rate limits.

**Response:**

```json
{
  "api_key": "sd_abc123...",
  "visitor_type": "bot",
  "tier": "free",
  "tokens_today": 42,
  "tokens_total": 1337,
  "limits": {
    "tokens_per_minute": 30,
    "tokens_per_day": 2000
  },
  "member_since": "2026-03-12T00:00:00Z"
}
```

---

### GET /api/v1/events

List active events sorted by 24h volume.

**Query parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `sport` | string | Filter by sport slug (e.g. `tennis`, `basketball`, `esports`) |
| `league` | string | Filter by league name |
| `live` | boolean | Set to `true` for live events only |
| `limit` | integer | Max results (default `50`, max `200`) |
| `offset` | integer | Pagination offset |

**Example:**

```http
GET /api/v1/events?sport=tennis&live=true&limit=10
```

**Response fields per event:** `title`, `slug`, `sport`, `league`, `teams`, `score`, `period`, `live`, `volume_24h`, `liquidity`, `market_count`, `odds`, `tags`

---

### GET /api/v1/events/:slug

Full event detail with all markets and outcomes.

**Example:**

```http
GET /api/v1/events/atp-napolit-neuchri-2026-03-11
```

**Response includes:**

- Event metadata: title, sport, league, teams, score, status
- All markets: moneyline, set winner, over/under, totals
- Per market: outcomes, prices (probabilities), decimal odds, CLOB token IDs, best bid/ask, spread, 24h volume

---

### GET /api/v1/events/:slug/markets

Markets only for a given event — lighter payload than the full event endpoint.

**Example:**

```http
GET /api/v1/events/atp-napolit-neuchri-2026-03-11/markets
```

---

### GET /api/v1/live

All currently live events with real-time scores and moneyline odds. Optimized for dashboards and tickers.

**Response:**

```json
{
  "live_events": [
    {
      "title": "Player A vs Player B",
      "slug": "atp-playera-playerb-2026-03-19",
      "sport": "tennis",
      "score": "6-4, 3-2",
      "period": "Set 2",
      "odds": { "home": 0.62, "away": 0.38 }
    }
  ]
}
```

---

### GET /api/v1/sports

List of available sports with event counts and total 24h volume.

**Response:**

```json
{
  "sports": [
    {
      "sport": "tennis",
      "event_count": 45,
      "live_count": 12,
      "total_volume_24h": 125000
    }
  ]
}
```

---

### GET /api/v1/leagues

List leagues, optionally filtered by sport.

**Query parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `sport` | string | Filter by sport slug |

---

### GET /api/v1/tags

All Polymarket category tags with event counts.

---

### GET /api/v1/usage

Your hourly token usage — useful for building consumption dashboards.

**Query parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `hours` | integer | Lookback window (default `24`, max `168`) |

---

### GET /api/v1/pricing

All tiers with pricing, token limits, and feature details.

---

### GET /api/v1/token-costs

Token cost per endpoint.

---

### GET /api/v1/brokers

List all active bookmakers linked into the odds comparison (currently Betfair, Kalshi, and Limitless, alongside Polymarket itself), with how many events each is linked to. LeoVegas is temporarily disabled.

**Response:**

```json
{
  "brokers": [
    {
      "slug": "betfair",
      "name": "Betfair Exchange",
      "icon_url": "/img/betfair-icon.png",
      "is_primary": false,
      "linked_events": 96
    }
  ]
}
```

---

### GET /api/v1/events/:slug/odds

Every bookmaker's raw odds for one event, grouped by market type then by bookmaker — the full comparison table (Polymarket-implied price alongside any matched Betfair/Kalshi/Limitless odds), not a single pre-picked "best price". Useful when the question is which broker offers the best line, or where markets disagree.

**Example:**

```http
GET /api/v1/events/atp-darderi-nakashi-2026-08-11/odds
```

**Response:**

```json
{
  "event_id": 10259606,
  "event_title": "National Bank Open: Luciano Darderi vs Brandon Nakashima",
  "odds_by_market": {
    "moneyline": {
      "Kalshi": [
        {
          "bookmaker": "Kalshi",
          "market_type": "moneyline",
          "odds_home": "3.1200",
          "odds_away": "1.4300",
          "link_home": "https://kalshi.com/markets/kxatpmatch/...",
          "volume": "550021.82",
          "broker_slug": "kalshi",
          "broker_icon": "/img/kalshi-icon.png"
        }
      ]
    }
  },
  "bookmaker_count": 1
}
```

Coverage varies by sport and updates continuously: Betfair (soccer, tennis) is scraped from live odds pages; Kalshi (soccer, baseball, esports, basketball, hockey, cricket, tennis, MMA) and Limitless (tennis, soccer) are pulled from their public trade APIs. LeoVegas (soccer) is temporarily disabled. Not every event has a broker match — `bookmaker_count` tells you how many were found for a given event, and `GET /api/v1/events/:slug` also includes matched broker odds inline alongside Polymarket's own price for the same outcome.

---

### GET /api/v1/health

System status check. **Costs 0 tokens.**

**Response:**

```json
{
  "status": "ok",
  "timestamp": "2026-03-19T12:00:00Z"
}
```

---

## Rate Limiting

When you exceed your token limit the API returns **HTTP 429**:

```json
{
  "error": "Rate limit exceeded",
  "message": "You've used all 30 tokens/minute on the free tier. Upgrade for more.",
  "tier": "free",
  "tokens_per_minute": 30,
  "upgrade": { "url": "/pricing" },
  "your_api_key": "sd_..."
}
```

**Rate limit headers on every response:**

| Header | Description |
|--------|-------------|
| `X-RateLimit-Limit` | Tokens per minute for your tier |
| `X-RateLimit-Remaining` | Tokens remaining this minute |
| `X-RateLimit-Daily-Limit` | Tokens per day |
| `X-RateLimit-Daily-Remaining` | Tokens remaining today |

---

## MCP Server

The same data is available as an [MCP](https://modelcontextprotocol.io) server for AI agents that support tool calling — no separate signup, no separate key. It's mounted inside this same API, so it shares the exact key-issuance, rate-limiting, and usage accounting described above: one tool call costs the same as one REST call, against the same `tokens_today` budget.

**Endpoint:** `POST https://sportmkt.live/api/mcp`

**Transport:** Streamable HTTP, stateless (each request is a self-contained JSON-RPC call — no session to establish or keep alive). Send `Accept: application/json, text/event-stream`; the response comes back as a single Server-Sent Event.

### Tools

| Tool | Description |
|------|-------------|
| `list_sports` | Every sport tracked, with event count and 24h volume — call this first to discover valid `sport` values |
| `list_events` | Events sorted by 24h volume, filterable by `sport`, `league`, `live` |
| `list_live_events` | Events currently in play, with live score/period |
| `list_leagues` | Leagues, optionally scoped to one sport |
| `list_tags` | All tags (sports, leagues, tournaments, teams) with event counts |
| `get_event` | One event by slug — full record, every market, betting links, matched broker odds |
| `compare_odds` | Every bookmaker's raw odds for one event, grouped by market and bookmaker — the same data as `GET /api/v1/events/:slug/odds`, returned as the full table rather than a single "best price" pick, so the calling agent can do its own reasoning over it |

### Example: list available tools

```bash
curl -s https://sportmkt.live/api/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

### Example: call a tool

```bash
curl -s https://sportmkt.live/api/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": { "name": "list_sports", "arguments": {} }
  }'
```

Response (SSE-framed JSON-RPC):

```
event: message
data: {"result":{"content":[{"type":"text","text":"{\"sports\":[{\"sport\":\"tennis\",\"event_count\":\"905\",...}]}"}]},"jsonrpc":"2.0","id":2}
```

### Manual client config

For MCP clients that only support a static JSON config pointing at a remote URL (most current clients don't yet query registries or discover tools at runtime):

```json
{
  "mcpServers": {
    "sportmkt": {
      "url": "https://sportmkt.live/api/mcp"
    }
  }
}
```

Your API key is auto-issued on first call and returned in the `X-Api-Key` response header, exactly as with REST — reuse it on subsequent calls the same way.

---

## Market Types

| Type | Description |
|------|-------------|
| `moneyline` | Match winner |
| `tennis_first_set_winner` | First set winner |
| `tennis_set_totals` | Total sets over/under |
| `tennis_match_totals` | Total games over/under |
| `tennis_first_set_totals` | First set games over/under |

Prices are **market-implied probabilities** in the range `0.00`–`1.00`. Markets are binary (Yes/No) or multiple-choice.

---

## Data Source

All data comes from [Polymarket](https://polymarket.com?r=sportmkt) prediction markets via their Gamma API and real-time sports WebSocket.

---

## Quick Start (Python)

```python
import requests

BASE = "https://sportmkt.live/api/v1"

# Step 1: get your API key (auto-assigned on first request)
me = requests.get(f"{BASE}/me").json()
key = me["api_key"]
print(f"Key: {key}  Tier: {me['tier']}  Tokens today: {me['tokens_today']}")

# Step 2: fetch live events
live = requests.get(f"{BASE}/live", headers={"X-Api-Key": key}).json()
for ev in live["live_events"]:
    print(f"{ev['title']} — {ev['score']} — {ev['odds']}")

# Step 3: fetch all tennis events
events = requests.get(
    f"{BASE}/events",
    params={"sport": "tennis", "limit": 20},
    headers={"X-Api-Key": key}
).json()
for ev in events["events"]:
    print(f"{ev['title']}  vol={ev['volume_24h']}")
```

---

## Quick Start (curl)

```bash
# Your key is in the X-Api-Key response header
curl -s https://sportmkt.live/api/v1/me | python3 -m json.tool

# Save your key
KEY=$(curl -sI https://sportmkt.live/api/v1/health | grep -i x-api-key | awk '{print $2}' | tr -d '\r')

# Live events
curl -s https://sportmkt.live/api/v1/live -H "X-Api-Key: $KEY" | python3 -m json.tool

# Tennis events
curl -s "https://sportmkt.live/api/v1/events?sport=tennis" -H "X-Api-Key: $KEY" | python3 -m json.tool
```

---

## Content Negotiation

This API is agent-friendly. Omit `text/html` from your `Accept` header (or send `Accept: application/json`) and the server returns machine-readable formats:

- `GET /` → JSON API index with your key and all endpoint paths
- `GET /docs` → raw Markdown (this document)
- All `/api/v1/*` routes → JSON always

Bots and AI agents are automatically detected by `User-Agent` and served the appropriate format without needing special headers.

Agents that support MCP tool calling can skip REST entirely and use the MCP server at `POST /api/mcp` instead (see the MCP Server section above) — same key, same rate limits, same data.
