API key Sign in, or get an instant trial key — no signup required.

Tennis

Tennis API for Scores, Odds & AI Agents

One event graph for ATP/WTA tennis — set-by-set schedules and live scores, moneyline / games-handicap / total-games odds across nine books, rankings-and-serve-rate /stats, and a market-anchored intelligence Price plane agents can reason over.

Available data

Everything below filters on sport=tennis; narrow to a tour with league=atp or league=wta. Discover players ranked with GET /v1/players?sport=tennis&ranked=true.

LayerEndpointTennis notes
Schedule / boardGET /v1/eventssport=tennis; league=atp/wta; status, date, include_odds / include_scores
Live scoreGET /v1/events/{id}/score · SSESet-by-set sets_won / sets[]; clock is null
OddsGET …/odds · …/odds/historyMoneyline (h2h), games handicap (spreads), total games (totals) · 9 books
Period oddsGET …/period-oddsFirst-set (S1) games handicap / total
Player propsGET …/player-propsNot available for tennis
Team totalsGET …/team-propsNot available — individual sport
SplitsGET …/splitsNot available for tennis
Stats (Data)GET …/statsRankings, form, surface form, H2H, career serve/return rates — singles main-draw only
IntelligenceGET …/intelligenceStage 1: moneyline + games handicap Price overlay; totals not yet published
EV scan (Beta)GET /v1/intelligence/evPregame moneyline/spreads +EV list (market=h2h|spreads; totals returns 400). MCP list_ev

Schedules

List the draw with GET /v1/events?sport=tennis. Add league=atp or league=wta to narrow by tour. Filter by status (scheduled / inprogress / final), date or from/to (max 90 days per request), and paginate with after_id / limit.

Retirements and walkovers surface via event status/result fields rather than a missing row.

Scores

Tennis score payloads are set-based: sets_won (player_1/player_2), winner_role once decided, and a sets[] array with player_1_games / player_2_games / tiebreak_score per set. Game clock is null (not applicable to tennis).

  • GET /v1/events?sport=tennis&status=inprogress&include_scores=true — live board
  • GET /v1/events/{id}/score — lightweight poll
  • GET /v1/events/{id}/stream — SSE event: score or signed webhooks

Cloneable pattern: live scoreboard use case (keep keys server-side).

Odds

Tennis markets are two-way: moneyline (h2h), games handicap (spreads), and total games (totals). American odds integers; point is null on moneyline sides. Default single-book call is Pinnacle; bookmaker=all is the same 1 credit.

Tennis is the one grading gap on final events: game-count isn't tracked through settlement, so result/close are only populated for moneyline — spreads/totals stay unresolved after the match ends. GET /v1/events/{id}/odds/history still returns recorded price/point moves for every market.

Full bookmaker list: Sports Odds API.

Stats (Data layer)

GET /v1/events/{id}/stats returns a tennis-native payload, player-keyed (player_1/player_2) rather than home/away:

  • Current ATP/WTA singles ranking + ranking_points · rest_days since each player's last completed match
  • recent_form (all surfaces) and surface_form (this match's surface) — trailing results, sets for/against
  • record — win/loss over a trailing lookback window from our own event history
  • career_rates — career hold %, break-point conversion/save %, first/second-serve-won % when the vendor has persisted match-stats
  • career_surface — trailing win/loss on this match's surface (hard/clay/grass/indoor hard/carpet)
  • head_to_head — up to 10 prior meetings with set scores and surface

No confidence, narrative, or odds inside /stats. available is false unless draw_type is singles and both players resolve (doubles and qualifying are out of scope today). Pair with /intelligence when you want the market Price plane on the same ID. Fields: API reference — stats.

Intelligence

Tennis /intelligence is Stage 1: a customer surface for moneyline and games handicap only. Framework plumbing (phase, blend_w, p_model, p_market, sufficiency, drivers, alignment, model_version) is omitted. probability / fair_price are a vig-stripped market reference — not a model pick — and edge / tier / has_recommend stay null/false until Edge clears.

  • fair — sharp-consensus fair (single-book Pinnacle reference is valid for tennis; no second sharp book)
  • edges_by_book — price gap vs fair.probability per soft book
  • best — highest-gap book/price for line-shopping (Tier C informational)
  • ev (Beta) — the same positive gap packaged as ev_pct + Kelly, on moneyline and spreads. Scan with GET /v1/intelligence/ev?market=h2h or spreads (totals returns 400).

Tokens: ML_P1/ML_P2, SPREAD_P1/SPREAD_P2 — no OVER/UNDER yet (total games stays unpublished). Price ≠ Edge. A positive gap is not a bet recommendation. Always check available before reading bets[].

Sportsbooks

Odds keys: pinnacle, fanduel, draftkings, betmgm, caesars, bet365, circa, hardrock, betonline. Availability varies by event — check each bookmaker entry. Tennis intelligence fair reference is Pinnacle (single sharp book).

Examples

Today's WTA slate with odds inlined:

curl
curl "https://lumify.ai/v1/events?sport=tennis&league=wta&status=scheduled&include_odds=true&limit=5" \
  -H "Authorization: Bearer YOUR_API_KEY"
Python
from lumify import Lumify

client = Lumify(api_key="YOUR_API_KEY")
page = client.events.list(
    sport="tennis",
    league="wta",
    status="scheduled",
    include_odds=True,
    limit=5,
)
for event in page["events"]:
    print(event["id"], event["name"], event.get("odds"))
TypeScript
import { Lumify } from "@lumifyai/sdk";

const client = new Lumify({ apiKey: "YOUR_API_KEY" });
const { events } = await client.events.list({
  sport: "tennis",
  league: "wta",
  status: "scheduled",
  includeOdds: true,
  limit: 5,
});
events.forEach(e => console.log(e.id, e.name, e.odds));

Intelligence Price surface (1 credit when available):

curl
curl "https://lumify.ai/v1/events/17438/intelligence" \
  -H "Authorization: Bearer YOUR_API_KEY"
Python
intel = client.events.intelligence(17438)
if intel.get("available"):
    for bet in intel.get("bets") or []:
        print(
            bet.get("bet_type"),
            bet.get("probability"),
            bet.get("fair"),
            bet.get("best"),
        )
TypeScript
const intel = await client.events.intelligence(17438);
if (intel.available) {
  for (const bet of intel.bets ?? []) {
    console.log(bet.bet_type, bet.probability, bet.fair, bet.best);
  }
}

Player-keyed Data-layer stats — rankings, form, career serve/return rates:

curl
curl "https://lumify.ai/v1/events/17438/stats" \
  -H "Authorization: Bearer YOUR_API_KEY"
Python
stats = client.events.stats(17438)
if stats.get("available"):
    p1 = stats["players"]["player_1"]
    print(p1["name"], p1["ranking"], p1.get("career_rates"))
TypeScript
const stats = await client.events.stats(17438);
if (stats.available) {
  const p1 = stats.players.player_1;
  console.log(p1.name, p1.ranking, p1.career_rates);
}

Sample odds shape (abridged moneyline + games handicap):

json
{
  "event_id": 17438,
  "available": true,
  "bookmakers": [
    {
      "bookmaker": "pinnacle",
      "markets": [
        {
          "key": "spreads",
          "label": "spread",
          "outcomes": [
            { "outcome": "Alexandra Eala", "price": -115, "point": -3.5 },
            { "outcome": "Opponent", "price": -105, "point": 3.5 }
          ]
        }
      ],
      "captured_at": "2026-08-10T17:05:00Z"
    }
  ]
}

Freshness

FeedCadenceFit
Live scores~1 minute (+ SSE/webhooks)Scoreboards, in-match agents
Odds ingest~10 minutes (2-min response cache)Research, line shop, alerts — not HFT
StatsRankings/form on ingest; career rates after vendor persistsData layer, singles main-draw only
IntelligenceAfter publish runsCheck available; moneyline + spreads only

Pricing

CallCredits
Most successful GETs (events, score, stats, intelligence, …)1
Multi-book odds (bookmaker=all or a list)1
available: false / errors0

Free Tier: 1,000 credits that never expire. Instant trial: 100 credits / 14 days, no signup. Details: /pricing.

For agents: machine-readable twin at /sports/tennis-api.md. MCP tools: list_events, get_odds, get_stats, get_intelligence, get_period_odds, list_ev — setup at /sports-mcp-server.

FAQ

What does the Tennis API include?

Filter with sport=tennis (league atp or wta) for schedules, set-by-set live scores, moneyline / games-handicap / total-games odds across nine books, tennis-native /stats (rankings, form, career serve/return rates), and predictive /intelligence with a sharp fair + cross-book Price surface. Same event IDs across every layer.

Why doesn't tennis intelligence have totals yet?

Tennis /intelligence is Stage 1 — moneyline (ML_P1/ML_P2) and games handicap (SPREAD_P1/SPREAD_P2) only. Total games is not yet published on bets[], and GET /v1/intelligence/ev?market=totals returns 400 for tennis. Moneyline and spreads both clear on list_ev / bets[].ev (Beta) today.

Do you support tennis player props or betting splits?

No. Player props are NFL/NCAAF/NBA/NCAAB/NHL/MLB only, and public ticket%/handle% splits cover MLB/NBA/NHL/NFL only — both 400 for tennis. Team totals don't apply either (individual sport). Use odds, /stats, and intelligence (where available) on the same event IDs.

What's in tennis /stats that isn't anywhere else?

Beyond the schedule/score graph, GET /v1/events/{id}/stats returns each player's ATP/WTA singles ranking, rest days, trailing form (overall and filtered to this match's surface), a lookback win/loss record, and — when the vendor has persisted match-stats for that player — career serve/return rates (hold_pct, break_pts_converted, first/second-serve-won) and trailing surface win/loss. Plus up to 10 prior head-to-head meetings with set scores. No scoring or narrative — that's the Data layer.

Does /stats cover doubles or qualifying?

No. /stats returns available: false unless draw_type is exactly singles and both player IDs resolve. Schedules, scores, and odds still cover the full ATP/WTA draw — the Data-layer aggregates are singles-only for now.

Start with the Tennis API

Create a free key — or use an instant trial key with no signup — and pull today's ATP/WTA slate with odds in under a minute.