Matches
List, get, search, and subscribe to live matches across every major sport.
The matches resource is the workhorse of the SDK — every other resource
hangs off it.
matches.list
Paginated list of matches matching the filters you pass.
const { data, pagination } = await mrdoge.matches.list({
sports: ["soccer"],
status: ["live"],
limit: 20,
});Params:
| Field | Type | Notes |
|---|---|---|
sports | string[] | "soccer", "basketball", "tennis", "volleyball", "baseball", "hockey", "football" (pass one or more) |
competitionIds | number[] | Filter to one or more competitions |
regionIds | number[] | Filter to one or more regions |
teamIds | number[] | Filter to matches involving any of the listed teams |
status | MatchStatus[] | ["upcoming"], ["live"], ["completed"], or combinations |
date | string | YYYY-MM-DD — matches on this day |
startDate / endDate | string | Range filter |
cursor | string | Pagination cursor (opaque) |
limit | number | Max 100 |
select | MatchSelect | Field selector — see selectors |
locale | string | "en", "pt-BR", "es" |
timezone | string | IANA timezone (e.g. "America/Sao_Paulo") |
Returns: { data: Match[]; pagination: { nextCursor: string | null; hasMore: boolean } }
See pagination for the cursor walk.
matches.listAll
Auto-paginated version of matches.list. Walks all pages and returns the
combined array.
const all = await mrdoge.matches.listAll(
{ sports: ["soccer"], status: ["live"] },
{
onPage: (page, accumulated) => render(accumulated),
signal: controller.signal,
},
);Params: Same as matches.list minus cursor.
Options:
| Field | Type | Notes |
|---|---|---|
onPage | (page, accumulated) => void | Called per page for progressive rendering |
signal | AbortSignal | Cancel the walk mid-page |
Returns: Match[] — every match across all pages.
matches.get
Single match by ID with full detail (MatchDetail).
const match = await mrdoge.matches.get({ id: "match_abc123" });Params:
| Field | Type | Notes |
|---|---|---|
id | string | Match ID — strings, not numbers |
select | MatchDetailSelect | Field selector |
locale | string | Localization |
Returns: MatchDetail — includes stats, markets, and current clock.
matches.trending
Top matches by sport, server-prioritised.
const trending = await mrdoge.matches.trending({
sports: ["soccer"],
limit: 5,
});Params:
| Field | Type | Notes |
|---|---|---|
sports | string[] | Optional — defaults to all sports |
status | MatchStatus[] | Optional |
limit | number | Max 50, default 5 |
select | MatchSelect | Field selector |
Returns: Match[]
matches.search
Free-text search across team names and competitions.
const results = await mrdoge.matches.search({
query: "liverpool",
limit: 10,
});Params:
| Field | Type | Notes |
|---|---|---|
query | string | Min 2 chars |
sports | string[] | Optional — narrow by one or more sports |
status | MatchStatus[] | Optional |
limit | number | Max 20 |
select | MatchSelect | Field selector |
Returns: Match[]
matches.subscribeLive
Live-match subscription with WebSocket deltas. The killer feature.
const sub = await mrdoge.matches.subscribeLive({
sports: ["soccer"],
});
// Initial state — already populated from the HTTP cold-start cache
console.log("Snapshot:", sub.snapshot.length, "matches");
// Stream deltas
sub.on("match.upd", (match) => render(match));
sub.on("match.del", (matchId) => removeFromUI(matchId));
// On any subscription closure (rate limit, auth expiry, server bounce, etc.)
sub.on("closed", ({ reason, message }) => {
console.warn("Subscription closed:", reason, message);
});
// Tear down
await sub.cancel();Params:
| Field | Type | Notes |
|---|---|---|
sports | string[] | Optional |
regionIds | number[] | Optional |
competitionIds | number[] | Optional |
select | MatchSelect | Field selector — applies to snapshot AND every delta |
Returns: Subscription<"matches.subscribeLive"> — see
subscriptions.
Push events: match.upd (full match payload), match.del (matchId string)
The SDK races HTTP cache against WebSocket connect for the initial snapshot
— sub.snapshot is populated in ~100ms instead of ~1.5s.
Read the cold-start details →
matches.subscribe
Deep subscription for a single match — every odds change, stat update, and status transition.
const sub = await mrdoge.matches.subscribe({
matchId: "match_abc123",
});
console.log("Match:", sub.snapshot);
sub.on("stats.upd", (stats) => updateScoreboard(stats));
sub.on("odds.upd", (markets) => repriceLines(markets));
sub.on("status.upd", ({ status }) => onPhaseChange(status));Params:
| Field | Type | Notes |
|---|---|---|
matchId | string | Required |
select | MatchDetailSelect | Field selector — applies to snapshot, stats.upd, and odds.upd |
Returns: Subscription<"matches.subscribe">
Push events: stats.upd (MatchStats), odds.upd (Market[]), status.upd ({ status })
matches.getLive
One-shot snapshot of all live matches matching the filters. No subscription, no deltas — useful for cron jobs or edge runtimes.
const snapshot = await mrdoge.matches.getLive({ sports: ["soccer"] });Params:
| Field | Type | Notes |
|---|---|---|
sports | string[] | Optional |
regionIds | number[] | Optional |
competitionIds | number[] | Optional |
select | MatchSelect | Field selector |
Returns: Match[]
The Match shape
type Match = {
id: string; // string, not number
startTime: string; // ISO 8601
status: "upcoming" | "live" | "completed";
homeTeam: { id: number; name: string };
awayTeam: { id: number; name: string };
sport: { id: number; name: string } | null;
competition: { id: number; name: string };
region: { id: number; name: string };
markets: Market[]; // match-result + under/over odds
stats?: MatchStats | null; // only on completed matches in list responses
};Use match.stats?.homeScore and match.stats?.awayScore for the score
(when available). For live matches in subscribeLive snapshots, stats are
included too.
Selectors
GraphQL-style projection — request only the fields you need. Reduces payload size, deserialization time, and re-render cost.
const { data } = await mrdoge.matches.list({
sports: ["soccer"],
select: {
id: true,
homeTeam: { name: true },
awayTeam: { name: true },
stats: { homeScore: true, awayScore: true, clock: { display: true } },
// omit markets, competition, region, etc.
},
});Selectors apply to every match-bearing method:
matches.list,matches.get,matches.trending,matches.search,matches.getLive— applies to the responsematches.subscribeLive— applies to the initial snapshot AND everymatch.updpush for the lifetime of the subscriptionmatches.subscribe— same forstats.updandodds.upddeltas
The select type (MatchSelect or MatchDetailSelect) walks the
response shape recursively, so autocomplete drives every key. Selector
rules:
field: true→ include the full subtreefield: { …nested }→ include only the listed nested fields- field omitted → not returned
Performance: projection is post-cache
The server caches the full response shape once, then projects per
request. Two calls with different select shapes share the same upstream
fetch and cache entry — you don't pay a cache miss for picking different
fields.
This means feature-flag-driven selectors (e.g. one component renders
homeScore, another renders homeScore + clock.display) are free — no
extra round-trips.
Subscriptions: bandwidth savings compound
For long-lived subscribeLive subscriptions, the selector applies to
every push. A subscription rendering 50 live matches with a 10-field
selector instead of the full ~60-field default cuts your bandwidth and
JSON-parse cost ~6×. The server stores the selector per-subscription and
projects each delta before sending.