API documentation
The EdgeLab API serves the site's data as JSON: match schedule and odds movement, the full analysis of a match, player profiles, head-to-heads, signal tracking. Every route is read-only and authenticated.
1. Authentication
A key is created from your account. It is shown once: we only keep its fingerprint, so a lost key cannot be recovered, only replaced.
curl -s "https://mma.edge-lab.io/api/v1/matches" \
-H "Authorization: Bearer $EDGELAB_API_KEY"Without a valid key: 401. A revoked key stops working on the very next call, with no delay and no cache: that is what makes revocation credible, and it is also why the key is checked against the database on every request.
2. The response envelope
A success always carries { "data": …, "meta": … }, an error always { "error": { "code": …, "message": … } }. Error codes are stable: unauthorized (401), bad_request (400), not_found (404), rate_limited (429), incomplete_response (500).
Test error.code, never the message: the message is written for a human and may be reworded; the code is part of the contract. The detail of every response, field by field, lives in the interactive reference.
3. Quotas and limit headers
Two plans, and they are not counted the same way.
- Trial: 20 calls in total, for life. There is no window that reopens, hence no
Retry-After: the message tells you to subscribe, not to wait. Replacing your key does not give back the calls you used. - Subscription: 2000 calls per Paris day. The window reopens at midnight Paris time, and
X-RateLimit-Resetsays when.
Every response, successes included, carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Window (day or lifetime). Going over returns 429, never a truncated response.
4. The viewer parameter, and what your plan does with it
Three routes accept ?viewer=free|premium. The key says who is calling; viewer says for whom you are asking: you are the authority on the tier of YOUR readers. When absent, it defaults to premium.
⚠️ The plan of the key caps this parameter. A trial key is served with viewer=free whatever it asks for: model probabilities and their weights, the value signal, the score matrix and serve statistics are absent from the body, which then carries locked: true. That is what the trial shows: the shape of the responses, not the data we sell. GET /api/v1/capabilities answers from the point of view of YOUR key and says so explicitly.
An unknown value returns 400: we do not guess on your behalf.
5. Two reading rules that avoid surprises
Player identifiers are opaque. A player_key only means something inside this API: compare them to each other, do not interpret them, do not join them to any external source. The same player has one identifier per tour, and a key may be null on an older match whose identity is not certain: treat that as « unknown identity », never as « invalid match ».
Nothing is recomputed after the fact. A value signal is frozen at the moment of the prediction, and the analysis of a started match shows the pre-match picture. That is what makes the track record verifiable, and it is also why a value will never change retroactively in your responses.
6. Versioning, and what already changed
The /api/v1 prefix is stable. We may add fields or routes without notice: your client must ignore what it does not know. Removing or renaming an existing field would require a new version.
Everything below already shipped. It is here because two of these entries changed the value of a field you may already have been reading:
- 2026-08-11 (MCP endpoint) - The API speaks MCP at `/api/mcp` (streamable HTTP): point an agent at that URL with your key and the routes appear as tools. One tool call costs one API call.
- 2026-08-10 (field metadata) - Every field published by `/capabilities` now carries `type`, and `nullable`, `format` or `enumValues` when they apply. Deserialise from the catalogue instead of guessing from an example.
- 2026-08-10 (OpenAPI spec) - The API publishes its OpenAPI 3.1 spec at `/openapi.json`, in the clear and without a key.
- 2026-08-10 (response examples) - The spec carries real response bodies, captured on production. Paid fields carry a typed placeholder and data sources are redacted; nothing else is invented.
- 2026-08-10 (reference page) - The spec has a page: `/reference`, public and keyless. It is generated from `/openapi.json`, so it cannot drift from what the API serves.
- 2026-08-09 (viewer, trial keys) - A trial key (`plan: free`, 20 calls for life) is always served as `viewer=free`, whatever `?viewer=` asks for. `meta.viewer` tells you the tier actually served, and `/capabilities` publishes your key's own plan.
- 2026-08-05 (model_prob, edge) - Probability scale corrected: values SHRINK, shape is unchanged (an edge of 16 becomes ~6). Signals captured before this date keep the old scale - `captured_at` decides.
- 2026-08-05 (player career records) - `overall` and `bySurface` now carry the OFFICIAL career scope (ATP + Challenger main draws; qualifying and ITF excluded), so the numbers dropped for players who came through the Futures. `byTier`, `form`, `charge` and `recent` are unchanged. `careerScope` says which scope a profile carries.
7. What the contract allows
Use of the API is governed by the API Terms, a contract separate from the site's. In two words: embedding our analyses inside your product, yes; reselling the feed as it comes or rebuilding the database, no.
A question, a volume need, a particular use case: hello@edge-lab.io.
8. Wiring your agent
This API describes itself, on purpose: an agent does not have to read this page to know what to call. Two addresses are enough, and both answer without a key.
⚠️ Both live at the site root, behind the bot protection that covers the pages: an automated client that is not a recognised crawler gets a challenge there (429). Open them in a browser. The /api/v1 prefix is exempt - that is where your program calls.
https://mma.edge-lab.io/openapi.json- the full OpenAPI 3.1 spec: routes, parameters, response shapes, the tier of every field, and real example bodies. This is what a client generator consumes.- https://mma.edge-lab.io/reference - the same thing for a human, with a button to try each route.
Once your key is in place, GET /api/v1/capabilities answers from the point of view of that key: the tier actually served, the fields of each response and their tier, the flags that are on. An agent that starts there will not ask for what it cannot receive.
# what YOUR key can get
curl -s https://mma.edge-lab.io/api/v1/capabilities \
-H "Authorization: Bearer $EDGELAB_API_KEY"
# today's card
curl -s https://mma.edge-lab.io/api/v1/matches \
-H "Authorization: Bearer $EDGELAB_API_KEY"Does your agent speak MCP? Paste https://mma.edge-lab.io/api/mcp into your client, with your key as a Bearer token. The nine routes show up as tools, with their parameters, plus two shortcuts for the usual journeys (today's value, and analysing a match by the players' names).
⚠️ One tool call costs one call from your quota, refusals included. Every tool description says so, but an agent looping on an error burns through a trial fast.
The free trial is 20 calls and serves real data - there is no sandbox, so there is nothing to relearn when you go to production. Count on every refused attempt consuming a call: an agent looping on an error burns through its trial.