has_stream and has_stats: check data before you ask

Availability differs per competition, so a match page can open onto a blank stats tab. The flags on every match and league tell you what exists before you request it.

Updated 2026-10-23 6 min read

The most common bug in a sports front end is not a crash. It is a tab that opens onto nothing — a Stats tab with no statistics, a Watch tab with no player, a Table tab for a competition that has no table. The user clicks, waits, and sees an empty panel.

This happens because coverage differs per competition. The Premier League carries line-ups, player ratings, shot data and a league table. A regional cup tie may carry a score and nothing else. Your code cannot assume, and finding out by requesting the data costs you a request and the user a wait.

SportSRC answers this with flags that travel inside responses you have already made. Reading them is free.

The four flags

FlagLives onTells you
has_streammatchA playable stream source exists
has_statsmatchStatistics, line-ups and incidents exist
has_standingsleagueThe competition has a table
has_oddsleagueBetting odds are offered

Two are per match because they vary match by match: a stream appears for one fixture and not its neighbour. Two are per league because a knockout cup structurally has no table, whatever the fixture.

Reading them

Both matches and scores group fixtures under their league, so you get the league flags and the match flags in one pass:

GET https://api.sportsrc.org/v3/?type=scores&date=2026-10-23
{
  "data": [
    {
      "league": {
        "name": "Premier League",
        "has_standings": true,
        "has_odds": true
      },
      "matches": [
        {
          "id": "arsenal-chelsea-ZjoxNTU2MTAx...",
          "status": "inprogress",
          "has_stream": true,
          "has_stats": true
        },
        {
          "id": "burnley-brentford-ZjoxNTU2MTAy...",
          "status": "notstarted",
          "has_stream": false,
          "has_stats": true
        }
      ]
    }
  ]
}

Build the tab list from the flags

Decide which tabs exist before you render, not after a request comes back empty:

function tabsFor(match, league) {
  const tabs = ['summary'];

  if (match.has_stream) tabs.push('watch');
  if (match.has_stats)  tabs.push('lineups', 'stats', 'incidents');
  if (league.has_standings) tabs.push('table');
  if (league.has_odds)      tabs.push('odds');

  return tabs;
}

A user who never sees a Stats tab does not experience a missing feature. A user who clicks Stats and finds nothing experiences a broken product. The difference is one boolean.

has_stream is a reading, not a property

This is the part that catches people. Stream sources are published by third parties and they appear close to kick-off — often inside the final fifteen minutes, sometimes after the match has already started. A fixture that reports has_stream: false six hours out may well report true at kick-off.

They can also disappear mid-match when a source drops. So:

  • Do not cache has_stream for the day and build a static list from it.
  • Do re-read it from the matches or scores payload you are already polling for scores.
  • Do let a match gain a Watch tab while the page is open. A viewer who arrived early is exactly the viewer most likely to still be there at kick-off.

A practical pattern: poll scores on your normal score interval and diff has_stream between polls. When it flips to true, add the tab. It costs no extra request, because you were polling for the score anyway.

Why the per-match endpoints are gated too

The flags are not advisory. SportSRC only serves per-match data for fixtures that have a stream — that is the service's core rule, inherited from v2. Requesting detail, lineups, stats or incidents for a fixture with no stream returns a not-found error rather than an empty object.

So the flag is not just a hint for your UI. It tells you which requests will succeed at all. Checking it first turns a wasted round trip and an error path into a branch you never take.

A note on status

Pair the flags with status, which is a fixed vocabulary rather than free text. Switch on these exact strings:

ValueMeaning
notstartedNot begun. The timer is an empty string.
inprogressBeing played. is_live is true.
finishedOver. The score is final.
canceledCalled off, postponed or abandoned.

A Watch tab makes sense for notstarted near kick-off and for inprogress. It rarely makes sense for finished, even when a source lingers.

Summary

  • Coverage varies per competition; assume nothing.
  • Four flags tell you what exists, and they ride along in responses you already make.
  • Build your tab list from the flags, before rendering.
  • Re-read has_stream as you poll — it changes around kick-off.
  • Per-match endpoints are gated on it, so the flag also predicts which requests will succeed.

Frequently asked

What does has_stream mean?

has_stream is a boolean on every match telling you whether a playable stream source exists for it. When it is false, the per-match stream endpoint returns nothing, so do not render a watch tab.

Why would stats be missing for a match that is live?

Coverage is set per competition, not per match. A top-tier league carries line-ups, statistics and incidents; a regional cup may carry only the score. has_stats tells you which case you are in before you spend a request.

Does checking a flag cost a request?

No. The flags travel inside the matches and scores responses you already requested, so reading them is free.

Can has_stream change during a match?

Yes. Sources appear close to kick-off and can drop mid-match, so treat it as a current reading rather than a fixed property. Re-read it from the matches or scores payload rather than caching it for the day.