Was Ballbuddy ist und welche Daten es gibt
Ballbuddy ist die Plattform für Maturaball-Komitees in Österreich, ein Projekt von Framed aus Graz. Öffentlich sichtbar ist der Ballkalender mit einer eigenen Seite je Ball. Das Komitee arbeitet dahinter in einer eigenen App – die ist für Agenten nicht erreichbar und soll es auch nicht sein.
Die API führt drei Arten von Daten: Bälle (Motto, Termin, Schule, Location, Stadt, Bundesland, Ticket-Hinweis, Cover), Schulen und Locations. Alle drei sind öffentlich und redaktionell gepflegt: Komitees tragen ihren Ball selbst ein, wir prüfen und ergänzen. Wie viele Bälle gerade drin sind, sagt dir das Feld total in jeder Listenantwort – der Bestand ändert sich täglich, deshalb steht hier keine Zahl.
Die Discovery-Dateien und die API gibt es nur auf ballbuddy.at; die anderen Flächen (app., partner., admin.) antworten darauf mit 404.
Grundregeln für alle Endpunkte
- Nur
GET. Antworten sind JSON in UTF-8 (der Kalendereintrag isttext/calendar), CORS ist offen:Access-Control-Allow-Origin: *. - Zeiten sind ISO-8601 in UTC (
starts_at,ends_at,doors_at,updated_at). Für die Anzeige gehört alles nachEurope/Vienna. - Eine Saison heißt nach ihrem Endjahr:
season=2027ist die Saison 2026/27. - Fehler kommen als JSON:
400mit{ "error": "invalid_params", "issues": [{ "path", "message" }] }bei ungültigen Parametern,404mit{ "error": "not_found" }bei unbekanntem Slug. - Der Slug eines Balls ist ein Vertrag: Alte Slugs leiten mit
301auf den aktuellen weiter – in der API wie auf der Ball-Seite unterhttps://ballbuddy.at/ball/<slug>.
Die sieben Endpunkte
Die Namen in Klammern sind die operationIds aus der OpenAPI-Beschreibung – dieselben stehen als capabilities im ai-catalog.
Bälle suchen GET /api/public/v1/balls (searchBalls)
Alle Parameter sind optional: season (Endjahr; ohne from, to und q gilt die laufende Saison), state (Bundesland, exakt einer von Burgenland, Kärnten, Niederösterreich, Oberösterreich, Salzburg, Steiermark, Tirol, Vorarlberg, Wien), city, school (Slug), venue (Slug), from und to (JJJJ-MM-TT als Wiener Kalendertag, from inklusive, to exklusive), q (Freitext über Motto, Schule und Stadt, mindestens zwei Zeichen), page (ab 1) und per (1–100, Standard 48). Sortiert wird nach starts_at aufsteigend. filters in der Antwort nennt die gültigen Werte für den nächsten Aufruf – Saisonen, Bundesländer, Städte, Schulen und Locations mit Zählern.
curl -s "https://ballbuddy.at/api/public/v1/balls?season=2027&state=Steiermark&per=10"Antwort, gekürzt – Beispielwerte, kein Live-Abruf:
{
"items": [
{
"id": "…",
"slug": "hak-weiz-2027",
"url": "https://ballbuddy.at/ball/hak-weiz-2027",
"title": "Casino Royale",
"starts_at": "2027-01-16T19:00:00+00:00",
"ends_at": null,
"doors_at": null,
"season": 2027,
"status": "scheduled",
"city": "Weiz",
"state": "Steiermark",
"school": { "name": "HAK Weiz", "slug": "hak-weiz" },
"venue": { "name": "Stadthalle Weiz", "slug": "stadthalle-weiz", "address": "…", "zip": "8160", "city": "Weiz" },
"venue_text": null,
"instagram": "hakweiz.ball",
"tickets": { "mode": "link", "url": "https://…" },
"cover": { "url640": "https://…/640.webp", "url1280": "https://…/1280.webp", "blur": "data:image/webp;base64,…" },
"is_claimed": false,
"updated_at": "2026-09-05T08:00:00+00:00",
"has_motto": true,
"time_known": true,
"schools": [{ "name": "HAK Weiz", "slug": "hak-weiz" }]
}
],
"total": …,
"page": 1,
"per": 10,
"filters": {
"seasons": [2025, 2026, 2027, 2028, 2029],
"states": ["Burgenland", "…"],
"cities": ["Graz", "…"],
"schools": [{ "slug": "hak-weiz", "name": "HAK Weiz", "count": 1 }],
"venues": [{ "slug": "stadthalle-weiz", "name": "Stadthalle Weiz", "count": 2 }]
}
}status ist scheduled, cancelled oder postponed. tickets ist null, wenn das Komitee keine Ticket-Infos gesetzt hat; mode ist link, instagram oder info – bei instagram und info ist url null, die Details stehen auf der Ball-Seite. cover ist null, solange kein Bild da ist.
Ein Ball im Detail GET /api/public/v1/balls/{slug} (getBall)
Wie ein Listeneintrag, zusätzlich description, links ([{ type, label?, url }]), tanzschule, disco, band und ticket_types ([{ id, name, price_presale_cents, price_door_cents, sort }] – nur öffentliche Kategorien, Preise in Cent). Unbekannte Slugs antworten 404, alte Slugs leiten mit 301 auf die aktuelle Adresse weiter.
curl -s "https://ballbuddy.at/api/public/v1/balls/hak-weiz-2027"Antwort, gekürzt – Beispielwerte, kein Live-Abruf:
{
"slug": "hak-weiz-2027",
"title": "Casino Royale",
"starts_at": "2027-01-16T19:00:00+00:00",
… (alle Felder des Listeneintrags), dazu:
"description": "…",
"links": [{ "type": "…", "label": "…", "url": "https://…" }],
"tanzschule": "…",
"disco": null,
"band": null,
"ticket_types": [{ "id": "…", "name": "…", "price_presale_cents": …, "price_door_cents": …, "sort": 0 }]
}Schulen GET /api/public/v1/schools (listSchools)
Alle Schulen mit Slug, Schultyp, Stadt, Bundesland, Website, Instagram und ball_count (zählt nur veröffentlichte Bälle), sortiert nach Name. Der slug ist der Wert für den Filter school= bei der Ballsuche.
curl -s "https://ballbuddy.at/api/public/v1/schools"Antwort, gekürzt – Beispielwerte, kein Live-Abruf:
{
"items": [
{ "id": "…", "name": "HAK Weiz", "slug": "hak-weiz", "school_type": "…", "city": "Weiz", "state": "Steiermark", "website": "https://…", "instagram": null, "ball_count": 1 }
],
"total": …
}Locations GET /api/public/v1/venues (listVenues)
Alle Locations mit Adresse, Postleitzahl, Stadt, Bundesland, Website, Koordinaten (lat, lng) und ball_count. Der slug ist der Wert für den Filter venue=.
curl -s "https://ballbuddy.at/api/public/v1/venues"Antwort, gekürzt – Beispielwerte, kein Live-Abruf:
{
"items": [
{ "id": "…", "name": "Stadthalle Weiz", "slug": "stadthalle-weiz", "address": "…", "zip": "8160", "city": "Weiz", "state": "Steiermark", "website": null, "lat": …, "lng": …, "ball_count": 2 }
],
"total": …
}Alle Ball-Seiten GET /api/public/v1/sitemap (listBallUrls)
Die Adressen aller veröffentlichten Ball-Seiten mit lastModified. Wenn du Ball-Seiten verlinken oder gezielt abrufen willst, ist das die vollständige Liste – kürzer als die Ballsuche und ohne Paginierung.
curl -s "https://ballbuddy.at/api/public/v1/sitemap"Antwort, gekürzt – Beispielwerte, kein Live-Abruf:
{
"items": [
{ "url": "https://ballbuddy.at/ball/hak-weiz-2027", "lastModified": "2026-09-05T08:00:00+00:00" }
],
"total": …
}Kalendereintrag GET /api/ics/{slug} (getBallIcs)
Ein iCalendar-Eintrag (text/calendar) je Ball mit zwei Erinnerungen (eine Woche und ein Tag vorher), eine Stunde cachebar, 404 bei unbekanntem Slug. Bei has_motto: false heißt der Eintrag „Maturaball <Schule>“, bei time_known: false ist er ganztägig – du musst dafür nichts selbst rechnen.
curl -s "https://ballbuddy.at/api/ics/hak-weiz-2027"Antwort, gekürzt – Beispielwerte, kein Live-Abruf:
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Ballbuddy//Ballkalender//DE
BEGIN:VEVENT
UID:…@ballbuddy.at
DTSTART:20270116T190000Z
DTEND:…
SUMMARY:Maturaball HAK Weiz: Casino Royale
LOCATION:Stadthalle Weiz\, 8160 Weiz
URL:https://ballbuddy.at/ball/hak-weiz-2027
BEGIN:VALARM
TRIGGER:-P7D
…
END:VEVENT
END:VCALENDARStatus GET /api/health (getHealth)
Sagt nur, ob der Dienst läuft – Cache-Control: no-store, keine Daten. Zum Prüfen, ob sich Daten geändert haben, ist das der falsche Weg; dafür gibt es den ETag (unten).
curl -s "https://ballbuddy.at/api/health"Antwort, gekürzt – Beispielwerte, kein Live-Abruf:
{ "ok": true, "service": "ballbuddy", "ts": "2026-09-05T08:00:00.000Z" }Cache und ETag: höflich pollen
Alle Antworten der Public-API tragen Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400 und einen schwachen ETag. Merk dir den ETag und schick ihn beim nächsten Abruf als If-None-Match mit – hat sich nichts geändert, bekommst du 304 Not Modified ohne Body. Einmal pro Stunde je Abfrage reicht; die Daten ändern sich täglich, nicht minütlich. Ein sprechender User-Agent mit Kontaktmöglichkeit ist erwünscht, zum Beispiel MeinAgent/1.0 (+https://example.org/bot).
curl -s -i "https://ballbuddy.at/api/public/v1/balls/hak-weiz-2027" | grep -i '^etag'
# ETag: W/"…"
curl -s -o /dev/null -w '%{http_code}' -H 'If-None-Match: W/"…"' "https://ballbuddy.at/api/public/v1/balls/hak-weiz-2027"
# 304Datenlücken: has_motto, time_known und schools
Drei Felder verhindern die typischen Fehler beim Anzeigen – dieselbe Erklärung steht im Skill ballbuddy-ballkalender, weil ein Agent selten beides liest:
has_motto:false⇒titleist generisch („HLW Weiz Maturaball“, „Maturaball HLW Weiz 2027“) und kein Motto. Zeig dann die Schule (schools[0].name, bei mehreren „A × B“) als Titel und „Motto folgt“ als Untertitel – so macht es ballbuddy.at selbst.titlebleibt unverändert,urlundsluggelten weiter.time_known:false⇒ die Uhrzeit ist unbekannt (starts_atträgt einen Platzhalter). Nur das Datum vonstarts_atin Europe/Vienna verwenden, keine Uhrzeit anzeigen, Kalender-Einträge ganztägig anlegen;ends_atunddoors_atdann ignorieren.schools: alle beteiligten Schulen, Hauptschule zuerst (nie leer, wennschoolgesetzt ist). Gemeinsame Bälle (HTL × Modeschule) haben mehr als einen Eintrag;schoolbleibt die Hauptschule. Der Filterschool=<slug>trifft jede beteiligte Schule.
Was die API nicht kann
- Kein Schreiben. Keine Bälle anlegen oder ändern, keine Erinnerungen, keine Übernahme-Anfragen – das machen Menschen auf der Ball-Seite.
- Keine Ticketkäufe.
tickets.urlundticket_typessind Hinweise; verkauft wird beim Komitee. - Keine personenbezogenen Daten. Keine Mitglieder, Kontakte oder E-Mail-Adressen – die Komitee-App liegt hinter einem Login und hat keine öffentliche Schnittstelle.
- Keine internen Felder. Prüf- und Importfelder (Quelle, Referenzen, Prüf-Flags) sind nicht Teil der API.
- Kein Token, keine Rate-Tiers. Lesen ist frei, mehr gibt es nicht – auth.md sagt genau das.
MCP: dieselben Daten als Werkzeuge
Wer lieber Werkzeuge aufruft, statt Adressen zu bauen, verbindet sich mit dem MCP-Server von Ballbuddy (Model Context Protocol): Endpunkt https://ballbuddy.at/api/mcp, Transport streamable-http, kein Token nötig. Die Werkzeuge rufen dieselben Funktionen wie die REST-Endpunkte oben – gleiche Daten, gleiche Reihenfolge, gleiche Regeln zu has_motto und time_known. Geschrieben wird auch hier nichts.
search_balls– Maturabälle suchen: Freitext, Stadt, Bundesland, Schule, Location, Saison, Zeitraum / search Austrian Maturabälleget_ball– Ein Ball im Detail per Slug: Beschreibung, Location mit Adresse, Links, Ticketkategorien / ball details by slugget_ball_calendar– Kalendereintrag (iCalendar/ICS) eines Balls samt Adresse der .ics-Datei / iCalendar entry for one balllist_schools_and_venues– Schulen und Locations mit Anzahl veröffentlichter Bälle, optional je Bundesland / schools and venues with ball counts
Konfiguration zum Kopieren (Claude Desktop, Cursor, MCP Inspector und andere Clients mit mcpServers):
{
"mcpServers": {
"ballbuddy": {
"url": "https://ballbuddy.at/api/mcp"
}
}
}Was ein Werkzeug genau erwartet und zurückgibt, sagt tools/list – die Beschreibungen dort sind zweisprachig (Deutsch, dann Englisch). Maschinenlesbar beschreibt die Server-Card unter https://ballbuddy.at/.well-known/mcp/server-card.json Endpunkt, Transport und Werkzeuge; derselbe Eintrag steht an erster Stelle im ai-catalog.
Maschinenlesbare Beschreibungen
- MCP-Server-Card (SEP-1649): https://ballbuddy.at/.well-known/mcp/server-card.json – Endpunkt
https://ballbuddy.at/api/mcp, Transport, Fähigkeiten und die vier Werkzeuge mit Kurzbeschreibung; keinauth-Block, weil es keinen Token gibt. - OpenAPI 3.1: https://ballbuddy.at/openapi.json – alle sieben Operationen mit Parametern, Grenzen und Schemata.
- API-Catalog (RFC 9727): https://ballbuddy.at/.well-known/api-catalog – der Einstieg, der auf OpenAPI, diese Seite und den Status verweist. Jede Antwort des Hosts trägt außerdem einen
Link-Header mitrel="api-catalog",service-desc,service-docundsitemap. - Capability-Manifest (ARD, ai-catalog 1.0): https://ballbuddy.at/.well-known/ai-catalog.json – bündelt MCP-Server, REST-API und Skills mit stabilen
urn:air:ballbuddy.at:…-Identifiern und Beispielfragen. Zusätzlich auffindbar überAgentmap:in der robots.txt und<link rel="ai-catalog">im Head jeder Seite. - Agent Skills (RFC v0.2.0): Index https://ballbuddy.at/.well-known/agent-skills/index.json mit
sha256-Digest je Skill; ballbuddy-ballkalender (Bälle finden) und ballbuddy-balltermin (ein konkreter Ball, Ball-Seite, Kalendereintrag). - Authentifizierung: https://ballbuddy.at/auth.md – kurz, weil es keinen Token gibt.
Prüfen: pnpm agents:verify im Repo geht diese Liste gegen eine laufende Instanz durch – eine Zeile je Zusicherung, vor jedem Deploy.
Nutzung und Quellenangabe
Du darfst die Daten in Antworten verwenden. Nenn Ballbuddy als Quelle und verlink die Ball-Seite (url aus der Antwort) – dort stehen Beschreibung, Tickets und der Weg zum Komitee, und dort landen Menschen richtig. Fragen, Fehler oder ein Feld, das dir fehlt: [email protected] oder das Kontaktformular.