
Developer platform
API-first, because the website is just another client.
The public pages you have been browsing are built on the same REST and WebSocket contracts documented here. Nothing is held back for internal use.
REST
/api/v1
Fixtures, results, standings, teams, athletes, statistics.
WebSocket
wss://live
Subscribe to a match and receive every event in sequence.
Webhooks
15 events
Signed callbacks with retry and replay.
Widgets
One script tag
Drop a live scoreboard into any CMS.
Quickstart
Live scores in
three minutes
-
Create an API key
Organization settings, then Developers. Sandbox keys are free and never expire.
-
Call the matches endpoint
Filter by sport, tournament, status or date. Cursor pagination throughout.
-
Subscribe to a match
Open the WebSocket, send a subscribe frame, and take events in sequence order.
-
Handle sequence gaps
If a sequence number is missing, replay from the events endpoint. Every event is idempotent on event_id.
# Authenticate with a bearer key. Sandbox keys are prefixed sk_test_. curl https://api.arenaos.dev/v1/matches?status=live&sport=basketball \ -H "Authorization: Bearer sk_live_9f2c..." \ -H "Accept: application/json"
{
"data": [{
"match_id": "BB-4471",
"sport": "basketball",
"tournament_id": "national-basketball-championship-2026",
"status": "live",
"period": { "number": 4, "label": "Q4", "clock": "03:42" },
"competitors": [
{ "id": "mumbai-lions", "side": "home", "score": 78 },
{ "id": "bengaluru-tigers", "side": "away", "score": 72 }
],
"venue_id": "nesco-dome",
"last_sequence": 1184
}],
"meta": { "cursor": "eyJvIjoyMH0", "has_more": false }
}
const ws = new WebSocket("wss://live.arenaos.dev/v1?key=pk_live_3a91"); ws.onopen = () => ws.send(JSON.stringify({ type: "subscribe", channels: ["match:BB-4471", "tournament:nbc-2026"] })); ws.onmessage = (e) => { const ev = JSON.parse(e.data); // Events arrive in sequence order. A gap means you missed one: // replay from GET /v1/matches/{id}/events?after={last_seq} if (ev.event_type === "score.updated") render(ev.payload); };
Reference
Endpoints
| Method | Path | Returns | Cache |
|---|---|---|---|
| GET | /api/v1/sports | Enabled sports and their ruleset defaults | 1 h |
| GET | /api/v1/tournaments | Tournament list, filterable by sport, status, city, level | 5 m |
| GET | /api/v1/tournaments/{id} | Full tournament record with stages and categories | 5 m |
| GET | /api/v1/tournaments/{id}/standings | Computed table with the tie-break rules applied | 30 s |
| GET | /api/v1/tournaments/{id}/schedule | Fixtures with venue, slot and official assignments | 1 m |
| GET | /api/v1/matches | Match list by status, sport, date or tournament | 15 s |
| GET | /api/v1/matches/{id} | Match record, competitors, officials and venue | 15 s |
| GET | /api/v1/matches/{id}/live | Current score, period, clock and last sequence | 1 s |
| GET | /api/v1/matches/{id}/events | Ordered event log, replayable from any sequence | none |
| GET | /api/v1/teams/{id} | Team profile, roster and season record | 10 m |
| GET | /api/v1/athletes/{id} | Athlete profile, career statistics and match history | 10 m |
| GET | /api/v1/rankings | Ranking table for a sport and category | 1 h |
| POST | /api/v1/webhooks | Register a callback URL and event subscription | none |
Realtime
Fifteen events,
every one ordered
Subscribe over WebSocket or receive signed webhooks. Both carry the same envelope, so you can develop against one and switch later.
// Headers X-Arena-Event: score.updated X-Arena-Delivery: d_7f21c8a0 X-Arena-Signature: sha256=1c9f... // HMAC of the raw body // Body — the same envelope the scoring service commits { "event_id": "evt_9c1f04ab", "match_id": "BB-4471", "sequence_number": 1184, "timestamp": "2026-08-30T14:12:41.882Z", "actor_id": "scorer:8821", "event_type": "score.updated", "payload": { "side": "home", "points": 3, "player": 12 }, "reverses": null, "version": 1 }
Embeddable widgets
Live scores on your own site
No build step, no framework. One script tag and a div, themed to your brand on Professional and above.
<script src="https://cdn.arenaos.dev/embed.js" async></script> <div data-arena="scoreboard" data-match="BB-4471" data-key="pk_live_3a91" data-theme="dark" data-accent="#FFB01F" data-logo="https://yoursite.com/logo.svg"></div>
data-arena="scoreboard"
data-arena="standings"
data-arena="fixtures"
data-arena="results"
data-arena="team"
data-arena="player"
This is the actual widget markup, running the same engine. On your site it would be inside an isolated shadow root so your CSS and ours cannot collide.
Rate limits
Enforced per key, per organization and per subscription (§77).
| Plan | Requests / month | Burst / min | WS channels |
|---|---|---|---|
| Free | 5,000 | 60 | 2 |
| Professional | 250,000 | 600 | 25 |
| Business | 2,000,000 | 3,000 | 150 |
| Enterprise | Custom | Custom | Custom |
X-RateLimit-Limit: 250000 X-RateLimit-Remaining: 231760 X-RateLimit-Reset: 1756569600
Versioning and stability
New fields can appear in v1. Fields are never removed or retyped inside a major version.
When v2 ships, v1 keeps serving for at least a year with security fixes.
A sunset date arrives as a Deprecation and Sunset header long before anything breaks.
sk_test_ keys hit a full copy of the platform seeded with the demo data on this site.
Client libraries
Get a sandbox key
Free, no expiry, seeded with this exact dataset so your integration tests have real fixtures to work against.