How to Get Free Sports Data with an API

Learn how to pull live scores, video highlights, player stats, and more using a free sports data API, with working JavaScript and Python examples included.

Free sports data API tutorial

A lot of people have great ideas for how a sports app, betting platform, or score tracker should work. But one of the most common hurdles they face is finding a free data source.

On the market, there are several options and one of them is our Highlightly Sports API which gives you access to live data across 9 sports and 950+ leagues. It includes a free tier with 100 API requests per day to help you kickstart your project, with no trial expiration and no credit card required.

In this guide, we'll walk you through the API step by step. By the end, you'll have working code examples that pull match data, video highlights, player stats, and more using JavaScript and Python.

Data availability for free tier

The free tier gives you access to most of the API's core endpoints. However, there are a few restrictions. Odds and geo-restriction checks for highlights require a paid plan. Additionally, highlight coverage for some sports is limited to a subset of available leagues. For building and prototyping, the free tier is a good place to start.

Live match scores update once a minute across football, NBA, NFL, NHL, MLB, cricket, basketball, hockey, rugby, volleyball, and handball. Each match includes the current score, match state, team info, league and country details, and competition round.

Video highlights are one of the core functionalities of the API. Game recaps, post-match interviews, big plays, and real-time clips from sources like YouTube, Twitter, ESPN, and others are available. Some clips even show up while the game is still going.

Player profiles and statistics are available for football, NFL, NBA, NHL, MLB and cricket. You can search players by name and pull entire career summaries along with per season stats.

Team statistics break down a team's record by league and season, including games played, wins, losses, draws, goals scored and conceded, plus home and away splits.

Standings give you full league tables for supported league and season.

Head-to-head records show the historical matchups between any two teams.

Last five games returns the team's most recent results, which is handy for quick form checks.

Lineups include starting players, formations, substitutions and positions for supported matches. Structure varies between sports.

Match events are returned through the detailed match endpoint and vary by sport. For football these include goals, own goals, penalties, missed penalties, yellow cards, red cards, substitutions, and VAR decisions. For NFL you get play-by-play events like touchdowns, field goals, turnovers, penalties, and scoring drives. Other sports return their own relevant event types through the same match detail structure.

Match statistics are broken down by team and cover a wide range of in-game metrics. For football, this includes possession, shots on target, expected goals/assists, corners, fouls, passes completed, interceptions, clearances, and more. The specific stats returned depend on the sport and league.

Predictions give you prematch and live win probabilities using a three-way model (home, draw, away) or a two-way model (home, away).

Bookmakers and odds are available through the API but require a paid plan. The same applies to the highlight geo-restriction checker. Everything else listed above works on the free tier.

Setting up your account

Go to highlightly.net and sign up. You can also create an account through RapidAPI if you prefer that platform. Both give you the same data and the same free tier.

Once you're in, grab your API key from the dashboard. It authenticates every request, so keep it private and don't commit it to a public repo.

Every request needs the x-rapidapi-key header set to your API key.

If you're using the RapidAPI base URL, you'll also need x-rapidapi-host set to sport-highlights-api.p.rapidapi.com. If you call the Highlightly base URL directly at https://sports.highlightly.net, you only need the API key header.

We'll use the Highlightly base URL for the rest of this guide since it's easier.

Retrieving today's matches

Let's keep it simple and grab all football matches for today.

JavaScript

          const API_KEY = "your-api-key-here";
const BASE_URL = "https://sports.highlightly.net";

async function getTodaysMatches() {
  const today = new Date().toISOString().split("T")[0];

  const response = await fetch(
    `${BASE_URL}/football/matches?date=${today}&limit=100`,
    {
      headers: {
        "x-rapidapi-key": API_KEY,
      },
    }
  );

  const { data, pagination } = await response.json();

  data.forEach((match) => {
    const home = match.homeTeam.name;
    const away = match.awayTeam.name;
    const score = match.state.score.current;
    const status = match.state.description;

    console.log(`${home} vs ${away} | ${score} | ${status}`);
  });

  console.log(`Showing ${data.length} of ${pagination.totalCount} matches`);
}

getTodaysMatches();
        

Python

          import requests
from datetime import date

API_KEY = "your-api-key-here"
BASE_URL = "https://sports.highlightly.net"

def get_todays_matches():
    today = date.today().isoformat()

    response = requests.get(
        f"{BASE_URL}/football/matches",
        headers={"x-rapidapi-key": API_KEY},
        params={"date": today, "limit": 100},
    )

    result = response.json()

    for match in result["data"]:
        home = match["homeTeam"]["name"]
        away = match["awayTeam"]["name"]
        score = match["state"]["score"]["current"]
        status = match["state"]["description"]

        print(f"{home} vs {away} | {score} | {status}")

    print(f"Showing {len(result['data'])} of {result['pagination']['totalCount']} matches")

get_todays_matches()
        

Each match comes with home and away team objects (id, name, and logo), the league, country, round, date, and current state.

You can narrow results with query parameters like leagueName, countryCode, homeTeamName, or season. For example, adding &leagueName=Premier League gives you only Premier League fixtures.

One thing to know is that at least one primary query parameter is required. The date field counts as primary, so the example above works fine. However, limit, offset, and timezone are secondary and can't be used alone.

Getting detailed match info

The /football/matches endpoint returns the basics. For the full picture on a specific game, use the match detail endpoint with the match ID.

          async function getMatchDetails(matchId) {
  const response = await fetch(`${BASE_URL}/football/matches/${matchId}`, {
    headers: {
      "x-rapidapi-key": API_KEY,
    },
  });

  const [match] = await response.json();

  console.log(`${match.homeTeam.name} vs ${match.awayTeam.name}`);
  console.log(`Score: ${match.state.score.current}`);
  console.log(`Venue: ${match.venue?.name}, ${match.venue?.city}`);
  console.log(`Referee: ${match.referee?.name}`);
  console.log(`Weather: ${match.forecast?.temperature}, ${match.forecast?.status}`);

  if (match.events?.length) {
    console.log("\nMatch events:");
    match.events.forEach((event) => {
      console.log(`  ${event.time}' - ${event.type} - ${event.player} (${event.team.name})`);
    });
  }

  if (match.predictions?.prematch?.length) {
    const prediction = match.predictions.prematch[0];
    console.log("\nPredictions:");
    console.log(`  Home: ${prediction.probabilities.home}`);
    console.log(`  Draw: ${prediction.probabilities.draw}`);
    console.log(`  Away: ${prediction.probabilities.away}`);
  }
}

getMatchDetails(489389);
        

This is where it gets interesting. For popular leagues you'll see venue info like stadium name, city, and capacity, along with weather conditions and the referee's name.

The events array is a timeline of the match. Each event has a type (Goal, Yellow Card, Red Card, Substitution, VAR Goal Cancelled, etc.), the time it happened, the player involved, and the team. For goals, there's also an assist field.

Match statistics come as an array broken down by team, with values like shot accuracy.

The predictions object has both prematch and live arrays. Each one includes a modelType, a readable description, and probabilities with home, draw, and away percentages.

Related news articles show up in the news array with the article URL, image, title, and publish date.

Fetching video highlights

This is one of the best parts of the API. Most sports data providers stick to scores and stats, while Highlightly includes video highlights as well.

          async function getHighlights(dateString) {
  const response = await fetch(
    `${BASE_URL}/football/highlights?date=${dateString}&limit=5`,
    {
      headers: {
        "x-rapidapi-key": API_KEY,
      },
    }
  );

  const { data } = await response.json();

  data.forEach((highlight) => {
    console.log(`[${highlight.type}] ${highlight.title}`);
    console.log(`  Source: ${highlight.source}`);
    console.log(`  URL: ${highlight.url}`);

    if (highlight.embedUrl) {
      console.log(`  Embed: ${highlight.embedUrl}`);
    }

    const matchScore = highlight.match.state.score.current;
    const home = highlight.match.homeTeam.name;
    const away = highlight.match.awayTeam.name;
    console.log(`  Match: ${home} vs ${away} (${matchScore})\n`);
  });
}

getHighlights("2026-04-25");
        

Highlights come in two parts. VERIFIED ones are from reputable and inspected sources, usually official channels that own the rights. These typically appear 0 to 48 hours after a match. UNVERIFIED ones are faster and might show up while the game is still on. These include user uploaded clips, last-minute goals, and big plays from various platforms.

Each highlight has a title, an optional description, a source (youtube, twitter, reddit, espn, etc.), and sometimes a channel name. The url links to the original video. If the clip supports embedding, you'll also get an embedUrl you can drop into an iframe.

You can filter by leagueName, countryCode, homeTeamName, awayTeamName, matchId, or season. The limit is 40 items per request, so use offset query parameter to paginate if there are more.

Note that on the free tier, highlight availability for some sports may be restricted to larger leagues. Upgrading removes these restrictions.

Looking up player stats

Player data works for football, NFL, NBA, NHL, MLB, and cricket. The flow is straightforward. You search for a player, get their ID, then pull their summary or season stats.

          import requests

API_KEY = "your-api-key-here"
BASE_URL = "https://sports.highlightly.net"

HEADERS = {"x-rapidapi-key": API_KEY}


def search_player(name):
    response = requests.get(
        f"{BASE_URL}/football/players",
        headers=HEADERS,
        params={"name": name, "limit": 5},
    )

    players = response.json()["data"]

    for player in players:
        print(f"ID: {player['id']} | {player['name']}")

    return players[0]["id"] if players else None


def get_player_summary(player_id):
    response = requests.get(
        f"{BASE_URL}/football/players/summary/{player_id}",
        headers=HEADERS,
    )

    summary = response.json()
    print(f"\nPlayer Summary:")
    print(summary)


def get_player_statistics(player_id):
    response = requests.get(
        f"{BASE_URL}/football/players/statistics/{player_id}",
        headers=HEADERS,
    )

    stats = response.json()
    print(f"\nPlayer Statistics:")
    print(stats)


player_id = search_player("Haaland")

if player_id:
    get_player_summary(player_id)
    get_player_statistics(player_id)
        

The /football/players endpoint takes a name and returns paginated results. Each player has an id and name you use in follow-up calls.

The summary endpoint gives you the player's profile and career overview, while the statistics endpoint returns per-season numbers.

For American sports, the same pattern applies with a different path: /nba/players searches NBA and NCAAB players, and /baseball/players covers MLB and NCAA baseball.

Pulling team statistics

You can get a team's season-by-season breakdown including wins, losses, draws, and goals by passing the team's ID and a fromDate.

          async function getTeamStats(teamId, fromDate) {
  const url = new URL(`${BASE_URL}/football/teams/statistics/${teamId}`);
  url.searchParams.set("fromDate", fromDate);

  const response = await fetch(url, {
    headers: {
      "x-rapidapi-key": API_KEY,
    },
  });

  const stats = await response.json();

  stats.forEach((entry) => {
    console.log(`\n${entry.leagueName} - ${entry.season}`);
    console.log(`  Played: ${entry.total.games.played}`);
    console.log(`  W/L/D: ${entry.total.games.wins}/${entry.total.games.loses}/${entry.total.games.draws}`);
    console.log(`  Goals: ${entry.total.goals.scored} scored, ${entry.total.goals.received} received`);
    console.log(`  Home W/L/D: ${entry.home.games.wins}/${entry.home.games.loses}/${entry.home.games.draws}`);
    console.log(`  Away W/L/D: ${entry.away.games.wins}/${entry.away.games.loses}/${entry.away.games.draws}`);
  });
}

getTeamStats(553, "2024-08-01");
        

The response is an array where each entry covers a league and season since the fromDate. Stats are split into total, home, and away sections, each containing games (played, wins, losses, draws) and goals (scored, received).

You can also pass a timezone parameter if you want date filtering adjusted for your location. The default is Etc/UTC.

Fetching standings

Standings are straightforward and only need the league ID and the season.

          import requests

API_KEY = "your-api-key-here"
BASE_URL = "https://sports.highlightly.net"


def get_standings(league_id, season):
    response = requests.get(
        f"{BASE_URL}/football/standings",
        headers={"x-rapidapi-key": API_KEY},
        params={"leagueId": league_id, "season": season},
    )

    standings = response.json()

    for entry in standings:
        print(entry)

get_standings(104, 2025)
        

If you don't know the league ID, you can look it up with the /football/leagues endpoint. Search with ?leagueName=Premier League and the response will include the league's id and available seasons.

Head-to-head and recent form

Two endpoints that are great for pre-match analysis.

The head-to-head endpoint returns the last ten historical matches between two teams by passing in homeTeamId and awayTeamId as query parameters.

          async function getHeadToHead(homeTeamId, awayTeamId) {
  const response = await fetch(
    `${BASE_URL}/football/h2h?homeTeamId=${homeTeamId}&awayTeamId=${awayTeamId}`,
    {
      headers: {
        "x-rapidapi-key": API_KEY,
      },
    }
  );

  const data = await response.json();
  console.log("Head to head results:", data);
}

getHeadToHead(5700782, 1907875);
        

The last five games endpoint takes a team ID and returns their five most recent finished matches, which is great for building form indicators.

          async function getLastFive(teamId) {
  const response = await fetch(
    `${BASE_URL}/football/last-five/${teamId}`,
    {
      headers: {
        "x-rapidapi-key": API_KEY,
      },
    }
  );

  const data = await response.json();
  console.log("Last 5 games:", data);
}

getLastFive(553);
        

Lineups, live events, and match statistics

Three more endpoints round out the match-day picture. All of them take a matchId as a path parameter.

Lineups give you starting players, positions, and formations before kickoff.

          const lineups = await fetch(`${BASE_URL}/football/lineups/${matchId}`, {
  headers: { "x-rapidapi-key": API_KEY },
}).then((res) => res.json());
        

Live events return real-time in-match events as they happen, and offer a more focused feed than the events from the match detail endpoint.

          const liveEvents = await fetch(`${BASE_URL}/football/live-events/${matchId}`, {
  headers: { "x-rapidapi-key": API_KEY },
}).then((res) => res.json());
        

Match statistics return post-match stats like shot accuracy, broken down by team.

          const stats = await fetch(`${BASE_URL}/football/statistics/${matchId}`, {
  headers: { "x-rapidapi-key": API_KEY },
}).then((res) => res.json());
        

Match box scores

For sports that support it (football, NFL, NBA, NHL, MLB, cricket), the box score endpoint gives you player-level stats for a specific match.

          async function getBoxScore(matchId) {
  const response = await fetch(
    `${BASE_URL}/football/match-box-score/${matchId}`,
    {
      headers: {
        "x-rapidapi-key": API_KEY,
      },
    }
  );

  const boxScore = await response.json();
  console.log("Box score:", boxScore);
}

getBoxScore(489389);
        

For the NBA, this is where you find per-player points, rebounds, assists, steals, blocks, and shooting splits. For MLB, you get batting and pitching lines. Just swap the path for the sport, for example /nba/match-box-score/{matchId} or /baseball/match-box-score/{matchId}.

What's next

Everything above used football as the example, but the same pattern works across all supported sports by changing the path prefix.

To get today's NBA matches you'd call /nba/matches?date=2026-04-25, and for cricket highlights you'd call /cricket/highlights?date=2026-04-25. The response structure is the same across all sports.

Every sport supports matches, highlights, teams, bookmakers, odds, standings, last five games, and head-to-head. Football, cricket, NBA, NFL, NHL, and MLB also have lineups, player stats, player summaries, and box scores. Keep in mind that odds require a paid plan regardless of sport, and highlight coverage on the free tier may vary by sport and league.

You've now seen how to pull matches, highlights, player stats, team stats, standings, head-to-head data, last five games, lineups, match events, statistics, predictions, and box scores.

Once your project grows, and you start hitting the 100 requests per day limit, upgrading unlocks higher limits along with access to odds data and the highlight geo-restriction checker. You can view the available plans on the Highlightly pricing page or through RapidAPI.

For the full endpoint reference, head to the API documentation.