Matchanalyse per Schnittstelle
Deine Software schickt mir ein Video, ich analysiere es und du holst die Zahlen ab – ohne Oberfläche, ohne manuelle Schritte. Für Buchungssysteme, Hallen-Apps, Kamerasysteme und Trainer-Plattformen.
So läuft es
- 1 · RegistrierenFormular unten ausfüllen. Ich prüfe die Anfrage und schicke dir Schlüssel und Webhook-Secret.
- 2 · Kamera einmessenPro feste Kamera einmal die zwölf Court-Punkte im Bild als Kamera-Profil anlegen (optional, aber empfohlen).
- 3 · Analyse einreichenPOST mit der Video-URL. Du bekommst sofort eine Job-Id zurück.
- 4 · Ergebnis abholenWebhook abwarten oder den Stand abfragen; bei „done“ steht das Ergebnis im selben Aufruf.
Zugang
Jede Anfrage trägt deinen Schlüssel im Authorization-Header. Der Schlüssel ist an deinen Partner-Eintrag gebunden, optional auf IP-Adressen eingeschränkt, und kann jederzeit gesperrt werden. Bitte serverseitig aufbewahren – nie in Apps oder im Browser.
Authorization: Bearer pv_live_… Basis-URL: https://padelvision.padelradar.io/api/v1
Kameras einmessen
Eine fest montierte Kamera sieht den Court immer gleich. Du meldest je Kamera einmal die zwölf Court-Punkte in Pixeln und bekommst eine camera_id, die du bei jeder Analyse mitgibst. Ohne Kamera-Profil erkenne ich den Court automatisch – das klappt meistens, ist aber ungenauer.
curl -X POST https://padelvision.padelradar.io/api/v1/cameras \
-H "Authorization: Bearer pv_live_…" \
-H "Content-Type: application/json" \
-d '{
"name": "Court 1 – Halle Nord",
"frame_width": 1920,
"frame_height": 1080,
"keypoints": [
{ "label": "corner_near_left", "x": 212, "y": 1040 },
{ "label": "corner_near_right", "x": 1710, "y": 1038 },
{ "label": "corner_far_left", "x": 702, "y": 318 },
{ "label": "corner_far_right", "x": 1222, "y": 316 },
{ "label": "service_near_left", "x": 330, "y": 870 },
{ "label": "service_near_right", "x": 1590, "y": 868 },
{ "label": "service_far_left", "x": 740, "y": 372 },
{ "label": "service_far_right", "x": 1184, "y": 371 },
{ "label": "net_left", "x": 560, "y": 540 },
{ "label": "net_right", "x": 1360, "y": 538 },
{ "label": "center_service_near", "x": 960, "y": 869 },
{ "label": "center_service_far", "x": 962, "y": 371 }
]
}'„near“ ist die Kameraseite, die Netzpunkte liegen am Boden (nicht an der Netzkante), Pixel beziehen sich auf frame_width × frame_height. GET /cameras listet deine Kameras.
Die zwölf Punkte bestimmen
Die Punkte sind Linienkreuzungen auf dem Boden – genau die, die du auch mit dem Auge im Standbild findest. Die Skizze zeigt den Court von oben, unten liegt die Kameraseite („near“). Die Nummern entsprechen der Reihenfolge in der Tabelle; die Reihenfolge im JSON ist egal, das Label zählt.
- 1corner_near_leftvordere Grundlinie, linke Ecke (vom Kamerabild aus links)
- 2corner_near_rightvordere Grundlinie, rechte Ecke
- 3corner_far_lefthintere Grundlinie, linke Ecke
- 4corner_far_righthintere Grundlinie, rechte Ecke
- 5service_near_leftvordere Aufschlaglinie trifft linke Seitenwand
- 6service_near_rightvordere Aufschlaglinie trifft rechte Seitenwand
- 7service_far_lefthintere Aufschlaglinie trifft linke Seitenwand
- 8service_far_righthintere Aufschlaglinie trifft rechte Seitenwand
- 9net_leftNetz trifft linke Seitenwand – am Boden, nicht an der Netzkante
- 10net_rightNetz trifft rechte Seitenwand – am Boden
- 11center_service_nearMittellinie trifft vordere Aufschlaglinie
- 12center_service_farMittellinie trifft hintere Aufschlaglinie
- Ein Standbild aus genau dieser Kamera in der Auflösung nehmen, in der die Videos später kommen (z. B. 1920 × 1080). Wird die Kamera später anders eingestellt oder bewegt, die Kamera neu einmessen.
- Für jeden der zwölf Punkte die Pixelposition ablesen – in jedem Bildprogramm, das Koordinaten anzeigt (GIMP, Photoshop, Vorschau, Paint) oder im Browser-Inspektor. x zählt von links, y von oben.
- Liegt ein Punkt außerhalb des Bildes (z. B. die vordere Grundlinie unter dem Bildrand), trotzdem die Position angeben, an der er läge – auch negative Werte oder Werte größer als das Bild sind erlaubt. Besser ist eine Kamera, die alle Linien sieht.
- Mit POST /cameras anlegen und die camera_id speichern. Zur Kontrolle eine kurze Testanalyse schicken: court.source im Ergebnis muss „profile“ sein.
Punkte automatisch vorschlagen lassen
Wenn du nicht von Hand messen willst: Schick ein Standbild (JPG/PNG) oder einen kurzen Clip aus der Kamera an POST /cameras/detect. Derselbe Detektor wie in der Analyse liefert die zwölf Punkte samt Konfidenz. Prüfe sie im Bild – bei niedriger Konfidenz ist der Vorschlag nur ein Trapez – und lege damit die Kamera an.
curl -X POST https://padelvision.padelradar.io/api/v1/cameras/detect \
-H "Authorization: Bearer pv_live_…" \
-H "Content-Type: application/json" \
-d '{ "image_url": "https://cdn.example.com/court-1-still.jpg" }'
{ "frame_width": 1920, "frame_height": 1080, "detected": true, "confidence": 0.93,
"keypoints": [ { "label": "corner_near_left", "x": 212, "y": 1040 }, … ] }Mehrere Anlagen und Kameras
Analyse einreichen
POST /analyses – Antwort 202 mit Job-Id. Den Download des Videos erledige ich im Hintergrund.
curl -X POST https://padelvision.padelradar.io/api/v1/analyses \
-H "Authorization: Bearer pv_live_…" \
-H "Content-Type: application/json" \
-d '{
"video_url": "https://cdn.example.com/match-48213.mp4?token=…",
"camera_id": "2f9c…",
"external_id": "match-48213",
"callback_url": "https://example.com/padelvision/webhook",
"trim_start_s": 0,
"trim_end_s": 5400
}'- video_url
- Pflicht. https-URL, von der ich das Video lade – MP4/MOV, bis 4 GB, bis 120 Minuten analysierter Abschnitt. Signierte, zeitlich begrenzte URLs sind ideal.
- camera_id
- Kamera-Profil (siehe oben). Alternativ court_points mit demselben Aufbau wie beim Anlegen einer Kamera.
- external_id
- Deine Kennung. Dieselbe Kennung liefert denselben Job zurück – keine Doppelanalyse bei Wiederholungen.
- callback_url
- Webhook für diesen Job; sonst die bei mir hinterlegte Webhook-URL.
- trim_start_s / trim_end_s
- Nur diesen Abschnitt analysieren (Sekunden).
{ "id": "7c1e…", "external_id": "match-48213", "status": "queued",
"result_url": "/api/v1/analyses/7c1e…" }Ergebnis abholen
GET /analyses/{id} liefert Stand und – bei status „done“ – das Ergebnis. Spieler sind Positionen, nicht Personen: near_left und near_right auf der Kameraseite, far_left und far_right gegenüber. Namen ordnest du selbst zu. Mit ?tracks=1 kommen zusätzlich die Positionsspuren (mehrere MB).
{
"id": "7c1e…",
"external_id": "match-48213",
"status": "done",
"stage": null,
"progress": 100,
"error": null,
"court": { "source": "profile", "confidence": null },
"result": {
"schema_version": 1,
"duration_s": 5388.2,
"total_ball_contacts": 2914,
"avg_rally_length_shots": 7.4,
"per_player": [
{ "player": "near_left", "ball_contacts": 742, "distance_covered_m": 3120.5 },
{ "player": "near_right", "ball_contacts": 701, "distance_covered_m": 2988.1 },
{ "player": "far_left", "ball_contacts": 745, "distance_covered_m": 3201.9 },
{ "player": "far_right", "ball_contacts": 726, "distance_covered_m": 3044.0 }
],
"rallies": [ { "index": 0, "start_t": 12.4, "end_t": 21.9, "shots": 9 }, … ],
"contacts": [ { "t": 12.4, "player": "far_left", "court_xy": [-6.1, 2.3],
"speed_kmh": 41.2, "shot_type": "serve" }, … ],
"heatmaps": [ … ],
"ball_speed": { "bin_edges_kmh": [ … ], "counts": [ … ], "max_kmh": 118.4, "avg_kmh": 46.7 }
}
}- status
- queued · processing · done · failed. Bei processing zeigt stage, wo die Analyse steht (ingest, court, players, ball, hits, aggregate …), progress in Prozent.
- result.contacts[]
- Jeder Ballkontakt: Zeit t in Sekunden, Spielerposition, Courtposition in Metern (Ursprung Courtmitte), Tempo in km/h, Schlagart (serve, volley, overhead, groundstroke, bandeja, vibora, topspin_smash …).
- result.rallies[]
- Ballwechsel mit Start, Ende und Zahl der Schläge.
- result.per_player[]
- Kontakte und Laufstrecke je Position; heatmaps und ball_speed dazu.
- court
- Woher die Homographie kam: profile (deine Kamera), clicked, auto (mit Konfidenz) oder synthetic.
Webhook
Bei done oder failed schicke ich einen POST an die callback_url – dreimal (sofort, nach 10 s, nach 60 s), bis dein Server mit 2xx antwortet. Das Ergebnis liegt unabhängig davon unter result_url.
POST https://example.com/padelvision/webhook
X-PadelVision-Signature: t=1759600000,v1=9d4c…
{ "event": "analysis.done", "id": "7c1e…", "external_id": "match-48213",
"status": "done", "error": null, "result_url": "/api/v1/analyses/7c1e…" }Signatur prüfen: v1 = HMAC-SHA256(secret, "<t>.<body>") in Hex; t ist der Unix-Zeitstempel. Bitte t gegen Wiederholungen begrenzen (z. B. fünf Minuten).
Fehler
- 400
- Anfrage unvollständig – das Feld error erklärt es.
- 401 / 403
- Schlüssel fehlt, ungültig, gesperrt oder falsche IP.
- 404
- Analyse gehört nicht zu deinem Schlüssel.
- 429
- Stundenlimit erreicht (60 neue Analysen je Stunde).
- 503
- Analyse derzeit nicht verfügbar.
- failed
- Fehler während der Analyse stehen am Job: status „failed“ plus error, z. B. „video download failed: HTTP 403“.
Grenzen
- 60 neue Analysen je Stunde und Partner.
- Video bis 4 GB, analysierter Abschnitt bis 120 Minuten.
- Ein Match von 90 Minuten ist in der Regel in unter einer Stunde fertig – nicht häufiger als alle 30 Sekunden abfragen, besser den Webhook nutzen.
Daten
- Das Originalvideo bleibt nur für die Analyse gespeichert; die Zahlen bleiben abrufbar.
- Videos zeigen Personen – bitte deine Spieler über die Analyse informieren. Einen Auftragsverarbeitungsvertrag stelle ich auf Wunsch.
- Keine Gesichtserkennung, keine Identifizierung: Spieler sind Positionen auf dem Court.
Als Partner registrieren
Ich prüfe jede Anfrage persönlich und melde mich in der Regel innerhalb von zwei Werktagen mit Schlüssel und Secret.