Die Spielplan-API liefert Vereinen ihre anstehenden Spiele aus TeamSL. Zu jedem Spiel gehören der Stand der Schiedsrichter-Ansetzungen und die Entfernung der angesetzten Schiedsrichter:innen zur Halle. Die Daten kommen als JSON für eigene Programme oder als CSV für Excel und Google Sheets.
Du willst nur den Spielplan eines Teams oder Vereins im Kalender sehen? Dafür brauchst du keinen Schlüssel: Kalender-Abo.
Zugang
Für die API braucht dein Verein einen persönlichen API-Schlüssel. Er beginnt mit spa_ und gilt nur für die Vereinsnummer deines Vereins. Spielgemeinschaften, an denen dein Verein beteiligt ist, können wir auf Wunsch mit aufnehmen.
Schlüssel beantragen
API-Schlüssel müssen per E-Mail an support@nbbv.de beantragt werden. Bitte gib an:
- Vereinsname und Vereinsnummer
- Name und Funktion der Person, die den Schlüssel nutzt, mit Kontakt-E-Mail
- ggf. Spielgemeinschaften, an denen dein Verein beteiligt ist und die mit abgefragt werden sollen
- kurz den Verwendungszweck (z. B. Übersicht der Heimspiele, Fahrtkostenplanung)
Schlüssel per E-Mail beantragen
Den Schlüssel übergibst du bei jeder Anfrage auf eine von zwei Arten:
- Als HTTP-Header
X-API-Key: spa_…(empfohlen, für Programme und Skripte) - Als Parameter
api_key=spa_…in der Adresse, wenn sich kein Header setzen lässt, etwa im Browser oder in Google Sheets. Der Schlüssel steht dann aber im Browserverlauf.
Schnellstart
Die Spiele der nächsten 7 Tage, zum Anschauen im Browser:
https://nbbv-basketball.de/spielplan-api/v1/spiele?vereinsnummer=720000&tage=7&api_key=DEIN_SCHLÜSSEL
Mit dem Baukasten stellst du dir die Adresse selbst zusammen:
Ersetze DEIN_SCHLÜSSEL durch deinen API-Schlüssel. Der Baukasten sendet nichts, er setzt nur die Adresse zusammen.
Parameter
GET https://nbbv-basketball.de/spielplan-api/v1/spiele
| Parameter | Werte | Standard | Bedeutung |
|---|---|---|---|
vereinsnummer | z. B. 720000 oder 0720000 | Verein des Schlüssels | Vereinsnummer aus TeamSL. Muss zum Schlüssel passen. |
modus | alle, heim, auswaerts | alle | Heim- und Auswärtsspiele, nur Heim- oder nur Auswärtsspiele. Heimspiele einer Spielgemeinschaft zählen als Heimspiele. |
tage | 0 bis 365 | 7 | Blick nach vorn ab heute. tage=1 heißt: nur heute. tage=7 heißt: heute und die folgenden 6 Tage. tage=0 geht nur zusammen mit zurueck. |
zurueck | 0 bis 365 | 0 | Blick zurück: die Tage vor heute, mit Ergebnissen. zurueck=7&tage=0 liefert die letzte Woche, zurueck=365&tage=365 die ganze Saison. |
format | json, csv | json | Ausgabeformat |
trenner | semikolon, komma | semikolon | Nur bei CSV: Semikolon mit Dezimalkomma für Excel, Komma mit Dezimalpunkt für Google Sheets und andere Programme. |
api_key | dein Schlüssel | – | Nur, wenn du den Header X-API-Key nicht setzen kannst. |
Die Spiele sind nach Datum und Uhrzeit sortiert. Zeiten gelten in deutscher Ortszeit.
Spiele eines Teams
Für die Spiele genau eines Teams, etwa für eine Team-Seite, gibt es /v1/teamspiele. Du gibst Team und Liga an und bekommst dieselben Daten im selben Format wie bei /v1/spiele, nur für dieses Team.
https://nbbv-basketball.de/spielplan-api/v1/teamspiele?vereinsnummer=720000&team=TSV+Musterstadt+2&liga=54136&tage=365
| Parameter | Bedeutung |
|---|---|
vereinsnummer | wie bei /v1/spiele |
team | Pflicht. Teamname genau wie in TeamSL, URL-kodiert (Leerzeichen als + oder %20) |
liga | Pflicht. Liga-ID (empfohlen) oder der genaue Liga- bzw. Kurzname |
modus, tage, format, trenner | wie bei /v1/spiele |
Die Liga ist nötig, weil derselbe Teamname in mehreren Ligen vorkommen kann, zum Beispiel in Liga und Pokal.
Teamname und Liga nachschlagen
Die genauen Namen und Liga-IDs liefert die Teamliste deines Vereins. Sie enthält alle Teams mit künftigen Spielen, je Liga:
https://nbbv-basketball.de/spielplan-api/v1/teamliste?vereinsnummer=720000
{
"verein": { "vereinsnummer": 720000, "name": "TSV Musterstadt" },
"anzahl": 12,
"teams": [
{
"team": "TSV Musterstadt 2",
"liga": { "id": 54136, "name": "Landesliga Herren 1", "kurz": "LLH1", "altersklasse": "Senioren", "geschlecht": "männlich" },
"spiele": 14,
"naechstesSpiel": "2026-10-03",
"abfrage": "vereinsnummer=720000&team=TSV+Musterstadt+2&liga=54136"
}
]
}
Der Wert in abfrage ist fertig kodiert. Hänge ihn an /v1/teamspiele? an und ergänze bei Bedarf tage oder format.
Die Teamnamen kommen aus TeamSL. Ändert der Verein dort einen Namen, ändert sich auch die Abfrage.
Antwort als JSON
Beispiel, gekürzt auf ein Spiel:
{
"verein": { "vereinsnummer": 720000, "name": "TSV Musterstadt" },
"zeitraum": { "von": "2026-09-28", "bis": "2026-10-04", "tage": 7 },
"modus": "alle",
"stand": "2026-09-28T07:50:12Z",
"anzahl": 13,
"spiele": [
{
"spielId": 2800001,
"geaendertAm": "2026-09-27T18:04:11Z",
"datum": "2026-10-03",
"wochentag": "Sa",
"uhrzeit": "16:00",
"heimspiel": true,
"liga": {
"name": "Landesliga Herren 1", "kurz": "LLH1",
"altersklasse": "Senioren", "geschlecht": "männlich",
"bezirk": null, "verband": "NBBV"
},
"spielnr": 42,
"spieltag": 3,
"heim": {
"team": "TSV Musterstadt 2", "verein": "TSV Musterstadt", "vereinsnummer": 720000,
"trikot": { "hemd": "blau", "hose": "blau" }
},
"gast": {
"team": "SC Beispielort", "verein": "SC Beispielort", "vereinsnummer": 710000,
"trikot": { "hemd": "weiß", "hose": "schwarz" }
},
"halle": { "name": "Sporthalle am Markt", "strasse": "Marktstraße 1", "plz": "30159", "ort": "Hannover" },
"abgesagt": false,
"verzicht": false,
"spielortGeaendert": false,
"spielzeitGeaendert": false,
"ergebnis": null,
"ansetzungenVerfuegbar": true,
"ansetzungen": [
{ "position": "SR1", "status": "angesetzt", "bestaetigt": true, "verein": null, "entfernungKm": 8.8 },
{ "position": "SR2", "status": "Verein zuständig", "bestaetigt": null, "verein": "TSV Musterstadt", "entfernungKm": null }
]
}
]
}
| Feld | Bedeutung |
|---|---|
stand | Zeitpunkt des letzten Abgleichs mit TeamSL (UTC, ISO 8601) |
spielId | Eindeutige Spiel-ID aus TeamSL, gleich bleibend auch bei Verlegungen |
geaendertAm | Zeitpunkt der letzten Änderung an diesem Spiel (UTC, ISO 8601): Termin, Halle, Teams, Status, Ansetzungen oder Entfernung. Ein Abgleich ohne Änderung setzt ihn nicht neu. So erkennst du, was sich seit deinem letzten Abruf geändert hat. |
spielnr, spieltag | Spielnummer und Spieltag laut Spielplan |
heimspiel | true, wenn dein Verein (oder deine Spielgemeinschaft) Heimverein ist |
heim.trikot, gast.trikot | Trikotfarben laut TeamSL: Heimteam in Heimfarben, Gastteam in Auswärtsfarben. null, wenn der Verein keine Farben hinterlegt hat. |
liga.kurz | Kurzname der Liga wie in der SR-Ansetzung |
abgesagt, verzicht | Spiel abgesagt bzw. Verzicht eines Teams |
spielortGeaendert, spielzeitGeaendert | Spielort oder Tipoff-Zeit wurde gegenüber dem ursprünglichen Plan geändert |
ergebnis | Bei gespielten Spielen der Endstand, z. B. { "heim": 58, "gast": 85, "bestaetigt": true }. bestaetigt ist false, solange das Ergebnis in TeamSL noch nicht bestätigt ist. Bei künftigen Spielen null. |
ansetzungenVerfuegbar | false, wenn die Ansetzungen beim letzten Abgleich nicht geladen werden konnten. Dann fehlen ansetzungen oder sind veraltet. |
ansetzungen | Eine Zeile je Position, siehe nächster Abschnitt |
Felder, die TeamSL nicht liefert, sind null.
Ansetzungen und Entfernung
SR1 und SR2 sind immer enthalten. SR3, Kommissar:in (KOM) und SR-Coach (COA) erscheinen nur, wenn die Position im Spiel besetzt oder vergeben ist.
status | Bedeutung |
|---|---|
angesetzt | Eine Person ist angesetzt. bestaetigt sagt, ob sie die Ansetzung in TeamSL bestätigt hat. |
vorläufig angesetzt | Die Ansetzung ist noch vorläufig. |
offen angeboten | Die Position wird in der Spielebörse angeboten. verein nennt den Verein, der sie abgegeben hat. |
Verein zuständig | Die Position ist einem Verein zugeteilt, der sie selbst besetzen muss (verein). |
nicht besetzt | Noch niemand angesetzt und kein Verein zuständig. |
Entfernung
entfernungKm ist die Entfernung vom Wohnort der angesetzten Person zur Halle in Kilometern, auf eine Nachkommastelle gerundet. Es ist der Wert, den TeamSL in der Schiedsrichter-Ansetzung anzeigt, als einfache Strecke. Er hilft zum Beispiel, die Fahrtkosten abzuschätzen.
Die Entfernung ist null, wenn
- niemand angesetzt ist,
- die Person nicht zum Schiedsrichter-Pool des NBBV gehört (zum Beispiel bei Regionalliga-Ansetzungen aus anderen Landesverbänden),
- TeamSL für die Person keine Geodaten hat,
- die Entfernung zur Halle noch nicht ermittelt werden konnte, etwa bei Hallen außerhalb Niedersachsens und Bremens.
Antwort als CSV
Mit format=csv gibt es eine Zeile je Spiel. Standardmäßig ist die Datei mit Semikolon getrennt, nutzt das Dezimalkomma und beginnt mit einer Byte-Order-Mark, damit Excel Umlaute und Spalten direkt richtig erkennt. Mit trenner=komma kommen Komma, Dezimalpunkt und keine Byte-Order-Mark.
Spalten in dieser Reihenfolge:
| Spalten | Inhalt |
|---|---|
spiel_id, datum, wochentag, uhrzeit | wie im JSON |
heim_auswaerts | heim oder auswärts |
liga, liga_kurz, altersklasse, geschlecht, spielnr | Liga und Spielnummer |
heim, heim_vereinsnummer, gast, gast_vereinsnummer | Teams und Vereinsnummern |
heim_trikot, gast_trikot | Trikotfarben als „Hemd / Hose“ |
halle, halle_strasse, halle_plz, halle_ort | Spielort |
abgesagt, verzicht, spielort_geaendert, spielzeit_geaendert | ja oder nein |
ergebnis_heim, ergebnis_gast, ergebnis_bestaetigt | Endstand und ob er bestätigt ist, bei künftigen Spielen leer |
geaendert_am | wie geaendertAm im JSON |
sr1_status, sr1_bestaetigt, sr1_verein, sr1_entfernung_km | Ansetzung SR1, ebenso für sr2_, sr3_, kom_ und coa_ |
Leere Zellen bedeuten: kein Wert vorhanden.
Beispiele
Excel (Power Query)
- Daten → Aus dem Web → Erweitert wählen.
- Als URL eintragen:
https://nbbv-basketball.de/spielplan-api/v1/spiele?vereinsnummer=720000&tage=14&format=csv - Unter HTTP-Anforderungsheaderparameter links
X-API-Keyund rechts deinen Schlüssel eintragen. - Laden. Mit Daten → Alle aktualisieren holst du später den aktuellen Stand.
Google Sheets
In eine leere Zelle:
=IMPORTDATA("https://nbbv-basketball.de/spielplan-api/v1/spiele?vereinsnummer=720000&tage=14&format=csv&trenner=komma&api_key=DEIN_SCHLÜSSEL")
Google Sheets kann keinen Header setzen, deshalb steht der Schlüssel hier in der Formel. Teile die Tabelle nur mit Personen, die den Schlüssel sehen dürfen.
Kommandozeile (curl)
curl -H "X-API-Key: DEIN_SCHLÜSSEL" \
"https://nbbv-basketball.de/spielplan-api/v1/spiele?vereinsnummer=720000&modus=heim&tage=14"
JavaScript
const res = await fetch(
"https://nbbv-basketball.de/spielplan-api/v1/spiele?vereinsnummer=720000&tage=7",
{ headers: { "X-API-Key": process.env.SPIELPLAN_API_KEY } }
);
const { spiele } = await res.json();
for (const s of spiele) {
console.log(s.datum, s.uhrzeit, s.heim.team, "–", s.gast.team);
}
Den Schlüssel nur serverseitig verwenden, nie in Webseiten-Code, den Besucher:innen einsehen können.
Python
import os, requests
r = requests.get(
"https://nbbv-basketball.de/spielplan-api/v1/spiele",
params={"vereinsnummer": 720000, "modus": "alle", "tage": 7},
headers={"X-API-Key": os.environ["SPIELPLAN_API_KEY"]},
timeout=30,
)
r.raise_for_status()
for s in r.json()["spiele"]:
print(s["datum"], s["uhrzeit"], s["heim"]["team"], "–", s["gast"]["team"])
Fehlermeldungen und Limits
Bei Fehlern antwortet die API mit einem passenden HTTP-Status und einer kurzen Erklärung, zum Beispiel {"error": "tage muss eine ganze Zahl von 0 bis 365 sein"}.
| Status | Bedeutung |
|---|---|
400 | Ein Parameter ist ungültig, unbekannt oder doppelt angegeben. |
401 | Der API-Schlüssel fehlt oder ist ungültig bzw. gesperrt. |
403 | Der Schlüssel gilt nicht für die angefragte Vereinsnummer, oder die Anfrage kam nicht per GET. |
404 | Die Adresse gibt es nicht. |
405 | Nur Abrufe per GET sind erlaubt. |
429 | Zu viele Anfragen, siehe Limits. Bitte kurz warten. |
500, 502 | Störung bei uns. Bitte später erneut versuchen und bei längeren Ausfällen Bescheid geben. |
Limits
- Höchstens 60 Abrufe pro Minute und Schlüssel.
- Pro Absender höchstens 2 Anfragen pro Sekunde (kurze Spitzen bis 20 sind erlaubt) und 10 gleichzeitige Verbindungen.
- Nach 20 Anfragen mit fehlendem oder falschem Schlüssel innerhalb von 10 Minuten wird der Absender für den Rest dieser 10 Minuten gesperrt.
Ob die API erreichbar ist und wann zuletzt abgeglichen wurde, zeigt ohne Schlüssel: /spielplan-api/v1/health
Aktualität und Grenzen
- Spiele der nächsten rund 5 Wochen werden täglich von 7 bis 23 Uhr alle 30 Minuten mit TeamSL abgeglichen. Änderungen erscheinen dort also mit bis zu 30 Minuten Verzögerung, nachts erst am Morgen.
- Alle späteren Spiele bis Saisonende werden einmal pro Nacht abgeglichen. Für sie können Termine und Ansetzungen bis zu einen Tag alt sein.
- Maßgeblich ist immer TeamSL.
- Die API liefert Spiele bis höchstens 365 Tage im Voraus, praktisch also bis Saisonende. Vergangene Spiele der laufenden Saison gibt es mit
zurueck, jeweils mit Ergebnis. Ergebnisse der letzten 30 Tage werden tagsüber alle 30 Minuten übernommen, ältere nachts. - Spiele, die TeamSL noch nicht im Spielplan führt (z. B. spätere Pokal- oder Endrunden), erscheinen erst, sobald sie dort angelegt sind.
- Verlegte Spiele erscheinen am neuen Termin, gestrichene Spiele verschwinden beim nächsten Abgleich.
- Häufigeres Abfragen als alle 30 Minuten bringt keine neueren Daten.
Datenschutz
Die API gibt keine personenbezogenen Daten von Schiedsrichter:innen heraus: keine Namen, keine Kontaktdaten, keine Lizenznummern und keine Adressen. Zu jeder Position liefert sie nur den Status, den zuständigen Verein und die Entfernung zur Halle in Kilometern.
Wer angesetzt ist, sehen berechtigte Vereinsvertreter:innen wie bisher direkt in TeamSL.
