Polymarket has two APIs for tennis. The Gamma API serves public market data (events, markets, prices) under tennis tag 864 and needs no key. The CLOB API handles order placement and execution and needs wallet-signed auth. Neither returns live tennis scores. That match-state layer comes from a dedicated tennis data feed.
If you build anything that reads or reacts to tennis prediction markets, keep three jobs separate. Discover markets, read or place orders, and know what is happening on court. This article walks through each one with a runnable Gamma snippet and shows where the Live Tennis API fits as the score and 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 or just retired. The tennis data layer fills that gap. 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).
One 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. No data feed defines it.
Grouped tournament markets live under /events with the same tag_id=864, and each event nests a markets array, which is handy for "who wins the tournament" questions.
Polymarket CLOB API: execution is yours
The CLOB (Central Limit Order Book) API is where 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 the builder's responsibility. A data feed never touches them, and this article does not cover placing orders. Read Polymarket's own CLOB docs for the current auth flow, tick sizes and rate limits.
The division of labour is clean. 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. 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 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"])
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, and null until settled), plus withdrew. So 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. The bot then reads 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 and 2: Gamma discovery, matching tennis matches to markets, and a joined live view. It reads and matches only. 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 has no subscription fee, but it needs wallet auth and involves on-chain USDC and gas when you trade. For current rate limits on both, check Polymarket's docs. Limits change. On the tennis-data side, the free Live Tennis API key is 30 req/min and 100 req/day. That is comfortable for polling a handful of live matches. Higher volumes and extras (history, market prices and 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.