Skip to content

Instantly share code, notes, and snippets.

@messeb
Created February 26, 2026 18:33
Show Gist options
  • Select an option

  • Save messeb/2ea41faf4e4af18c09c3ab2ccdc616b2 to your computer and use it in GitHub Desktop.

Select an option

Save messeb/2ea41faf4e4af18c09c3ab2ccdc616b2 to your computer and use it in GitHub Desktop.

Deutsche Bahn (bahn.de) API Reference

Reverse-engineered from the bahn.de web frontend. All endpoints are HTTP GET, return JSON, and require browser-like headers to avoid bot detection. No authentication token is needed, but the headers below are essential.


Base URL

https://www.bahn.de/web/api

Required Headers

Every request must include these headers:

Accept:           application/json, text/plain, */*
Accept-Language:  de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control:    no-cache
Pragma:           no-cache
Origin:           https://www.bahn.de
Referer:          https://www.bahn.de/buchung/fahrplan/suche
User-Agent:       <Chrome-like UA string>
Sec-Fetch-Dest:   empty
Sec-Fetch-Mode:   cors
Sec-Fetch-Site:   same-origin
sec-ch-ua:        "Chromium";v="131", "Not?A_Brand";v="24", "Google Chrome";v="131"
sec-ch-ua-mobile: ?0
x-correlation-id: <uuid4>_<uuid4>

Use a realistic Chrome user-agent string. Rotating through several helps avoid blocks.


Transport Modes

Used in departure/arrival queries as verkehrsmittel[] parameter:

Value Description
ICE Intercity-Express
EC_IC EuroCity / InterCity
IR InterRegio
REGIONAL Regional trains (RE/RB)
SBAHN S-Bahn
BUS Bus
SCHIFF Ferry
UBAHN U-Bahn
TRAM Tram
ANRUFPFLICHTIG On-call services

Endpoints

1. Search Locations

Search stations and stops by name.

GET /reiseloesung/orte

Query Parameters

Parameter Type Required Description
suchbegriff string yes Search term (station name)
typ string yes Always ALL
limit int yes Max results (e.g. 10)

Example

GET /reiseloesung/orte?suchbegriff=Frankfurt&typ=ALL&limit=10

Response — array of Location

[
  {
    "extId": "8000105",
    "evaNumber": 8000105,
    "id": "A=1@O=Frankfurt(Main)Hbf@X=8663785@Y=50107149@",
    "name": "Frankfurt(Main)Hbf",
    "lat": 50.107149,
    "lon": 8.663785,
    "type": "ST",
    "products": ["ICE", "EC_IC", "REGIONAL", "SBAHN"]
  }
]

2. Search Nearby Stations

Find stations near a coordinate.

GET /reiseloesung/orte/nearby

Query Parameters

Parameter Type Required Default Description
lat float yes Latitude
long float yes Longitude
radius int no 9999 Search radius in meters
maxNo int no 100 Maximum number of results

Example

GET /reiseloesung/orte/nearby?lat=50.107149&long=8.663785&radius=1000&maxNo=10

Response — array of Location


3. Departures

Get departures from a station.

GET /reiseloesung/abfahrten

Query Parameters

Parameter Type Required Default Description
datum string yes Date in YYYY-MM-DD format
zeit string yes Time in HH:MM:SS format
ortExtId int64 yes Station EVA number
ortId string yes Station ID string (from location search)
mitVias bool yes Include via stations (true)
maxVias int yes Max via stations per entry (e.g. 5)
verkehrsmittel[] string[] no all Transport mode filter (repeatable)

Example

GET /reiseloesung/abfahrten?datum=2026-02-24&zeit=14:00:00&ortExtId=8000105&ortId=A%3D1%40O%3DFrankfurt...&mitVias=true&maxVias=5&verkehrsmittel[]=ICE&verkehrsmittel[]=REGIONAL

Response

{
  "entries": [
    {
      "journeyId": "2|#VN#1#ST#...",
      "bahnhofsId": "8000105",
      "terminus": "München Hbf",
      "gleis": "7",
      "ezGleis": "7A",
      "zeit": "2026-02-24T14:30:00",
      "ezZeit": "2026-02-24T14:32:00",
      "ueber": ["Mannheim Hbf", "Stuttgart Hbf"],
      "verkehrmittel": {
        "kurzText": "ICE",
        "mittelText": "ICE 619",
        "langText": "ICE 619",
        "name": "ICE 619"
      },
      "meldungen": [
        { "type": "DELAY", "text": "Delay due to operational reasons" }
      ]
    }
  ]
}

Field notes

  • zeit = scheduled departure, ezZeit = real-time departure (empty if on time)
  • gleis = scheduled platform, ezGleis = real-time platform (empty if unchanged)
  • ueber[0] is the origin station — skip it, via stations start at index 1
  • meldungen[].type == "HALT_AUSFALL" means the departure is cancelled
  • Delay in minutes = (ezZeit - zeit) parsed as timestamps

4. Arrivals

Same as departures but for arrivals.

GET /reiseloesung/ankuenfte

Parameters and response format are identical to Departures.


5. Journey Detail

Get all stops for a specific journey/trip.

GET /reiseloesung/fahrt

Query Parameters

Parameter Type Required Description
journeyId string yes Journey ID from a departure entry
poly bool yes Include polyline geometry (true or false)

Example

GET /reiseloesung/fahrt?journeyId=2%7C%23VN%23...&poly=false

Response

{
  "reisetag": "2026-02-24",
  "zugName": "ICE 619",
  "cancelled": false,
  "halte": [
    {
      "name": "Frankfurt(Main)Hbf",
      "extId": "8000105",
      "evaNumber": 8000105,
      "id": "A=1@O=Frankfurt(Main)Hbf@X=8663785@Y=50107149@",
      "gleis": "7",
      "ezGleis": "7A",
      "abfahrtsZeitpunkt": "2026-02-24T14:30:00",
      "ezAbfahrtsZeitpunkt": "2026-02-24T14:32:00",
      "ankunftsZeitpunkt": null,
      "ezAnkunftsZeitpunkt": null,
      "adminID": "80____",
      "nummer": "619",
      "kategorie": "ICE",
      "canceled": false,
      "additional": false,
      "priorisierteMeldungen": [],
      "risMeldungen": []
    }
  ],
  "himMeldungen": [],
  "priorisierteMeldungen": []
}

Field notes

  • abfahrtsZeitpunkt / ezAbfahrtsZeitpunkt = scheduled / real-time departure
  • ankunftsZeitpunkt / ezAnkunftsZeitpunkt = scheduled / real-time arrival
  • adminID maps to a train operator (e.g. 80____ = DB Fernverkehr)
  • priorisierteMeldungen[].type == "HALT_AUSFALL" = stop is cancelled
  • risMeldungen[].key == "text.realtime.stop.cancelled" = stop is cancelled
  • Coordinates are in the id field: @X=<lon*1e6>@Y=<lat*1e6>
  • himMeldungen are service alerts; ueberschrift is the title, text is the body

6. Train Formation

Get the carriage composition of a train.

GET /reisebegleitung/wagenreihung/vehicle-sequence

Query Parameters

Parameter Type Required Description
administrationId string yes Operator ID (use 80 for DB Fernverkehr)
category string yes Train type, e.g. ICE
date string yes Departure date in YYYY-MM-DD (UTC)
evaNumber int64 yes Station EVA number
number string yes Train number, e.g. 619
time string yes Departure datetime in YYYY-MM-DDT15:04:05.000Z (UTC)

Example

GET /reisebegleitung/wagenreihung/vehicle-sequence?administrationId=80&category=ICE&date=2026-02-24&evaNumber=8000105&number=619&time=2026-02-24T13:30:00.000Z

Response

{
  "departurePlatform": "7A",
  "departurePlatformSchedule": "7",
  "platform": {
    "start": 0.0,
    "end": 400.0,
    "sectors": [
      { "name": "A", "start": 0.0, "end": 100.0, "cubePosition": 50.0 },
      { "name": "B", "start": 100.0, "end": 200.0, "cubePosition": 150.0 }
    ]
  },
  "groups": [
    {
      "name": "ICE 301",
      "transport": {
        "category": "ICE",
        "number": "301",
        "destination": { "name": "München Hbf" }
      },
      "vehicles": [
        {
          "wagonIdentificationNumber": "11",
          "vehicleID": "93800040800000000",
          "status": "ACTIVE",
          "type": {
            "constructionType": "AB",
            "category": "PASSENGER",
            "hasFirstClass": true,
            "hasEconomyClass": true
          },
          "platformPosition": { "start": 0.0, "end": 26.7, "sector": "A" },
          "amenities": [
            { "type": "AIR_CONDITION" },
            { "type": "SEATS_BAHN_COMFORT" }
          ]
        }
      ]
    }
  ]
}

Field notes

  • Platform positions are in meters; convert to % using (pos - platform.start) / (platform.end - platform.start) * 100
  • constructionType encodes class: A = 1st, B = 2nd, AB = mixed, D prefix = double-decker
  • wagonIdentificationNumber and transport.number may be a string or a number
  • UIC vehicle ID middle 3 digits (index 5–8) encode the model (e.g. 408 = ICE 3neo)
  • Amenity types: AIR_CONDITION, WHEELCHAIR_SPACE, ZONE_FAMILY, ZONE_QUIET, SEATS_BAHN_COMFORT
  • Vehicle category LOCOMOTIVE / POWERCAR / DININGCAR

Data Models

Location

{
  "eva": 8000105,
  "id": "A=1@O=Frankfurt(Main)Hbf@X=8663785@Y=50107149@",
  "name": "Frankfurt(Main)Hbf",
  "lat": 50.107149,
  "lon": 8.663785,
  "type": "ST",
  "products": ["ICE", "EC_IC", "REGIONAL", "SBAHN"]
}
Field Type Description
eva int64 EVA station number (use as ortExtId)
id string Hafas station ID (use as ortId)
name string Human-readable station name
lat float64 Latitude (may be parsed from id if missing)
lon float64 Longitude (may be parsed from id if missing)
type string Location type: ST = station, POI, ADR
products string[] Available transport modes at this location

Coordinate extraction from id: The id string may encode coordinates as @X=<lon×1e6>@Y=<lat×1e6>. Divide by 1,000,000 to get decimal degrees.


Departure / Arrival

{
  "journeyId": "2|#VN#1#ST#...",
  "type": "ICE",
  "line": "ICE 619",
  "train": "ICE 619",
  "trainShort": "ICE",
  "trainMid": "ICE 619",
  "trainLong": "ICE 619",
  "stopEva": "8000105",
  "destination": "München Hbf",
  "platform": "7",
  "rtPlatform": "7A",
  "via": ["Mannheim Hbf", "Stuttgart Hbf"],
  "viaLast": "Stuttgart Hbf",
  "schedDep": "2026-02-24T14:30:00+01:00",
  "rtDep": "2026-02-24T14:32:00+01:00",
  "dep": "2026-02-24T14:32:00+01:00",
  "delay": 2,
  "isCancelled": false,
  "messages": []
}
Field Type Description
journeyId string Opaque journey ID — use for the Journey Detail endpoint
type string Short train type (ICE, RE, S, …)
line string Line designation (e.g. ICE 619, RE 4)
destination string Final destination name
platform string Scheduled platform
rtPlatform string Real-time platform (empty if unchanged)
via string[] Intermediate stops (first entry from API is skipped)
schedDep datetime Scheduled departure (Europe/Berlin)
rtDep datetime Real-time departure (null if on time)
dep datetime Effective departure = rtDep if available, else schedDep
delay int Delay in minutes (rtDep - schedDep)
isCancelled bool True if message type HALT_AUSFALL is present

Journey

{
  "id": "2|#VN#1#ST#...",
  "name": "ICE 619",
  "type": "ICE",
  "tripNo": "619",
  "operator": "DB Fernverkehr",
  "day": "2026-02-24",
  "isCancelled": false,
  "stops": [],
  "messages": []
}
Field Type Description
id string Journey ID (same as used in the request)
name string Full train name (e.g. ICE 619)
type string Train type from most common kategorie in stops
tripNo string Trip/train number
operator string Operator name resolved from adminID
day date Operating day (reisetag)
isCancelled bool True if the entire journey is cancelled
stops Stop[] Ordered list of stops
messages Message[] Service alerts from himMeldungen + priorisierteMeldungen

Stop

{
  "eva": 8000105,
  "name": "Frankfurt(Main)Hbf",
  "lat": 50.107149,
  "lon": 8.663785,
  "platform": "7",
  "rtPlatform": "7A",
  "schedDep": "2026-02-24T14:30:00+01:00",
  "rtDep": "2026-02-24T14:32:00+01:00",
  "dep": "2026-02-24T14:32:00+01:00",
  "schedArr": null,
  "rtArr": null,
  "arr": null,
  "depDelay": 2,
  "arrDelay": 0,
  "delay": 2,
  "isCancelled": false,
  "isAdditional": false
}
Field Type Description
eva int64 EVA station number
name string Station name
lat / lon float64 Coordinates (parsed from id field)
platform string Scheduled platform (gleis)
rtPlatform string Real-time platform (ezGleis)
schedDep datetime Scheduled departure
rtDep datetime Real-time departure (null if on time)
dep datetime Effective departure
schedArr datetime Scheduled arrival
rtArr datetime Real-time arrival (null if on time)
arr datetime Effective arrival
depDelay int Departure delay in minutes
arrDelay int Arrival delay in minutes
isCancelled bool Stop is cancelled
isAdditional bool Stop was added (not in original schedule)

Formation

{
  "platform": "7A",
  "direction": 0,
  "trainType": "ICE",
  "sectors": [],
  "carriages": [],
  "groups": [],
  "destinations": ["München Hbf"],
  "trainNumbers": ["619"]
}
Field Type Description
platform string Real-time departure platform
direction int 0 = normal, 100 = reversed
sectors Sector[] Platform sectors (A, B, C, …)
carriages Carriage[] All carriages sorted by position
groups Group[] Train units (each group is a coupled train set)

Sector

Field Type Description
name string Sector label (A, B, C, …)
startPercent float64 Start position as % of platform length
endPercent float64 End position as % of platform length
startMeters float64 Start in meters from platform start
endMeters float64 End in meters from platform start

Carriage

Field Type Description
number string Wagon number
model string Model code from UIC ID (e.g. 408 = ICE 3neo)
type string Construction type (A, B, AB, DA, DB, …)
classType int 0=unknown, 1=1st class, 2=2nd class, 12=mixed
section string Platform sector this carriage is in
startPercent float64 Start position as % of platform length
endPercent float64 End position as % of platform length
isClosed bool Carriage not in service
isLocomotive bool This is a locomotive
isPowercar bool This is a power car
isDosto bool Double-decker carriage
hasFirstClass bool
hasSecondClass bool
hasBistro bool Dining car / bistro
hasAC bool Air conditioning
hasWheelchairSpace bool
hasFamilyZone bool
hasQuietZone bool
hasBahnComfort bool BahnComfort priority seats

Model codes (UIC ID digits 5–8)

Code Model Series
401 ICE 1 BR 401
402 ICE 2 BR 402
403 ICE 3 BR 403
406 ICE 3 BR 406
407 ICE 3 Velaro BR 407
408 ICE 3neo BR 408
411 ICE T BR 411
412 ICE 4 BR 412
415 ICE T BR 415

Message

{ "type": "DELAY", "text": "Delay due to operational reasons" }
Field Type Description
type string Alert type (e.g. DELAY, HALT_AUSFALL, priority level)
text string Human-readable message

Error Responses

HTTP Status Meaning
400 Invalid request parameters
404 Station / journey not found
5xx Server error

The API returns plain status codes without a structured error body in most cases.


Time Format

All timestamps in request parameters use Europe/Berlin timezone:

  • Date: YYYY-MM-DD
  • Time: HH:MM:SS
  • DateTime: YYYY-MM-DDTHH:MM:SS (may include +01:00 / +02:00 suffix in responses)

The formation endpoint requires UTC times:

  • date: YYYY-MM-DD
  • time: YYYY-MM-DDT15:04:05.000Z

Notes

  • No official API — this is reverse-engineered from the bahn.de web app and may change without notice.
  • Rate limiting — use caching (TTL ~90 s is reasonable for boards).
  • Cookie jar — persist session cookies between requests for better reliability.
  • Correlation ID — send a fresh <uuid4>_<uuid4> per request as x-correlation-id.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment