Polymarket exposes two distinct APIs for tennis: the Gamma API serves public market data (events, markets, prices) under tennis tag 864 and needs no key, while the CLOB API handles order placement and execution and requires wallet-signed auth. Neither returns live tennis scores — that match-state layer comes from a dedicated tennis data feed.
If you are building anything that reads or reacts to tennis prediction markets, it helps to keep those three jobs separate in your head: discover markets, read/place orders, and know what is actually happening on court. This article walks each one, with a runnable Gamma snippet, and shows where the Live Tennis API slots in as the score/status source.
The three layers, and which API owns each
| Job | Who owns it | Auth | Notes |
|---|---|---|---|
| Discover tennis markets & read prices | Polymarket Gamma API | Keyless | Public market metadata, outcomes/outcomePrices |
| Place & manage orders | Polymarket CLOB API | Wallet-signed | Execution is yours to build; use py-clob-client |
| Live match scores & status | Live Tennis API | X-API-Key |
Live matches, per-set scores, match status |
Polymarket's APIs are Polymarket's. They tell you what markets exist and let you trade them. They do not tell you that a player just got broken, took a medical timeout, or retired. That is the gap the tennis data layer fills — and it is why a bot that reads market prices alone is flying half-blind.
Polymarket Gamma API: discovering tennis markets
Gamma (https://gamma-api.polymarket.com) is the public, keyless data API. Tennis markets carry tag 864. The two useful endpoints are /events (grouped markets, e.g. a whole tournament winner) and /markets (individual yes/no markets).
A key gotcha: outcomes and outcomePrices come back as JSON-encoded strings, not arrays — you have to parse them a second time.
import requests, json
GAMMA = "https://gamma-api.polymarket.com"
# Open tennis markets, tag 864
resp = requests.get(
f"{GAMMA}/markets",
params={"tag_id": 864, "closed": "false", "limit": 20},
timeout=15,
)
resp.raise_for_status()
for m in resp.json():
# These fields are STRINGS containing JSON — parse again.
outcomes = json.loads(m.get("outcomes") or "[]")
prices = json.loads(m.get("outcomePrices") or "[]")
pairs = ", ".join(f"{o}={p}" for o, p in zip(outcomes, prices))
print(f'{m["question"]}')
print(f' {pairs}')
print(f' conditionId={m.get("conditionId")}\n')
The conditionId on each market is the handle you carry into the CLOB layer for order books and execution. The resolutionSource and description fields carry the market's own resolution wording — read those, because how a market settles (including on a retirement or walkover) is defined there, by Polymarket, not by any data feed.
Grouped tournament markets live under /events with the same tag_id=864, and each event nests a markets array — handy for "who wins the tournament" style questions.
Polymarket CLOB API: execution is yours
The CLOB (Central Limit Order Book) API is where actual trading happens: order books, order placement, cancellation, balances. It is authenticated with your wallet signature, and the maintained client is py-clob-client. Orders, wallets, private keys, and USDC are entirely the builder's responsibility — a data feed never touches them, and this article does not cover placing orders. Consult Polymarket's own CLOB docs for the current auth flow, tick sizes, and rate limits.
The clean division of labour: Gamma answers "what markets exist and what do they cost?", CLOB answers "put my order in the book." Your logic in between decides whether to act — and that decision is only as good as your read on the live match.
Where live tennis scores fit
Gamma gives you a market price; it does not tell you the score that price is reacting to. To close that loop you need a live match-state feed. The Live Tennis API free tier (30 req/min, 100/day, no card) covers exactly the read side a market bot needs:
GET /matches?status=live— live matches in progressGET /matches/{id}/score— current per-set / game scoreGET /players/{id}— player incl. current ranking and EloGET /fixtures— upcoming schedule
import requests
TENNIS = "https://api.livetennisapi.com/api/public/v1"
HEADERS = {"X-API-Key": "YOUR_FREE_KEY"}
live = requests.get(
f"{TENNIS}/matches",
params={"status": "live"},
headers=HEADERS,
timeout=15,
).json()
for match in live["data"]: # list endpoints return {"data": [...], "meta": {...}}
print(match["id"], match["status"], match.get("event_status"), match.get("outcome"),
match["players"]["p1"]["name"],
"vs",
match["players"]["p2"]["name"])
Crucially, the match object carries more than the lifecycle status (upcoming / live / completed / cancelled): it also carries event_status (the feed's own designator — Retired, Walk Over, Cancelled, Postponed, Interrupted) and a closed-vocabulary outcome (completed, retired, walkover, default, abandoned; null until settled), plus withdrew. That means a bot can detect a retirement in the data as soon as it lands (an in-play retirement is stamped within seconds of the feed stating it; a walkover declared before first serve comes through a short pre-start status poll, since no live socket can push a match that never went live), then read the relevant market's own resolution wording to decide what to do. Detecting the event and reading the venue's rule are two separate steps; the feed does the first, the venue's published rules govern the second. (We go deeper on that in Tennis retirements & walkovers on prediction markets.)
Putting it together
A minimal read loop looks like this:
- Gamma → pull open tennis markets (tag
864), keepconditionIdand currentoutcomePrices. - Live Tennis API → match those markets to live matches and poll
score+status. - Your logic → compare the market price against what the score is telling you; flag divergences or a status change (e.g. a retirement).
- CLOB (optional, your responsibility) → if you choose to act, place the order via
py-clob-client.
The open-source polymarket-tennis toolkit (MIT, observe-only) demonstrates steps 1–2: Gamma discovery, matching tennis matches to markets, and a joined live view. It is a reading and matching harness — it does not place orders.
Is the Polymarket API free? Limits?
Gamma is public and keyless for reading market data — you do not sign in to query prices. The CLOB API is free to use in the sense that there is no subscription, but it requires wallet auth and involves on-chain USDC and gas when you actually trade. For current rate limits on both, check Polymarket's docs rather than assuming — limits change. On the tennis-data side, the free Live Tennis API key is 30 req/min and 100 req/day, which is comfortable for polling a handful of live matches; higher volumes and extras (history, market prices/events, win-probability, live per-point, WebSocket) sit on the paid tiers.
For the full end-to-end build across both venues, see the pillar guide How to build a Polymarket tennis trading bot. None of this is financial advice — it is plumbing.