Skip to content

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();