Partner-API v1

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.

Nur Zahlen
Du bekommst das Analyse-JSON: Kontakte, Ballwechsel, Laufwege, Schlagarten, Tempo, Heatmaps. Keine Videos zurück, keine Padel-Vision-Oberfläche nötig.
Feste Kameras
Einmal die zwölf Court-Punkte je Kamera melden, danach läuft jede Analyse auf derselben Homographie – präziser als jede automatische Erkennung.
Asynchron
Einreichen dauert eine Sekunde, die Analyse Minuten. Du fragst den Stand ab oder bekommst einen signierten Webhook, wenn das Ergebnis da ist.
Freigabe von Hand
Zugang nur für geprüfte Partner. Du registrierst dich unten, ich melde mich mit dem Schlüssel.

So läuft es

  1. 1 · RegistrierenFormular unten ausfüllen. Ich prüfe die Anfrage und schicke dir Schlüssel und Webhook-Secret.
  2. 2 · Kamera einmessenPro feste Kamera einmal die zwölf Court-Punkte im Bild als Kamera-Profil anlegen (optional, aber empfohlen).
  3. 3 · Analyse einreichenPOST mit der Video-URL. Du bekommst sofort eine Job-Id zurück.
  4. 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.

123456789101112Kameraseite (near)Gegenseite (far)
  1. 1corner_near_leftvordere Grundlinie, linke Ecke (vom Kamerabild aus links)
  2. 2corner_near_rightvordere Grundlinie, rechte Ecke
  3. 3corner_far_lefthintere Grundlinie, linke Ecke
  4. 4corner_far_righthintere Grundlinie, rechte Ecke
  5. 5service_near_leftvordere Aufschlaglinie trifft linke Seitenwand
  6. 6service_near_rightvordere Aufschlaglinie trifft rechte Seitenwand
  7. 7service_far_lefthintere Aufschlaglinie trifft linke Seitenwand
  8. 8service_far_righthintere Aufschlaglinie trifft rechte Seitenwand
  9. 9net_leftNetz trifft linke Seitenwand – am Boden, nicht an der Netzkante
  10. 10net_rightNetz trifft rechte Seitenwand – am Boden
  11. 11center_service_nearMittellinie trifft vordere Aufschlaglinie
  12. 12center_service_farMittellinie trifft hintere Aufschlaglinie
  1. 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.
  2. 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.
  3. 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.
  4. 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

Eine Kamera, ein Profil
Jede physische Kamera bekommt ihr eigenes Profil – auch zwei Courts in derselben Halle sind zwei Kameras. Du kannst beliebig viele anlegen; der Name ist dein Ordnungsschlüssel, z. B. „Halle Nord – Court 3“.
Die Zuordnung liegt bei dir
Deine Software weiß, von welchem Court ein Video stammt, und gibt die passende camera_id mit. Padelradar erkennt die Kamera nicht am Bild.
Ohne camera_id geht es auch
Lässt du camera_id weg, erkenne ich den Court je Video automatisch (court.source = „auto“ mit Konfidenz). Das ist der richtige Weg für wechselnde oder unbekannte Kameras – nur etwas ungenauer als ein eingemessenes Profil.
Kamera bewegt?
Nach jedem Umbau, Zoom oder Neigen das Profil neu einmessen (einfach eine neue Kamera anlegen und die alte nicht mehr benutzen). Ein falsches Profil ist schlechter als keines.

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.

Mit dem Absenden erklärst du dich einverstanden, dass ich deine Angaben zur Bearbeitung der Anfrage speichere.
Partner-API: Padel-Matchanalyse per Schnittstelle | Padel Vision