B2B integration

Universal API — Sports

Server-side API for soccer, cricket, tennis, horse racing, and greyhound racing match lists and live odds from the kingexch365 exchange feed.

Base URL: https://universeapi.store

Overview

Sports data is separate from the casino live tables product. Your account must have the Sports product enabled (see Admin → client products).

  • Soccer — sportId=1
  • Tennis — sportId=2
  • Cricket — sportId=4
  • Horse Racing — sportId=7
  • Greyhound Racing — sportId=4339

Product event ID format: {sportId}.{exEventId} (e.g. 1.100202662715511777).

Product access

Each B2B API key is entitled to one or both products: Casino and Sports. If Sports is not enabled, sports endpoints return:

HTTP 403Copy
{
  "error": "Product not enabled: sports",
  "products": { "casino": true, "sports": false }
}

Authentication

Send your API key on every request:

HeaderCopy
X-API-Key: your-partner-api-key

Or:

BearerCopy
Authorization: Bearer your-partner-api-key

IP allowlist

Admin → client Allowed IPs applies to all B2B sports REST and WebSocket calls from your servers:

  • Leave empty — any source IP may use the API key (default).
  • Non-empty — only those IPs (your backend egress) are accepted. Others get 403 / WebSocket close IP not allowlisted.

The gateway reads the first hop of X-Forwarded-For when present (set by the reverse proxy in front of the API).

API key & endpoint setup

Every B2B integration needs one real API key and one base URL. Follow these steps end to end; the same key powers events, odds, results and TV embeds.

1

Request a B2B API key

Email the provider with your company name, brand domain(s) and the products you need (Sports, Casino or both). You receive a key in the form uapi_live_xxxxxxxxxxxxxxxx plus an allowlisted origin list.
2

Store the key server-side

Keep the key in a server environment variable — never in browser code, mobile bundles or public repos.
.env (backend only)Copy
UAPI_BASE_URL=https://universeapi.store/api
UAPI_KEY=uapi_live_xxxxxxxxxxxxxxxx
3

Verify the key

A 200 response with a sports array means the key and entitlements are active.
curlCopy
curl -s "$UAPI_BASE_URL/sports" \
  -H "X-API-Key: $UAPI_KEY"
4

Proxy the API from your backend

Expose your own thin routes so the key never reaches the client. This site uses exactly that pattern: /api/public/uapi/* forwards to the provider and injects the credential server-side.
Node / TypeScript proxyCopy
export async function uapi(path: string) {
  const res = await fetch(`${process.env.UAPI_BASE_URL}/${path}`, {
    headers: { "X-API-Key": process.env.UAPI_KEY!, accept: "application/json" },
  });
  if (!res.ok) throw new Error(`Upstream ${res.status}`);
  return res.json();
}
5

Wire the front end

Call your proxy routes only. Poll the event list every 10–15s and odds every 1–2s for the event the user is viewing.

Your key setup

Stored only in this browser to generate your snippets. Production keys must live in a server environment variable.

# .env (backend only)
UAPI_BASE_URL=https://universeapi.store/api
UAPI_KEY=uapi_live_xxxxxxxxxxxxxxxx

# verify
curl -s "https://universeapi.store/api/sports" -H "X-API-Key: $UAPI_KEY"

Rotate keys from the partner portal. A rotated key invalidates old TV embed tokens within 60 seconds.

Sport IDs

sportIdSport
1Soccer
2Tennis
4Cricket
7Horse Racing
4339Greyhound Racing

Event fields

Each object in events[] (from GET /sports/events or GET /sports/:sportId/events) includes kingexch-style capability flags alongside match-odds snapshot data.

FieldTypeDescription
tournamentIdstring | nullExchange tournament ID (when published by upstream).
tournamentNamestring | nullHuman-readable tournament or competition name.
isBookmakersbooleanEvent has bookmaker markets available.
isFancybooleanEvent has fancy / line markets available.
isSportsbookbooleanEvent has sportsbook markets available.
isStreaming, tvbooleanHint that upstream advertises live TV for this event. tv is an alias of isStreaming. Flags can lag — a true value does not guarantee the CDN is publishing yet, and false does not mean mint will always fail. Prefer POST /sports/:sportId/:exEventId/tv/embed and handle 503.
isScorebooleanHint that a live score payload may exist (soccer / tennis / cricket). On the event list when odds cache exists; always on GET /odds; merged into summary on GET /state. Still call GET /score or POST /score/embed and handle 503 when unpublished.
betDelaynumber | nullBet placement delay in seconds (from upstream match odds).
preBet, crossMatching, totalMatchedboolean / number | nullAdditional upstream match-odds metadata.
runnersDataobject | nullMap of selection ID to runner name, e.g. {"235":"Team A"}.
marketType, popular, quickLinkstring | booleanUpstream market classification flags.
isCasinoGame, isVirtualbooleanUpstream event type flags.
_idstring | nullUpstream document id.
runners[].handicapnumber | nullRunner handicap from upstream.

Endpoints

All paths are relative to https://universeapi.store/api.

GET/sportsX-API-Key + Sports product

Sport catalog (IDs and names).

GET/sports/eventsX-API-Key + Sports product

All events across every supported sport (soccer, tennis, cricket, horse, greyhound). Pass ?inPlay=true to return only live matches (recommended for lobby UIs).

GET/sports/:sportId/eventsX-API-Key + Sports product

Event list for one sport with match-odds snapshot per event.

GET/sports/:sportId/:exEventId/oddsX-API-Key + Sports product

Live odds — match odds, fancy, bookmakers, sportsbook, lottery, and binary markets for one event. Top-level betDelay, preBet, crossMatching, and totalMatched mirror upstream match odds.

GET/sports/:sportId/:exEventId/stateX-API-Key + Sports product

Combined event summary + odds payload.

GET/sports/:sportId/:exEventId/scoreX-API-Key + Sports product + domain whitelist

Live scoreboard JSON for soccer (1), tennis (2), and cricket (4). Your API key must have at least one domain in allowedDomains (same whitelist used for embeds). Horse / greyhound return 404.

POST/sports/:sportId/:exEventId/tv/embedX-API-Key + X-TV-Client + domain whitelist

Mint a short-lived embed token for the live TV iframe. Returns streamingId (diamondtech gmid when primary, else LTVE channel id), source, iframePath (preferred), and optional iframeUrl. Returns 503 when no channel is assigned for the event.

POST/sports/:sportId/:exEventId/score/embedX-API-Key + X-TV-Client + domain whitelist

Mint a short-lived embed token for the scoreboard iframe. Returns iframePath and current score snapshot. Returns 503 when score is not published yet.

GET/tv/sports/playerembedToken + tv=true + Referer/Origin

Browser iframe HTML for live TV. Query: sportId, exEventId, embedToken, tv=true. Load only inside an iframe on an allowlisted partner page.

GET/tv/sports/scoreboardembedToken + tv=true + Referer/Origin

Browser iframe HTML for the live scoreboard (soccer / tennis / cricket). Same query params as the TV player. Soft-polls scores about every 5s in-page (no full reload).

WebSocket odds

Push live odds from your backend instead of polling HTTP for every tick. Prefer WebSocket for in-play viewing; keep GET /sports/:sportId/:exEventId/odds as a reconnect fallback.

URL (path is on the site origin, not under /api)Copy
wss://universeapi.store/ws/sports?sportId={sportId}&exEventId={exEventId}&apiKey=YOUR_KEY
  • Auth: apiKey query param (same key as X-API-Key) + Sports product + IP allowlist (same rules as REST).
  • On connect you receive { "type": "subscribed", "sportId", "exEventId", "client" }.
  • Updates are { "type": "odds", "sportId", "exEventId", "data": { … } } data has the same shape as GET /odds.
  • Rejected connections close with code 1008 (e.g. Unauthorized, IP not allowlisted, Product not enabled: sports).
Node exampleCopy
import WebSocket from "ws";

const sportId = "4";
const exEventId = "40020266291998437";
const apiKey = process.env.UNIVERSAL_API_KEY;
const ws = new WebSocket(
  `wss://universeapi.store/ws/sports?sportId=${sportId}&exEventId=${exEventId}&apiKey=${apiKey}`
);
ws.on("message", (raw) => {
  const msg = JSON.parse(String(raw));
  if (msg.type === "odds") {
    // msg.data.matchOdds / fancy / …
  }
});

TV & scoreboard embeds

Browser iframes use the same pattern as casino TV: your backend mints an embed token with your API key; the user's browser loads our iframe URL with that token. Domain access is enforced via your client's allowedDomains list.

LIVE TV source (Universal sports): diamondtech is primary for cricket, soccer, tennis, horse racing, and greyhound racing. We resolve the diamondtech event id (gmid) for the match and expose it as streamingId. Kingexch LTVE is secondary when no diamondtech channel is available. Always embed via iframePath on https://universeapi.store — do not open diamondtech URLs yourself.

What partners embed: only https://universeapi.store/api/tv/sports/player?... (from mint iframePath). That page is our HTML wrapper; when diamond is primary it nests diamondtech /play/sportstv/{etid}/{gmid} with the viewer IP. Scoreboard uses /api/tv/sports/scoreboard?... the same way.

Capability by sport

sportIdSportTV embedScore JSON / scoreboard iframe
1SoccerYes (per event)Yes
2TennisYes (per event)Yes
4CricketYes (per event)Yes
7Horse RacingYes (per event)No — 404
4339Greyhound RacingYes (per event)No — 404

TV and score availability is per event. Flags isStreaming / tv on the event list (and isScore on odds/state) are hints — they can lag behind upstream. Prefer trying the mint endpoints for in-play events and treat 503 as "not available yet". Resolve exEventId from GET /sports/:sportId/events before minting embeds.

Live first-party demo (provider site only): universeapi.store/sports.

Requirements

  • Sports product enabled on your API key
  • At least one domain on your allowlist (Admin → client Domains) — this is your panel origin (e.g. mahabet.club), required for score JSON, TV mint, and score mint. Do not put universeapi.store on the partner allowlist for B2B embeds
  • X-TV-Client: true on embed mint requests
  • embedDomain in the POST body when you have multiple whitelisted domains (must be on your allowlist). Subdomains of an allowlisted apex are accepted (e.g. app.partner.com if partner.com is listed; www. is normalized away).
  • Iframe URL must include tv=true
  • Iframe must be loaded from a page whose Referer/Origin matches your allowlist — opening the iframe URL in a new tab fails with 403
  • Use referrerpolicy="strict-origin-when-cross-origin" on your iframe. Do not set referrerpolicy="no-referrer" — diamondtech sportstv returns embed_origin_required without Origin/Referer
  • Do not mix tokens: a TV token only works on /api/tv/sports/player; a score token only on /api/tv/sports/scoreboard
  • Remint before expiresIn (typically ~3600s)
  • If your site uses CSP, allow frame-src / child-src for https://universeapi.store (required when using iframePath)

Resolve event ID (any sport)

List events for a sportCopy
curl -sS -H "X-API-Key: YOUR_KEY" \
  "https://universeapi.store/api/sports/1/events?inPlay=true"
# use events[].exEventId from the response

TV iframe flow (all sports: 1, 2, 4, 7, 4339)

1. Mint embed token (server-side)Copy
curl -sS -X POST \
  -H "X-API-Key: YOUR_KEY" \
  -H "X-TV-Client: true" \
  -H "Content-Type: application/json" \
  -d '{"embedDomain":"your-partner-site.com"}' \
  "https://universeapi.store/api/sports/{sportId}/{exEventId}/tv/embed"
Example mint response (diamond / AllPanel primary)Copy
{
  "sportId": "4",
  "exEventId": "4002026823127209",
  "productId": "4.4002026823127209",
  "embedToken": "YkpY2Roz...",
  "expiresIn": 3600,
  "streamingId": "807289833",
  "source": "allpanel-sportstv",
  "iframePath": "/api/tv/sports/player?sportId=4&exEventId=...&embedToken=...&tv=true"
}

streamingId is the diamondtech event id (gmid) when source is allpanel-crickettv / allpanel-sportstv. For LTVE secondary feeds it is the LTVE channel id. For diamond/AllPanel, mint omits iframeUrl — you must use iframePath so we remint playback with the viewer IP. LTVE-only responses may include an optional iframeUrl; still prefer iframePath in production.

2. Embed in your page (use iframePath on our host)Copy
<iframe
  src="https://universeapi.store/api/tv/sports/player?sportId={sportId}&exEventId={exEventId}&embedToken=TOKEN&tv=true"
  allow="autoplay; fullscreen; encrypted-media; picture-in-picture"
  referrerpolicy="strict-origin-when-cross-origin"
  allowfullscreen
  style="width:100%;aspect-ratio:16/9;border:0"
></iframe>

Prefer iframePath on https://universeapi.store — that path enforces your domain allowlist and token kind, then nests diamondtech (or LTVE) inside our player. Do not paste diamondtech /play/... URLs into your panel.

Channel vs broadcast: a successful mint means a stream channel was assigned (streamingId). The player may still show a waiting screen until the feed is publishing. That is expected and is not an API error. Mint returns 503 only when no channel is assigned for the event.

Per-sport TV examples

Soccer (1)Copy
POST https://universeapi.store/api/sports/1/{exEventId}/tv/embed
Tennis (2)Copy
POST https://universeapi.store/api/sports/2/{exEventId}/tv/embed
Cricket (4)Copy
POST https://universeapi.store/api/sports/4/{exEventId}/tv/embed
Horse (7)Copy
POST https://universeapi.store/api/sports/7/{exEventId}/tv/embed
Greyhound (4339)Copy
POST https://universeapi.store/api/sports/4339/{exEventId}/tv/embed

Score JSON shape (soccer, tennis, cricket)

GET /sports/:sportId/:exEventId/scoreCopy
{
  "client": "your-client-name",
  "sportId": "4",
  "exEventId": "4002026712143720420",
  "productId": "4.4002026712143720420",
  "score": {
    "sportId": "4",
    "exEventId": "4002026712143720420",
    "sportName": "Cricket",
    "commentary": "",
    "slider": [ /* upstream score panels (teamInfo, scoreItems, statusCommentry, …) */ ],
    "updatedAt": "2026-07-13T07:00:00.000Z"
  }
}

Parse score.slider for sport-specific panels. Cricket / soccer / tennis layouts differ; the scoreboard iframe renders a kingexch-style board for you if you do not want to parse slider yourself.

Scoreboard iframe flow (soccer, tennis, cricket only)

Mint scoreboard embedCopy
curl -sS -X POST \
  -H "X-API-Key: YOUR_KEY" \
  -H "X-TV-Client: true" \
  -H "Content-Type: application/json" \
  -d '{"embedDomain":"your-partner-site.com"}' \
  "https://universeapi.store/api/sports/{sportId}/{exEventId}/score/embed"
Example mint responseCopy
{
  "sportId": "4",
  "exEventId": "4002026712143720420",
  "productId": "4.4002026712143720420",
  "embedToken": "YkpY2Roz...",
  "expiresIn": 3600,
  "score": { "sportId": "4", "exEventId": "...", "slider": [], "updatedAt": "..." },
  "iframePath": "/api/tv/sports/scoreboard?sportId=4&exEventId=...&embedToken=...&tv=true"
}
Scoreboard iframeCopy
<iframe
  src="https://universeapi.store/api/tv/sports/scoreboard?sportId={sportId}&exEventId={exEventId}&embedToken=TOKEN&tv=true"
  referrerpolicy="strict-origin-when-cross-origin"
  style="width:100%;min-height:220px;border:0;background:#000"
></iframe>

Score mint response includes embedToken, expiresIn, iframePath, and a score snapshot. The scoreboard soft-polls about every 5s in-page (no full reload) so Referer stays valid — you do not need to remint the token just to refresh scores.

Per-sport scoreboard examples

Soccer (1)Copy
POST https://universeapi.store/api/sports/1/{exEventId}/score/embed
GET  https://universeapi.store/api/sports/1/{exEventId}/score
Tennis (2)Copy
POST https://universeapi.store/api/sports/2/{exEventId}/score/embed
GET  https://universeapi.store/api/sports/2/{exEventId}/score
Cricket (4)Copy
POST https://universeapi.store/api/sports/4/{exEventId}/score/embed
GET  https://universeapi.store/api/sports/4/{exEventId}/score

Horse (7) and greyhound (4339) do not have scoreboard endpoints — GET /score and POST /score/embed return 404.

For server-side polling (no iframe), use GET /sports/:sportId/:exEventId/score every 2–5s during in-play events (soccer, tennis, cricket only).

Live demo

On the provider site you can preview working TV and scoreboard iframes without a B2B API key: /sports (in-play list) and event detail /sports/:sportId/:exEventId.

That page uses first-party public routes (GET /public/sports/events, GET /public/tv/sports/player, GET /public/tv/sports/scoreboard) and is not for partner embedding. Partners must use the B2B mint + /api/tv/sports/* flow above.

The widget below issues real requests against this site’s backend proxy, which forwards to the provider with server-side credentials.

Sports API — live responses

idle · 0 ms

GET /api/public/uapi/sports

Polling guide

Poll the endpoints below from your backend — do not call the API from end-user browsers.

How often you should poll

Prefer WebSocket odds for live event screens. If you poll HTTP instead, apply these rates per endpoint and only for events your users are actively viewing. Do not poll the full event list at odds frequency.

EndpointWhenPoll interval
GET /sports/events or GET /sports/:sportId/eventsDiscovery / lobby30–60s
GET /sports/:sportId/:exEventId/oddsPre-match event on screen5–10s
GET /sports/:sportId/:exEventId/oddsIn-play event on screen500ms–1s
GET /sports/:sportId/:exEventId/stateSame as odds for that eventSame as odds row above
GET /sports/:sportId/:exEventId/scoreIn-play event with scoreboard2–5s

Best practices

  • Poll odds only for events on screen — not every event in the list.
  • Use updatedAt in the odds response; skip UI updates when it has not changed.
  • Polling faster than the recommended intervals only increases load on your API key.
  • Do not poll /sports/events every 1s or 500ms.

Errors

  • 401 — missing/invalid API key; missing X-TV-Client on mint; missing tv=true on iframe URL; invalid/expired/wrong-kind embed token
  • 403 — subscription inactive, sports product not enabled, caller IP not on Allowed IPs (when configured), domain not on allowlist (also blocks GET /score when allowlist is empty), missing browser Referer/Origin on iframe, or token bound to a different embed domain
  • 404 — unknown sportId, event excluded, or scoreboard not supported (horse/greyhound)
  • 400embedDomain not on this client's allowlist
  • 502 — upstream mint failure while creating an embed token
  • 503 — ingest unavailable; no TV channel assigned; or score not published yet. A waiting TV player (mint succeeded) is not a 503 — the CDN feed may still be offline.
  • Nested diamond player JSON embed_origin_required — your panel iframe is missing Origin/Referer (often referrerpolicy="no-referrer"). Use strict-origin-when-cross-origin and load the iframe from your allowlisted panel page

Examples

List cricket eventsCopy
curl -sS \
  -H "X-API-Key: YOUR_KEY" \
  "https://universeapi.store/api/sports/4/events?inPlay=true"
Horse racing eventsCopy
curl -sS \
  -H "X-API-Key: YOUR_KEY" \
  "https://universeapi.store/api/sports/7/events?inPlay=true"
Greyhound racing oddsCopy
curl -sS \
  -H "X-API-Key: YOUR_KEY" \
  "https://universeapi.store/api/sports/4339/43390020266291998437/odds"
Live odds for one eventCopy
curl -sS \
  -H "X-API-Key: YOUR_KEY" \
  "https://universeapi.store/api/sports/1/100202662715511777/odds"
Combined stateCopy
curl -sS \
  -H "X-API-Key: YOUR_KEY" \
  "https://universeapi.store/api/sports/2/200202662832959134/state"
Soccer score (JSON)Copy
curl -sS \
  -H "X-API-Key: YOUR_KEY" \
  "https://universeapi.store/api/sports/1/{exEventId}/score"
Tennis score (JSON)Copy
curl -sS \
  -H "X-API-Key: YOUR_KEY" \
  "https://universeapi.store/api/sports/2/{exEventId}/score"
Cricket score (JSON)Copy
curl -sS \
  -H "X-API-Key: YOUR_KEY" \
  "https://universeapi.store/api/sports/4/{exEventId}/score"
Mint cricket TV embedCopy
curl -sS -X POST \
  -H "X-API-Key: YOUR_KEY" \
  -H "X-TV-Client: true" \
  -H "Content-Type: application/json" \
  -d '{"embedDomain":"your-partner-site.com"}' \
  "https://universeapi.store/api/sports/4/{exEventId}/tv/embed"
Mint cricket scoreboard embedCopy
curl -sS -X POST \
  -H "X-API-Key: YOUR_KEY" \
  -H "X-TV-Client: true" \
  -H "Content-Type: application/json" \
  -d '{"embedDomain":"your-partner-site.com"}' \
  "https://universeapi.store/api/sports/4/{exEventId}/score/embed"

B2B integration

Keep the API key server-side, mint embed tokens from your backend, and serve only the iframePath URLs to browsers. Remint before expiresIn, respect the polling intervals above, and treat 503 responses as "not available yet" rather than hard failures.

Casino tables are documented separately — Casino API docs.