Developer
Menü

Grundlagen

Adresse und Version

Alle Endpunkte liegen unter https://developer.bahnhoftafel.de/v1. Die Version steht im Pfad. Innerhalb von v1 kommen höchstens neue Felder und Endpunkte dazu; nichts wird umbenannt oder entfernt. Lies also nur die Felder, die du brauchst, und rechne damit, dass es mehr werden.

Anmeldung

Jede Anfrage schickt den Schlüssel deiner App im Header Authorization:

Authorization: Bearer bt_abcdefghij_…

Der Teil zwischen den Unterstrichen ist die öffentliche ID deiner App, der Rest das Geheimnis. Wir speichern nur einen Prüfwert davon; wenn du den Schlüssel verlierst, erzeugst du im Portal einen neuen. Der alte gilt dann sofort nicht mehr.

Wichtig

Der Schlüssel gehört auf deinen Server. Nicht in eine Webseite, nicht in eine App, die andere herunterladen, nicht in ein öffentliches Repo. Die API schickt deshalb auch keine CORS-Header: Aufrufe direkt aus dem Browser gehen nicht.

Wer mehr Schutz will, signiert seine Anfragen zusätzlich, siehe Signierte Anfragen. Nur GET /v1/status geht ohne Schlüssel.

Formate

AntwortenJSON in UTF-8, Bilder als PNG, Live-Ströme als Server-Sent Events.
ZeitenISO 8601 mit Zeitzone, z. B. 2026-10-15T18:18:00+02:00. Verspätungen in ganzen Minuten.
BahnhöfeEVA-Nummer als Text, z. B. "8011160" für Berlin Hbf.
Fahrtentrip_id ist undurchsichtig. Nimm sie, wie sie kommt; ihr Aufbau kann sich ändern.
FelderEnglisch in snake_case, damit sie zu üblichen Werkzeugen passen. Die Doku ist deutsch.

In der Referenz heißt feld?, dass ein Feld fehlen kann, und ?typ, dass es null sein kann.

Fehler

Bei einem Fehler kommt ein passender HTTP-Status und immer derselbe Aufbau: ein fester code zum Auswerten, eine message für Menschen und je nach Fehler weitere Felder.

{
  "code": "invalid_request",
  "message": "Ungültige Anfrage",
  "errors": {
    "duration": "höchstens 180",
    "foo": "unbekannter Parameter"
  }
}

Unbekannte Parameter sind ein Fehler. So fallen Tippfehler sofort auf, statt dass ein Filter still nicht greift.

codeStatusBedeutung
invalid_request400Parameter fehlen, haben den falschen Typ oder liegen außerhalb des Bereichs. errors nennt die Felder.
unauthorized401Kein oder ein ungültiger API-Schlüssel.
invalid_signature401Signatur fehlt, ist falsch, zu alt oder wurde schon einmal benutzt.
forbidden403Der Schlüssel darf diesen Endpunkt nicht nutzen (Bereich fehlt oder App gesperrt).
not_found404Diesen Endpunkt gibt es nicht.
station_not_found404Bahnhof unbekannt.
trip_not_found404Fahrt unbekannt oder nicht mehr verfügbar.
formation_not_found404Für diesen Zug liegt keine Wagenreihung vor.
method_not_allowed405Methode für diesen Pfad nicht erlaubt.
payload_too_large413Anfrage zu groß.
rate_limited429Zu viele Anfragen. retry_after sagt, wie lange zu warten ist.
temporarily_blocked429Nach sehr vielen fehlerhaften Anfragen ist der Schlüssel kurz gesperrt.
upstream_unavailable502Die Datenquelle antwortet gerade nicht. Später erneut versuchen.
internal_error500Unerwarteter Fehler auf unserer Seite.

Wie aktuell die Daten sind

Wir fragen die Quellen je Bahnhof selbst in kurzen Abständen, und alle Apps teilen sich diese Abrufe. Öfter fragen bringt also nichts Neueres. Wer ständig aktuelle Abfahrten braucht, nimmt den Live-Strom.