Skip to main content

Python clients for the Deutsche Bahn APIs (Timetables, StaDa Station Data)

Project description

deutsche-bahn

A Python library for the Deutsche Bahn APIs — currently supporting the Timetables API and the StaDa Station Data API.

Requirements

  • Python 3.13+
  • requests

Installation

uv add deutsche-bahn-py

Or with pip:

pip install deutsche-bahn-py

Setup

  1. Register at developers.deutschebahn.com, create an application, and subscribe to the APIs you need (Timetables and/or StaDa).
  2. Copy your Client ID and API Key.

Timetables API

from deutsche_bahn.timetables import TimetablesClient

client = TimetablesClient(
    client_id="your_client_id",
    api_key="your_api_key",
)

Find a station

stations = client.get_station("Frankfurt")
# [Station(name='Frankfurt am Main - Stadion', eva='8002040'), ...]

eva = stations[0].eva

Stations are identified by their EVA number, a numeric DB station ID.

Planned timetable

plan = client.get_plan(eva)               # current hour
plan = client.get_plan(eva, hour=14)      # specific hour today
plan = client.get_plan(eva, date=datetime(2026, 5, 20), hour=9)

for stop in plan.stops:
    dp = stop.departure
    print(stop.train_line.display_name, dp.planned_time, dp.planned_platform)

Returns one hour of scheduled departures/arrivals. No live data.

Live timetable (plan + changes merged)

live = client.get_timetable_with_changes(eva)

for stop in live.stops:
    dp = stop.departure
    if dp and dp.delay_minutes:
        print(f"{stop.train_line.display_name} is +{dp.delay_minutes} min late")
    if stop.is_cancelled:
        print(f"{stop.train_line.display_name} is cancelled")

This is the most useful method — it fetches the plan and all current deviations, then merges them so each stop reflects reality.

Changes only

# All current deviations for the station (delays, cancellations, platform changes)
changes = client.get_full_changes(eva)

# Only what changed since your last call - use this when polling
recent = client.get_recent_changes(eva)

Data model

TimetableStop

Attribute Type Description
id str Unique stop identifier
train_line TrainLine Train category and number
arrival ArrivalDeparture | None Arrival data
departure ArrivalDeparture | None Departure data
messages list[Message] Delay/disruption messages
is_cancelled bool True if arrival or departure is cancelled

ArrivalDeparture

Attribute Type Description
planned_time datetime | None Scheduled time
changed_time datetime | None Actual/expected time (set when delayed)
planned_platform str Scheduled platform
changed_platform str Actual platform (set when changed)
planned_path list[str] Scheduled route as station names
changed_path list[str] Actual route (set when rerouted)
effective_time datetime | None changed_time if set, else planned_time
effective_platform str changed_platform if set, else planned_platform
delay_minutes int | None Difference in minutes, or None if on time
is_cancelled bool True if status is cancelled

TrainLine

Attribute Type Description
category str Train category (ICE, IC, RE, S, …)
number str Train number
owner str Operator code
display_name str "{category} {number}" (e.g. "ICE 9551")

Station

Attribute Type Description
eva str EVA station number
name str Station name
ds100 str DS100 abbreviation
lat / lon float Coordinates

StaDa Station Data API

from deutsche_bahn.stada import StaDaClient

client = StaDaClient(
    client_id="your_client_id",
    api_key="your_api_key",
)

Search for stations

results = client.get_stations(searchstring="Frankfurt")
# StationQuery(total=12, offset=0, limit=100, result=[...])

for station in results.result:
    print(station.number, station.name, station.category)

Supports wildcards * and ? in the search string.

Filter options

# By federal state
results = client.get_stations(federalstate="bayern")

# By category range or list
results = client.get_stations(category="1-3")
results = client.get_stations(category="1,3,5")

# By EVA number
results = client.get_stations(eva=8000105)

# By Ril100 identifier
results = client.get_stations(ril="FF")

# Combine filters with OR logic
results = client.get_stations(searchstring="Hbf", federalstate="hessen", logicaloperator="or")

# Pagination
results = client.get_stations(offset=100, limit=50)

Fetch a single station

station = client.get_station(1071)  # Bahnhofsnummer
print(station.result[0].name)       # Frankfurt(Main)Hbf

3-S-Zentralen

3-S-Zentralen are the 24/7 operations centres for German railway stations.

# All 3-S-Zentralen
szentralen = client.get_szentralen()

# Single entry by ID
sz = client.get_szentrale(50)
print(sz.result[0].name, sz.result[0].public_phone_number)

Data model

StadaStation

Attribute Type Description
number int Bahnhofsnummer (station ID)
name str Station name
category int Station category (1–7)
price_category int Price category
federal_state str Federal state name
has_wifi bool DB WiFi available
has_db_lounge bool DB Lounge available
has_travel_center bool DB Reisezentrum available
has_locker_system bool Lockers available
has_parking bool Parking available
has_bicycle_parking bool Bicycle parking available
has_taxi_rank bool Taxi rank available
has_stepless_access str "yes", "no", or "partial"
eva_numbers list[EVANumber] Associated EVA numbers
ril100_identifiers list[RiL100Identifier] Ril100 codes
mailing_address Address | None Station mailing address
szentrale SZentrale | None Responsible 3-S-Zentrale

SZentrale

Attribute Type Description
number int Unique ID
name str Name
public_phone_number str Public phone
email str Email address
address Address | None Physical address

Exceptions

All clients share the same exception hierarchy:

Exception When
AuthenticationError Invalid or missing credentials (401/403)
NotFoundError Station or resource not found (404)
RateLimitError Too many requests (429)
DBApiError Any other API or network error
from deutsche_bahn.stada.exceptions import NotFoundError, RateLimitError, DBApiError

try:
    station = client.get_station(9999999)
except NotFoundError:
    print("Station not found")
except RateLimitError:
    print("Rate limit hit")
except DBApiError as e:
    print(f"API error: {e}")

API reference

Data is provided under CC BY 4.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

deutsche_bahn_py-0.2.1.tar.gz (32.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

deutsche_bahn_py-0.2.1-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file deutsche_bahn_py-0.2.1.tar.gz.

File metadata

  • Download URL: deutsche_bahn_py-0.2.1.tar.gz
  • Upload date:
  • Size: 32.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for deutsche_bahn_py-0.2.1.tar.gz
Algorithm Hash digest
SHA256 8fcf7b6ba62fdeeea0657c0b51edc42582053fcbd51c2a2234fb3b85ec4744c2
MD5 e5b60b15e8f5570db93c5525ac68e111
BLAKE2b-256 60ac4ba509a75ed5dbf6b6a6f169e2d88a037477738adadc35d6ebad3e87f8cb

See more details on using hashes here.

File details

Details for the file deutsche_bahn_py-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: deutsche_bahn_py-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 17.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for deutsche_bahn_py-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f7b7c8ea8d1f8b7143beefe68ae57b0acaf78572df72d112471cd784ebf1e4f4
MD5 a8454b733bfaed5686f5a67f1eca8947
BLAKE2b-256 2a47bedf571082d44f6527b9938b7d46ca16092059d8daa54bfe9b3bc3198346

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page