# Deutsche Bahn Train Tickets and Connections Scraper (`muhammadafzal/deutsche-bahn-train-tickets-connections-scraper`) Actor

Scrape Deutsche Bahn train connections, departure and arrival times, changes, operators, current prices, ticket offers, delays, platforms, and service alerts.

- **URL**: https://apify.com/muhammadafzal/deutsche-bahn-train-tickets-connections-scraper.md
- **Developed by:** [Muhammad Afzal](https://apify.com/muhammadafzal) (community)
- **Categories:** Travel, Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 train connections

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Deutsche Bahn Train Tickets & Connections Scraper

Search Deutsche Bahn passenger connections and export structured schedules, current starting prices, ticket offers, train products, operators, delays, platforms, changes, and service notices. The Actor resolves human-readable station names, queries Deutsche Bahn’s first-party journey interfaces, and saves one validated dataset row per connection.

This is a read-only research and automation tool. It does not reserve seats, book journeys, purchase tickets, manage a Bahn account, or bypass authentication. Availability and prices can change after collection; confirm important journey details on the official Deutsche Bahn website before travel.

### What the Actor collects

Each result can contain:

- resolved origin and destination names and Deutsche Bahn station IDs;
- actual and planned departure and arrival timestamps;
- duration, number of changes, and direct/cancelled flags;
- current journey-level starting price and currency when supplied;
- refreshed ticket offer names and prices when requested and available;
- service products such as ICE, IC, EC, RE, RB, S-Bahn, U-Bahn, tram, or bus;
- operating companies;
- individual journey legs with platforms, delays, direction, walking distance, and load factor;
- traffic notices and other source remarks;
- the search parameters, source profile, source URL, and collection timestamp.

The Actor never exposes Deutsche Bahn’s opaque refresh token in dataset results. It hashes that token into a short connection identifier, which is useful for deduplication without leaking the source session value.

### Input

Set `origin` and `destination` to station or stop names. `Berlin Hbf` and `München Hbf` are the default example. Leave `travelDate` empty to search tomorrow in the `Europe/Berlin` time zone; this makes saved Actor Tasks evergreen. Set `travelTime` in `HH:MM` form and choose whether it means departure-at-or-after or arrive-by.

Passenger inputs support one to five adults of a shared age, first or second class, and a common BahnCard 25, 50, or 100 discount. `directOnly` limits results to connections without public-transport changes. `includeLocalTransport` controls S-Bahn, U-Bahn, tram, bus, and ferry legs. `bike` requests bicycle-compatible itineraries.

When `includeTicketOffers` is enabled, the Actor refreshes every selected journey once to obtain fare names and ticket-level prices. This produces richer results but makes one extra source request per connection. Disable it when schedules and journey-level starting prices are sufficient.

`maxResults` is deliberately capped at 20. It is both the result limit and a predictable cost boundary. A German residential proxy is configured by default because Deutsche Bahn’s journey endpoints commonly reject datacenter traffic. Keep one sticky proxy session throughout a run so location lookup, journey search, and fare refreshes share coherent network identity.

Example input:

```json
{
  "origin": "Berlin Hbf",
  "destination": "München Hbf",
  "travelTime": "08:00",
  "searchBy": "departure",
  "adults": 1,
  "adultAge": 30,
  "travelClass": "second",
  "bahnCard": "none",
  "directOnly": false,
  "includeLocalTransport": true,
  "includeTicketOffers": true,
  "language": "en",
  "maxResults": 5
}
```

### Output

Results are stored in the default dataset. The `connections` view puts the most useful columns first: departure, arrival, duration, stations, changes, price, services, operators, cancellation status, and ticket offers. Nested `legs` retain the route details for downstream analysis.

The default key-value store record named `OUTPUT` contains a terminal summary. Its `outcome` is one of:

- `DATA`: one or more validated connections were delivered;
- `EMPTY`: the query completed but returned no matching connections;
- `REJECTED`: the input was invalid, such as identical stations or a past date;
- `BLOCKED`: Deutsche Bahn rejected both bounded source-profile attempts;
- `FAILED`: an unexpected Actor failure occurred.

`OUTPUT` also records source-request counts, resolved stations, warnings, the source profile used, result charges, and whether a maximum charge limit stopped the run. Use this record to distinguish an authentic empty timetable from an access failure.

### Pricing and cost controls

The pay-per-event model charges `$0.005` once when a run starts and `$0.003` for each connection written to the dataset. Invalid, blocked, and empty searches do not generate connection charges. The Actor displays the maximum event-charge estimate at startup. With `maxResults: 5`, that estimate is `$0.020`; Apify platform usage and residential proxy traffic may be billed separately according to your account plan.

Use a low `maxResults` while integrating. Add an Apify `maxTotalChargeUsd` run limit when you need a hard account-side ceiling. Result charging happens only after a record passes normalization and is written successfully.

### Reliability behavior

The Actor begins with the web journey profile used by bahn.de and falls back across Deutsche Bahn’s Navigator/mobile profiles. When Akamai rejects those inexpensive HTTP paths, it starts a displayed Camoufox/Firefox browser on a fresh sticky German residential IP, warms the normal Deutsche Bahn homepage, keeps cookies, locale, fingerprint, and IP coherent, and makes the page-owned request from that browser session. All routes remain first-party Deutsche Bahn data paths. Pagination is bounded to four pages, every result is deduplicated, and each ticket refresh failure is isolated so schedule records can still be returned with a warning.

Source websites evolve. A sudden `BLOCKED` result is not equivalent to “no trains”; retry with the default German residential proxy and a fresh session. A genuine zero-result search returns `EMPTY` and succeeds. Avoid high-frequency polling, overlapping runs, or unnecessarily large searches.

### Responsible use

Deutsche Bahn’s interfaces and data are governed by Deutsche Bahn’s terms, applicable database rights, and local law. Before sustained or commercial use, obtain any permission required for your intended volume and downstream use. Do not use the Actor to overload services, reproduce a competing timetable database, evade access controls, or mislead travelers. Store only what you need and respect notices from the source operator.

This project uses the independent ISC-licensed `db-vendo-client`; see `THIRD_PARTY_NOTICES.md`. The Actor is not endorsed by or affiliated with Deutsche Bahn AG. Deutsche Bahn product names are used descriptively to identify the data source.

### Local development

The Actor requires Node.js 20 or newer.

```bash
npm install
npm test
npm run validate:input
npm run preflight
apify call
```

Local direct requests may be blocked even when the code is correct. The meaningful integration check is a private Apify cloud run with the default residential proxy. Unit tests cover input constraints, Europe/Berlin daylight-saving conversion, journey normalization, ticket cents-to-currency conversion, and refresh-token redaction.

# Actor input Schema

## `origin` (type: `string`):

Station or stop name, such as Berlin Hbf, München Hbf, or Frankfurt(Main)Hbf.

## `destination` (type: `string`):

Destination station or stop name.

## `travelDate` (type: `string`):

YYYY-MM-DD in Europe/Berlin time. Leave empty to search tomorrow, keeping saved Tasks evergreen.

## `travelTime` (type: `string`):

Departure or arrival time in 24-hour HH:MM format, Germany local time.

## `searchBy` (type: `string`):

Interpret the requested date and time as the earliest departure or latest arrival.

## `adults` (type: `integer`):

Adults used to request current ticket prices.

## `adultAge` (type: `integer`):

Age applied to each adult passenger for fare calculation.

## `travelClass` (type: `string`):

Request prices for second class or first class.

## `bahnCard` (type: `string`):

Apply the same Deutsche Bahn BahnCard discount to every adult.

## `directOnly` (type: `boolean`):

Return journeys with no public-transport changes.

## `includeLocalTransport` (type: `boolean`):

Include S-Bahn, U-Bahn, tram, and bus legs in connection searches.

## `bike` (type: `boolean`):

Ask Deutsche Bahn for connections that permit bicycles.

## `includeTicketOffers` (type: `boolean`):

Refresh each returned journey to collect ticket names and current prices when Deutsche Bahn provides them.

## `language` (type: `string`):

Language requested for service notices and fare details.

## `maxResults` (type: `integer`):

Maximum dataset records. Each delivered connection is one billable result event.

## `proxyConfiguration` (type: `object`):

A German residential proxy is strongly recommended because Deutsche Bahn protects its journey endpoints.

## Actor input object example

```json
{
  "origin": "Berlin Hbf",
  "destination": "München Hbf",
  "travelTime": "08:00",
  "searchBy": "departure",
  "adults": 1,
  "adultAge": 30,
  "travelClass": "second",
  "bahnCard": "none",
  "directOnly": false,
  "includeLocalTransport": true,
  "bike": false,
  "includeTicketOffers": true,
  "language": "en",
  "maxResults": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "DE"
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "origin": "Berlin Hbf",
    "destination": "München Hbf"
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/deutsche-bahn-train-tickets-connections-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "origin": "Berlin Hbf",
    "destination": "München Hbf",
}

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/deutsche-bahn-train-tickets-connections-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "origin": "Berlin Hbf",
  "destination": "München Hbf"
}' |
apify call muhammadafzal/deutsche-bahn-train-tickets-connections-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,muhammadafzal/deutsche-bahn-train-tickets-connections-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/R7KXtBgcdhRqnxYfK/builds/dfkK6zDRQVpoa1lNE/openapi.json
