DocumentationMatches
Volleyball point-by-point API
GET/v1/matches/{id}/points
Every rally of every set in the order it was played: the score after it, the side that won it and the side that served it. Enough to work out side-out and break-point rates, runs, momentum and clutch points.
Parameters
Days you can read depend on your plan: the free key reads today and yesterday, Live the last 30 days, Archive and Complete every season since 2001.
| Name | Type | What it does | Example |
|---|---|---|---|
id | path | The match id, in the address. Required. | mt_jg5ktpky |
Request
Send your key in the Authorization header as a Bearer token, or in X-API-Key. A key in the address is refused, so it never ends up in a log.
# Every rally of every set: the score after it, who won it and who served curl "https://api.volleyballapi.com/v1/matches/mt_jg5ktpky/points" \ -H "Authorization: Bearer $VBA_KEY"
$ch = curl_init('https://api.volleyballapi.com/v1/matches/mt_jg5ktpky/points'); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('VBA_KEY')], CURLOPT_RETURNTRANSFER => true, ]); $res = json_decode(curl_exec($ch), true); print_r($res['data']);
import os, requests r = requests.get( "https://api.volleyballapi.com/v1/matches/mt_jg5ktpky/points", headers={"Authorization": f"Bearer {os.environ['VBA_KEY']}"}, ) print(r.json()["data"])
const res = await fetch("https://api.volleyballapi.com/v1/matches/mt_jg5ktpky/points", { headers: { "X-API-Key": process.env.VBA_KEY }, }); const { data, meta } = await res.json();
Response
Every answer is data plus meta. This is a real answer to the request above, counted 2026-10-12, with long lists cut short.
{
"data": {
"match": "mt_jg5ktpky",
"sets": [
{
"set": 1,
"rallies": [
{ "a": 0, "b": 1, "won_by": "b", "served_by": "b" },
{ "a": 1, "b": 1, "won_by": "a", "served_by": "b" },
{ "a": 1, "b": 2, "won_by": "b", "served_by": "a" },
{ "a": 2, "b": 2, "won_by": "a", "served_by": "b" },
...
]
},
{
"set": 2,
"rallies": [
{ "a": 1, "b": 0, "won_by": "a", "served_by": "a" },
{ "a": 1, "b": 1, "won_by": "b", "served_by": "a" },
{ "a": 1, "b": 2, "won_by": "b", "served_by": "b" },
{ "a": 2, "b": 2, "won_by": "a", "served_by": "b" },
...
]
},
{
"set": 3,
"rallies": [
{ "a": 0, "b": 1, "won_by": "b", "served_by": "b" },
{ "a": 0, "b": 2, "won_by": "b", "served_by": "b" },
{ "a": 1, "b": 2, "won_by": "a", "served_by": "b" },
{ "a": 2, "b": 2, "won_by": "a", "served_by": "a" },
...
]
},
{
"set": 4,
"rallies": [
{ "a": 1, "b": 0, "won_by": "a", "served_by": "a" },
{ "a": 1, "b": 1, "won_by": "b", "served_by": "a" },
{ "a": 2, "b": 1, "won_by": "a", "served_by": "b" },
{ "a": 3, "b": 1, "won_by": "a", "served_by": "a" },
...
]
},
...
]
},
"meta": { "plan": "Complete", "rallies": 213 }
}Fields
| Field | Type | What it holds |
|---|---|---|
match | string | The match id |
sets | array | One entry a set: set, and rallies in the order played |
rallies[].a, rallies[].b | int | The score after the rally |
rallies[].won_by | enum | a or b, the side that won it |
rallies[].served_by | enum | a or b, the side that served it |
meta.rallies | int | Rallies in the match |
Errors
Errors come back as {"error": {"code", "message", "docs"}}, with plan and pricing added when a plan would answer. Every error.
| HTTP | Code | When |
|---|---|---|
| 401 | unauthorized | No key, or a key that is not known or was revoked |
| 403 | upgrade_required | Your plan does not include this endpoint; the answer names the plans that do |
| 404 | not_found | An id that does not exist |
| 422 | unknown_parameter | A parameter this endpoint does not take; the answer lists the ones it does |
| 429 | rate_limited | Too many calls this minute; Retry-After says when to try again |
| 429 | quota_exceeded | The day's or month's allowance is used; the answer says when it resets |
Try it
Fill in what you need; the request is built as you type. Paste your key to run it here, straight against the API. This endpoint answers on the Live, Archive and Complete plans.