Query Intervals
Query time intervals matching specific conditions using filters. This is the main endpoint for building custom video clips and analysis.
Request
POST /analyses/{id}/query-intervals
Request Body
{
"operator": "AND",
"pad_sec": 5,
"filters": [
{
"type": "team_possession",
"teams": ["team_0"]
},
{
"type": "ball_location",
"x_min": 70,
"x_max": 105
}
],
"existing_filters": [],
"gap_fill_sec": 5
}
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
operator |
string | "AND" |
How to combine filters: "AND" or "OR" |
pad_sec |
integer | 5 |
Padding around each interval (seconds) |
merge_results |
boolean | true |
Merge overlapping intervals. Set to false to keep each event as a separate interval. |
include_pixel_coords |
boolean | false |
Include player pixel coordinates for ball action events (sampled at 4 FPS). |
gap_fill_sec |
integer | 5 |
Merge intervals within this gap (seconds) |
filters |
array | [] |
List of filter conditions |
existing_filters |
array | [] |
Pre-defined time windows |
Response
[
{
"start": 125.5,
"end": 142.3,
"label": "Team A",
"type": "team_possession"
},
{
"start": 380.2,
"end": 395.8,
"label": "Team A",
"type": "team_possession"
}
]
Response Fields
| Field | Type | Description |
|---|---|---|
start |
float | Start time in seconds |
end |
float | End time in seconds |
label |
string | Description of the interval |
type |
string | Primary filter type |
Filter Types
| Type | Description | Documentation |
|---|---|---|
event_property |
Query events by metadata (v3+) | Details |
frame_state |
Player physical state (v3+) | Details |
ball_location |
Ball in pitch zone | Details |
player_ball_proximity |
Player near ball | Details |
team_possession |
Ball controlled by team | Details |
player_possession |
Player has ball | Details |
match_timeline |
Game period | Details |
scene |
Analysable footage | Details |
ball_action |
Pass, drive, shot (legacy) | Details |
Combining Filters
AND (Intersection)
All conditions must be true simultaneously:
{
"operator": "AND",
"filters": [
{ "type": "team_possession", "teams": ["team_0"] },
{ "type": "ball_location", "x_min": 70, "x_max": 105 }
]
}
Result: Moments when Team 0 has the ball AND it's in their attacking third.
OR (Union)
Any condition can be true:
{
"operator": "OR",
"filters": [
{ "type": "ball_location", "x_min": 0, "x_max": 16.5 },
{ "type": "ball_location", "x_min": 88.5, "x_max": 105 }
]
}
Result: Moments when ball is in either penalty area.
Existing Filters
Constrain results to specific time windows:
{
"filters": [
{ "type": "team_possession", "teams": ["team_0"] }
],
"existing_filters": [
{ "start": 0, "end": 600, "name": "First 10 minutes" },
{ "start": 2700, "end": 3300, "name": "Last 10 min of first half" }
]
}
Padding
The pad_sec parameter adds context around each interval:
pad_sec: 5→ Each clip starts 5 seconds early, ends 5 seconds late- Adjacent intervals are merged after padding (unless
merge_results: false) - Padding is clamped to video boundaries
Merge Results
By default, overlapping intervals are merged into a single clip. Set merge_results: false to keep each event as a separate interval:
{
"merge_results": false,
"filters": [
{ "type": "ball_action", "actions": ["pass"] }
]
}
This is particularly useful for ball action filters where you want each pass, drive, or shot as a distinct interval rather than having consecutive actions merged together.
Examples
Team Attacks
Find all moments when Team A attacks in the final third:
{
"operator": "AND",
"pad_sec": 3,
"filters": [
{ "type": "team_possession", "teams": ["team_0"] },
{ "type": "ball_location", "x_min": 70, "x_max": 105, "y_min": 0, "y_max": 68 },
{ "type": "scene", "is_analysable": true }
]
}
Player Highlight Reel
Find all moments when player #10 has the ball:
{
"filters": [
{ "type": "player_possession", "players": ["0-10"] }
],
"pad_sec": 5
}
Penalty Area Action
Find all action in both penalty areas:
{
"operator": "OR",
"pad_sec": 3,
"filters": [
{ "type": "ball_location", "x_min": 0, "x_max": 16.5, "y_min": 13.85, "y_max": 54.15 },
{ "type": "ball_location", "x_min": 88.5, "x_max": 105, "y_min": 13.85, "y_max": 54.15 }
]
}
Example Requests
curl -X POST "https://aiontheball.nl/api/v1/analyses/1246/query-intervals" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"operator": "AND",
"filters": [
{"type": "team_possession", "teams": ["team_0"]},
{"type": "ball_location", "x_min": 70, "x_max": 105}
]
}'
import requests
response = requests.post(
'https://aiontheball.nl/api/v1/analyses/1246/query-intervals',
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
},
json={
'operator': 'AND',
'pad_sec': 5,
'filters': [
{'type': 'team_possession', 'teams': ['team_0']},
{'type': 'ball_location', 'x_min': 70, 'x_max': 105}
]
}
)
intervals = response.json()
total_time = sum(i['end'] - i['start'] for i in intervals)
print(f"Found {len(intervals)} clips, {total_time:.1f}s total")
const response = await fetch(
'https://aiontheball.nl/api/v1/analyses/1246/query-intervals',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
operator: 'AND',
filters: [
{ type: 'team_possession', teams: ['team_0'] },
{ type: 'ball_location', x_min: 70, x_max: 105 }
]
})
}
);
const intervals = await response.json();