Ball-by-Ball API ★ USED
Source: https://www.cricketapi.com/v5/docs/match-ball-by-ball-rest-api Refresh: Every 5 seconds
Overview
Ball-by-ball updates for each over of a cricket match. Includes ball type, batsman, bowler, fielder information, wicket details, team score, and other information for each ball.
Endpoints
All ball-by-ball data
GET /v5/cricket/{project_key}/match/{match_key}/ball-by-ball/
First over only
GET /v5/cricket/{project_key}/match/{match_key}/ball-by-ball/FIRST-OVER/
Specific over (by over key)
GET /v5/cricket/{project_key}/match/{match_key}/ball-by-ball/{over_key}/
Over key format: {innings}_{over_number} — e.g., b_1_11 = team B, innings 1, over 11
Headers
rs-token: <access_token>
Sample Request (Node.js)
// All ball-by-ball
const response = await axios.get(
`https://api.sports.roanuz.com/v5/cricket/${projectKey}/match/${matchKey}/ball-by-ball/`,
{ headers: { 'rs-token': token } }
);
// Specific over
const overKey = 'b_1_11'; // Team B, innings 1, over 11
const response = await axios.get(
`https://api.sports.roanuz.com/v5/cricket/${projectKey}/match/${matchKey}/ball-by-ball/${overKey}/`,
{ headers: { 'rs-token': token } }
);
Ball Object Structure
{
"key": "529536",
"overs": [14, 6],
"innings": "a_1",
"batsman": {
"player_key": "c__player__virat_kohli__abc12",
"name": "Virat Kohli",
"runs": 4,
"is_four": true,
"is_six": false,
"is_dot_ball": false
},
"bowler": {
"player_key": "c__player__jasprit_bumrah__def34",
"name": "Jasprit Bumrah"
},
"runs": 4,
"batsman_runs": 4,
"extras": {
"total": 0,
"wides": 0,
"wide": 0,
"no_balls": 0,
"no_ball": 0,
"byes": 0,
"bye": 0,
"leg_byes": 0,
"leg_bye": 0
},
"wicket": null,
"score": {
"runs": 156,
"wickets": 3,
"overs": [14, 6]
},
"four": true,
"six": false,
"comment": "FOUR! Short and wide outside off...",
"commentary": "FOUR! Short and wide outside off...",
"timestamp": 1711234567
}
Ball Fields
| Field | Type | Description |
|---|---|---|
key | string | Unique ball identifier |
overs | number[] | [over_number, ball_number] e.g., [14, 6] |
innings | string | Innings ID: a_1, b_1, a_2, b_2 |
batsman.player_key | string | Roanuz player key |
batsman.name | string | Batsman name |
batsman.runs | number | Runs scored by batsman on this ball |
batsman.is_four | boolean | Was it a four? |
batsman.is_six | boolean | Was it a six? |
batsman.is_dot_ball | boolean | Was it a dot ball? |
bowler.player_key | string | Bowler's Roanuz player key |
bowler.name | string | Bowler name |
runs | number | Total runs (including extras) |
extras | Object | Extras breakdown |
wicket | Object/null | Wicket details if wicket fell |
score | Object | Team score after this ball |
comment / commentary | string | Ball commentary text |
Illegal Deliveries and Duplicate overs
Roanuz's key is the unique ball-event identifier. The overs tuple is not a
unique event key.
Verified against match a-rz--cricket--UJ2037214113687568424: Roanuz returned
two related-ball objects with overs: [18, 3]:
key | overs | Meaning |
|---|---|---|
1057984 | [18, 3] | Wide: Saurabh Dubey to Sai Sudharsan: wide, 1 run. |
1057985 | [18, 3] | Legal delivery: Saurabh Dubey to Sai Sudharsan: 2 runs. |
Do not de-duplicate Roanuz balls by overs alone. Hannibal treats the delivery
identity as innings + overs + delivery kind (wide, no_ball, or legal).
The UI displays illegal deliveries with a suffix such as 18.3w or 18.3nb
so they do not look like duplicate legal balls.
Some live snapshots omit runs and extras on illegal deliveries while still
carrying the detail in commentary, for example wide, 1 run. Hannibal infers
the illegal-delivery score from explicit extras first, then commentary.
Wicket Object (when wicket is not null)
{
"type": "caught",
"how_out": "caught",
"player": {
"player_key": "c__player__...",
"key": "...",
"name": "Player Name"
}
}
Dismissal types: bowled, caught, lbw, run_out, stumped, hit_wicket, retired_hurt, etc.
Extras Fields
Both singular and plural forms may appear:
wides/wideno_balls/no_ballbyes/byeleg_byes/leg_bye
Hannibal's adapter checks both forms.
Hannibal Usage
File: RoanuzDataAdapter.ts
Direct Ball-by-Ball API
async getBallByBall(params: GetBallByBallParams): Promise<GetBallByBallResult> {
const data = await this.makeRequest(`/match/${matchId}/ball-by-ball/`);
let balls = this.normalizeBallByBall(matchId, data);
// Apply fromOver filter and limit
}
This endpoint exists in the adapter for the provider interface, but the live UI
does not call it. Live score and ball history use Match Details
play.related_balls and WebSocket match updates only.
Related Balls (Preferred for Live)
For live matches, the adapter prefers extracting play.related_balls from the match endpoint instead:
async getRelatedBalls(params): Promise<GetBallByBallResult> {
const data = await this.makeRequest(`/match/${matchId}/`);
const relatedBalls = data.play?.related_balls;
// Object with numeric keys → convert to array → normalize each ball
}
This is more reliable than the dedicated ball-by-ball endpoint for live data because:
- It comes as part of the match state (one API call vs two)
- It includes the current score context
- It's the same format the WebSocket uses
Normalization
Each ball is normalized to canonical BallEvent:
oversarray →overandballnumbers- Player keys → player names (extracted from key format if name missing)
- Extras checked in both singular/plural forms
- Innings string (
a_1,b_1) → innings number (1, 2) - Wicket type mapped to canonical dismissal type