ÖBB Train Ticket and Connection Scraper
Pricing
from $1.50 / 1,000 trips
Ö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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
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
directoption, 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
| Field | Required | Default | Description |
|---|---|---|---|
from | yes | — | Departure station (e.g. "Wien Hbf", "Salzburg Hbf", "Praha hl.n.") |
to | yes | — | Destination station |
date | no | today | Departure date (date picker, ISO YYYY-MM-DD) |
time | no | now | Departure time (HH:MM, 24-hour, Vienna local time) |
adults | no | 1 | Number of adult passengers (1–6) |
direct | no | false | Only return direct connections (no transfers) |
regionalTrainsOnly | no | false | Restrict results to regional trains (exclude long-distance RJ/EC/NJ) |
bikeCarriage | no | false | Only return journeys where bicycle carriage is available |
wheelchair | no | false | Only return wheelchair-accessible journeys |
sortBy | no | DEPARTURE | Order results by DEPARTURE, ARRIVAL, or DURATION |
maxResults | no | 20 | Maximum 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
| Field | Type | Description |
|---|---|---|
id | string | Stable identifier for the journey |
from / to | string | Station names |
fromId / toId | string | ÖBB station IDs |
departure / arrival | ISO 8601 datetime | Scheduled time in Vienna local time |
durationMinutes | integer | Total travel time |
transfers | integer | Number of transfers (0 = direct) |
priceAmount | number or null | Cheapest 2nd-class fare in priceCurrency. Null when no bookable offer is available. May cover only part of the journey — see priceCoversFullJourney |
priceCurrency | string or null | ISO 4217 ("EUR") |
priceCoversFullJourney | boolean | true when priceAmount covers the whole origin → destination journey; false for a partial fare (or when there's no price) |
priceCoverage | array or null | When 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[] | array | Per-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 runcurl -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 datasetcurl "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 output —
departure,arrival,durationMinutes,transfers,priceAmountare 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'sfalsethe price covers just the segment listed inpriceCoverage(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
datemust beYYYY-MM-DD(e.g.2026-06-15).timemust beHH:MMin 24-hour format (e.g.08:00, not8 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
departureDelayMinutesandarrivalDelayMinutesare alwaysnull.
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:
- Deutsche Bahn ticket scraper — German and European rail timetable and ticket prices.
- České dráhy connection scraper — Czech rail timetable and ticket prices.
- Flixbus connection scraper — long-distance bus routes across Europe and the US.
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.