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.
https://www.bahn.de/web/api
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.
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 |
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"]
}
]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
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 1meldungen[].type == "HALT_AUSFALL"means the departure is cancelled- Delay in minutes =
(ezZeit - zeit)parsed as timestamps
Same as departures but for arrivals.
GET /reiseloesung/ankuenfte
Parameters and response format are identical to Departures.
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 departureankunftsZeitpunkt/ezAnkunftsZeitpunkt= scheduled / real-time arrivaladminIDmaps to a train operator (e.g.80____= DB Fernverkehr)priorisierteMeldungen[].type == "HALT_AUSFALL"= stop is cancelledrisMeldungen[].key == "text.realtime.stop.cancelled"= stop is cancelled- Coordinates are in the
idfield:@X=<lon*1e6>@Y=<lat*1e6> himMeldungenare service alerts;ueberschriftis the title,textis the body
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 constructionTypeencodes class:A= 1st,B= 2nd,AB= mixed,Dprefix = double-deckerwagonIdentificationNumberandtransport.numbermay 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
{
"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.
{
"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 |
{
"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 |
{
"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) |
{
"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) |
| 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 |
| 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 |
| 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 |
{ "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 |
| 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.
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:00suffix in responses)
The formation endpoint requires UTC times:
date:YYYY-MM-DDtime:YYYY-MM-DDT15:04:05.000Z
- 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 asx-correlation-id.