Volleyball scoring works differently from most sports โ matches are won by sets, and each set has its own point tally. Building a proper live match view means showing both the overall set count and the point-by-point score of the set in progress, plus per-player stats once they're available. This guide covers exactly that using /live_match_details.
Two Endpoints, Two Jobs
It's worth being clear about the split between the two main live endpoints:
/matchesโ the quick live board: current set count, andcurrent_set_pointsfor the set in progress/live_match_detailsโ the full picture: complete set-by-set score history, venue, broadcast info, bracket position, and per-player statistics
Use /matches for a scrolling live scores list, and /live_match_details once a user taps into a specific match.
Fetching the Full Set-by-Set Score
The score object breaks down every set played so far, not just the current tally:
import requests
response = requests.get(
'https://live-volleyball-api.com/api/v1/live_match_details',
params={'api_key': 'YOUR_KEY', 'match_id': match_id, 'lang': 'en'}
).json()
data = response['data']
score = data['score']
print(f"Sets: {score['home']} - {score['away']}")
for i, set_score in enumerate(score['sets'], 1):
print(f" Set {i}: {set_score['home']}-{set_score['away']}")
The same call in JavaScript:
const res = await fetch(
'https://live-volleyball-api.com/api/v1/live_match_details' +
`?api_key=YOUR_KEY&match_id=${matchId}&lang=en`
);
const result = await res.json();
const score = result.data.score;
score.sets.forEach((set, i) => {
console.log(`Set ${i + 1}: ${set.home}-${set.away}`);
});
Identifying the Live In-Progress Set
While a match is live, the last entry in the sets array is the set currently being played โ its point values update as the match progresses, so it's the one worth highlighting differently in your UI:
function renderSets(sets, isLive) {
return sets.map((set, i) => {
const isCurrent = isLive && i === sets.length - 1;
return (
<span key={i} className={isCurrent ? 'set-live' : 'set-final'}>
{set.home}-{set.away}
</span>
);
});
}
Handling a Match That Hasn't Started
score is null before kickoff โ always check before accessing its fields:
if (data.score === null) {
showKickoffCountdown(data.date_time);
} else {
renderScoreboard(data.score);
}
Fetching Per-Player Match Statistics
The statistics object returns a dynamic set of columns (points, attack%, block, serve%, ace, reception%) per player, plus a team total row. Since the exact columns vary by competition, read them from statistics.columns rather than hardcoding field names:
const stats = data.statistics;
if (stats) {
console.log('Columns:', stats.columns);
stats.home.forEach(player => {
const row = stats.columns.map(col => `${col}: ${player.stats[col]}`).join(', ');
console.log(row);
});
console.log('Team total:', stats.home_total);
}
Building a Player Stats Table
function PlayerStatsTable({ statistics, side }) {
if (!statistics || statistics[side].length === 0) {
return <p>Stats not available for this match</p>;
}
return (
<table>
<thead>
<tr>
{statistics.columns.map(col => <th key={col}>{col}</th>)}
</tr>
</thead>
<tbody>
{statistics[side].map(player => (
<tr key={player.player_id}>
{statistics.columns.map(col => (
<td key={col}>{player.stats[col] || 'โ'}</td>
))}
</tr>
))}
</tbody>
</table>
);
}
Why Statistics Can Be Missing for One Side
Statistics availability isn't always symmetric โ one team's row can be populated while the other's away array is empty and away_total is null, depending on what the upstream feed provides for that specific competition. Always render each side independently rather than assuming both are present together:
function hasStats(statsSide) {
return Array.isArray(statsSide) && statsSide.length > 0;
}
Combining Score and Stats in One View
function MatchLiveView({ data }) {
return (
<div>
{data.score ? (
<SetScoreboard score={data.score} isLive={data.status?.is_live} />
) : (
<KickoffCountdown dateTime={data.date_time} />
)}
{data.statistics && (
<>
<PlayerStatsTable statistics={data.statistics} side="home" />
<PlayerStatsTable statistics={data.statistics} side="away" />
</>
)}
</div>
);
}
Frequently Asked Questions
Is the score field the same as current_set_points from /matches?
No, current_set_points in /matches is just the live point score of the in-progress set โ score.sets in /live_match_details gives the full history of every completed and in-progress set together.
Are player statistics available for every league?
No, statistics is only available for some leagues and matches โ always check for null before rendering a stats table, and show a fallback message otherwise.
Do I need to poll live_match_details for score updates, or is matches enough?
For a match list view, /matches alone is enough. Poll /live_match_details only for the specific match a user has open, since it's a heavier response with more fields to update.
What does an empty reception (Rec) value mean for a player?
An empty string for a stat like Rec or Rec% typically means that player didn't register any actions in that category during the match โ treat it as "no data" rather than zero.