ÖBB Train Ticket and Connection Scraper avatar

ÖBB Train Ticket and Connection Scraper

Pricing

from $1.50 / 1,000 trips

Go to Apify Store
ÖBB Train Ticket and Connection Scraper

ÖBB Train Ticket and Connection Scraper

Scrape live Austrian Federal Railways train connections, schedules, ticket prices, transfers, delays, and fares across Austria and Europe. Extract structured ÖBB timetable data for travel apps, price monitoring, analytics, and AI agents.

Pricing

from $1.50 / 1,000 trips

Rating

5.0

(1)

Developer

Jindřich Bär

Jindřich Bär

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

ÖBB train tickets & connections scraper

Search ÖBB (Austrian Railways) train connections and ticket prices across Austria and neighbouring countries, and get back a structured dataset of routes, times, transfers, and fares — ready to plug into spreadsheets, databases, dashboards, or AI agents.

Pulls live data from the official ÖBB ticket shop (shop.oebbtickets.at), so you always get the same trains, prices, and schedules a passenger would see when booking online.

What you can do with it

  • Compare ticket prices between stations in real time — Wien to Salzburg, Graz to Innsbruck, Linz to Wien, and any other route the ÖBB timetable covers (including cross-border Railjet, EuroCity, and Nightjet services to Prague, Munich, Zürich, Budapest, and beyond).
  • Find direct trains with the direct option, or restrict results to regional trains only.
  • Filter for accessibility and bikes — request only wheelchair-accessible journeys or connections with bicycle carriage.
  • Monitor fares over time by scheduling the actor to run daily / hourly and writing the results to your own datastore.
  • Build a travel-planning assistant — feed the JSON output directly into an LLM agent that answers "what's the cheapest train from Wien to Salzburg on date Z" or "is there a direct Railjet from Wien to Graz tomorrow morning."

Typical use cases: travel comparison sites, price-monitoring tools, business-trip planners, train spotters, journalists working on transport coverage, and AI agents that need a structured rail-data source for Austria and Central Europe.

Input

FieldRequiredDefaultDescription
fromyesDeparture station (e.g. "Wien Hbf", "Salzburg Hbf", "Praha hl.n.")
toyesDestination station
datenotodayDeparture date (date picker, ISO YYYY-MM-DD)
timenonowDeparture time (HH:MM, 24-hour, Vienna local time)
adultsno1Number of adult passengers (1–6)
directnofalseOnly return direct connections (no transfers)
regionalTrainsOnlynofalseRestrict results to regional trains (exclude long-distance RJ/EC/NJ)
bikeCarriagenofalseOnly return journeys where bicycle carriage is available
wheelchairnofalseOnly return wheelchair-accessible journeys
sortBynoDEPARTUREOrder results by DEPARTURE, ARRIVAL, or DURATION
maxResultsno20Maximum number of connections to push to the dataset (1–200)

Station names are matched against the official ÖBB station list — local accents, German umlauts, and foreign-station names are all supported. The actor automatically paginates forward in time, so you reliably get the requested number of connections.

Example input

{
"from": "Wien Hbf",
"to": "Salzburg Hbf",
"date": "2026-06-15",
"time": "08:00",
"adults": 1,
"direct": true,
"sortBy": "DEPARTURE",
"maxResults": 25
}

Output

Each item in the dataset describes one train connection.

Example item

{
"id": "ca8d20e4b00038a3f8584c5626018b8f…",
"from": "Wien Hbf",
"to": "Salzburg Hbf",
"fromId": "1290401",
"toId": "8100002",
"departure": "2026-06-15T08:30:00.000",
"arrival": "2026-06-15T11:22:00.000",
"durationMinutes": 172,
"transfers": 0,
"priceAmount": 32.4,
"priceCurrency": "EUR",
"priceCoversFullJourney": true,
"priceCoverage": null,
"legs": [
{
"tripId": null,
"line": "RJX 162",
"direction": "Bregenz",
"from": "Wien Hbf",
"to": "Salzburg Hbf",
"fromId": "1290401",
"toId": "8100002",
"departure": "2026-06-15T08:30:00.000",
"arrival": "2026-06-15T11:22:00.000",
"departurePlatform": "11",
"arrivalPlatform": "5",
"departureDelayMinutes": null,
"arrivalDelayMinutes": null,
"durationMinutes": 172,
"walking": false
}
]
}

Field reference

FieldTypeDescription
idstringStable identifier for the journey
from / tostringStation names
fromId / toIdstringÖBB station IDs
departure / arrivalISO 8601 datetimeScheduled time in Vienna local time
durationMinutesintegerTotal travel time
transfersintegerNumber of transfers (0 = direct)
priceAmountnumber or nullCheapest 2nd-class fare in priceCurrency. Null when no bookable offer is available. May cover only part of the journey — see priceCoversFullJourney
priceCurrencystring or nullISO 4217 ("EUR")
priceCoversFullJourneybooleantrue when priceAmount covers the whole origin → destination journey; false for a partial fare (or when there's no price)
priceCoveragearray or nullWhen the price covers only part of the journey, the station range(s) the fare actually covers (e.g. [{ "from": "Wien Hbf", "to": "Trieste Centrale" }]); null when the price covers the whole journey
legs[]arrayPer-leg breakdown: line name (RJX, RJ, EC, NJ, REX, …), direction, departure/arrival, platforms, walking transfers

priceAmount is null on journeys where ÖBB doesn't sell a through-ticket (e.g. some cross-border or operator-only fares). Walking legs between platforms or stations are included in legs[] with walking: true.

Partial prices on cross-border routes

ÖBB can't always sell a single through-ticket for an entire international journey — some legs are operated by partners (e.g. Italian regional trains, Nightjet sections) that aren't bookable through the ÖBB shop. In those cases the returned priceAmount covers only the portion ÖBB can ticket, not the whole trip.

When this happens, priceCoversFullJourney is false and priceCoverage tells you exactly which segment the price applies to. For example, a Praha → Trieste Centrale search may return:

{
"priceAmount": 109.1,
"priceCurrency": "EUR",
"priceCoversFullJourney": false,
"priceCoverage": [{ "from": "Wien Hbf", "to": "Trieste Centrale" }]
}

Here €109.10 only covers Wien → Trieste; the Praha → Wien leg must be bought separately. Only treat priceAmount as the full-journey fare when priceCoversFullJourney is true.

Pricing

Pay-per-event — one search-result event is charged for each connection pushed to the dataset. That means a query with maxResults: 10 charges for at most ten events, regardless of how many requests the actor makes under the hood.

Using the API

Trigger runs from your own code via the Apify API. With your Apify API token, a POST request runs the actor synchronously and returns the dataset items:

curl -X POST "https://api.apify.com/v2/acts/jindrich.bar~oebb-ticket-scraper/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"from": "Wien Hbf", "to": "Salzburg Hbf", "maxResults": 10}'

Or run asynchronously and poll for status / dataset items:

# Start a run
curl -X POST "https://api.apify.com/v2/acts/jindrich.bar~oebb-ticket-scraper/runs?token=<APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"from": "Wien Hbf", "to": "Salzburg Hbf"}'
# When it's done, read the dataset
curl "https://api.apify.com/v2/datasets/<DATASET_ID>/items?token=<APIFY_TOKEN>"

Official client libraries are available for JavaScript / TypeScript, Python, and via the Apify REST API directly.

Scheduling

Run the actor on a cron schedule from the Schedules tab in the Apify console — daily, hourly, or any custom cron expression. Common patterns:

  • Daily price snapshot at 09:00 — track fare changes for a fixed Wien → Salzburg route and chart the trend over weeks.
  • Hourly refresh for a launch week — keep a real-time price board for a route during a promotional campaign.
  • Weekly market scan — compare a basket of intercity routes (Wien↔Graz, Wien↔Innsbruck, Salzburg↔Linz) for week-over-week price changes.

Schedules can fan out into multiple datasets, push to a webhook, or trigger downstream actors when the run finishes.

Use with AI Agents (Apify MCP)

This actor is exposed through the Apify Model Context Protocol (MCP) server, so any AI agent that speaks MCP — Claude, ChatGPT custom agents, OpenAI Agents SDK, Cursor, etc. — can call it directly to fetch live ÖBB ticket prices and connection options.

Once the Apify MCP server is connected, the agent picks up the actor's input schema automatically. Typical prompts that work out of the box:

  • "What's the cheapest train from Wien to Salzburg tomorrow morning?"
  • "Find me a direct Railjet from Wien Hbf to Graz Hbf on 2026-06-15."
  • "How long does the train from Wien to Innsbruck take, and when does the next one leave?"
  • "List the next five trains from Linz to Wien."
  • "Are there any direct overnight Nightjet trains from Wien to Hamburg in the next two weeks?"

The agent fills the input, runs the actor, and reads the structured dataset items back — no scraping, no HTML parsing, no scheduling logic on the agent side.

Why this works well for agents

  • Typed input schema — every field has a title, type, default, and validation rules, so an agent can call the actor without trial-and-error prompting.
  • Typed outputdeparture, arrival, durationMinutes, transfers, priceAmount are all numbers / ISO timestamps, ready to be diffed, sorted, or compared directly.
  • Predictable pagination — results come back in chronological order with duplicates removed, so the agent doesn't have to reason about paging.

Troubleshooting & support

Most issues come from date/time formatting or sparsely-served routes. Try the fixes below before opening an issue.

Common problems

No results returned

  • The departure date is in the past, or beyond the ÖBB timetable horizon. Pick a date within the next year.
  • The route genuinely has no service on the requested day. Try shifting the date or turning off direct / regionalTrainsOnly.

priceAmount is null on some / all journeys

  • ÖBB doesn't sell a through-ticket for the connection. This is normal for some cross-border legs or operator-only fares. The connection is still returned with full timing and leg details — only the price is missing.

priceAmount looks too low for the route

  • Check priceCoversFullJourney. On cross-border journeys ÖBB often can only ticket part of the trip, so when it's false the price covers just the segment listed in priceCoverage (e.g. Wien → Trieste, not Praha → Trieste). The rest of the journey must be booked separately.

Fewer results than maxResults

  • On sparsely-served routes (small regional stations, late-night cross-border), the timetable simply may not have enough matching trains. Try turning off direct, allowing long-distance trains (regionalTrainsOnly: false), or starting from a larger nearby hub.

Invalid date or Invalid time error

  • date must be YYYY-MM-DD (e.g. 2026-06-15). time must be HH:MM in 24-hour format (e.g. 08:00, not 8 AM). Both fields are optional — leave them empty to use "now".

Delays show as null

  • This actor returns scheduled timetable data; real-time delay forecasts aren't included, so departureDelayMinutes and arrivalDelayMinutes are always null.

Different prices than the ÖBB shop shows in a browser

  • The actor returns the cheapest available 2nd-class fare (Sparschiene / Standard) for the requested journey. Promo codes, Vorteilscard discounts, and group fares aren't applied — those require a logged-in ÖBB account.

FAQs

Can I search by station ID instead of name? Not currently — from and to accept station names only. The actor resolves them to the official ÖBB station IDs and returns those as fromId / toId so you can round-trip them.

Does it cover S-Bahn, trams, or buses? Long-distance and regional rail (Railjet, EuroCity, Nightjet, InterCity, REX, regional) plus operator partners visible in the ÖBB timetable. Urban transit is largely out of scope. For long-distance buses, see the Flixbus actor below.

Does it work for routes that don't touch Austria? Coverage is strongest for journeys that start, end, or pass through Austria, including cross-border routes like Wien–Praha, Wien–München, and Wien–Budapest. For purely-domestic journeys in other countries, use the relevant national actor instead.

Can I get seat availability or book a ticket? No — the actor returns timetable and fare data only. Booking requires ÖBB's authenticated checkout flow.

Support

Open an issue on the actor's Issues tab in the Apify console. Include the full input JSON, the run ID (visible in the run URL), and the expected vs. actual output — that lets the maintainer pull the exact logs and reproduce the issue quickly.

Need a different data source?

If you're scraping connections across multiple operators, check our companion actors:

All these actors emit a comparable schema (from, to, departure, arrival, price, leg-level breakdown), so an aggregator agent can merge their outputs into a single multi-modal travel search.