National Rail Train Ticket & Connection Scraper avatar

National Rail Train Ticket & Connection Scraper

Pricing

from $0.40 / 1,000 trips

Go to Apify Store
National Rail Train Ticket & Connection Scraper

National Rail Train Ticket & Connection Scraper

Scrape live train connections, schedules, ticket prices, transfers, delays, and fares across the UK. Extract structured UK timetable data for travel apps, price monitoring, analytics, and AI agents.

Pricing

from $0.40 / 1,000 trips

Rating

5.0

(3)

Developer

Jindřich Bär

Jindřich Bär

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

0

Monthly active users

a day ago

Last modified

Share

National Rail (UK) train tickets & connections scraper

Search National Rail train connections and ticket prices across England, Scotland, and Wales, and get back a structured dataset of routes, times, changes, and fares — ready to plug into spreadsheets, databases, dashboards, or AI agents.

Pulls live data from the official National Rail journey planner, so you always get the same trains, prices, and schedules a passenger would see on nationalrail.co.uk or in the National Rail app.

What you can do with it

  • Compare ticket prices between any two UK stations in real time — London to Edinburgh, Manchester to Birmingham, Cardiff to Bristol, and every other route the National Rail timetable covers (LNER, Avanti West Coast, GWR, CrossCountry, ScotRail, Northern, and all other TOCs).
  • Get every fare, not just the headline price — Advance, Off-Peak, Anytime, Standard and First Class are all returned with their ticket type and route restriction, so you can see exactly which ticket produced the cheapest result.
  • Find direct trains or allow changes, with an optional extra buffer at every interchange.
  • Search by arrival time — "get me there by 09:00" — not just by departure.
  • Monitor fares over time by scheduling the actor to run daily / hourly and writing the results to your own datastore. Advance fares move a lot as departure approaches.
  • Build a travel-planning assistant — feed the JSON output directly into an LLM agent that answers "what's the cheapest train from London to Manchester next Tuesday morning."
  • Track delays and journey duration — every leg includes scheduled times, real-time times, platforms, and the current delay in minutes.

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

Input

FieldRequiredDefaultDescription
fromyesOrigin station name or CRS code (e.g. "London Kings Cross", "KGX", or a group like "London")
toyesDestination station name or CRS code
datenotodayTravel date (date picker, ISO YYYY-MM-DD), UK local time
timenonowTravel time (HH:MM, 24-hour), UK local time
timeTypeno"DEPART""DEPART" treats the time as a departure time, "ARRIVE" as a required arrival time
adultsno1Adult passengers (16+) used for fare pricing
childrenno0Child passengers (5–15) used for fare pricing
fareClassno"ANY""ANY", "STANDARD", or "FIRST" — which ticket classes to price
directTrainsnofalseOnly return journeys with no change of train
reducedTransferTimenofalseAllow tighter connections than the default minimum interchange time
increasedInterchangeno"ZERO"Extra buffer at every change: "ZERO", "THIRTY_MINUTES", or "SIXTY_MINUTES"
onlySearchForSleepernofalseRestrict to overnight sleeper services (Caledonian Sleeper, Night Riviera)
overtakenTrainsnotrueInclude slower services that a later, faster train overtakes
useAlternativeServicesnofalseLet the planner include alternative (replacement or diverted) services
includeAllFaresnofalseStore every fare offered in fares[], not just the cheapest ones
maxResultsno20Maximum number of journeys to push to the dataset

Station names are matched against the official National Rail station list, which is bundled with the actor and refreshed on every build — so no lookup request is made at run time. You can pass a full name ("Manchester Piccadilly"), a partial name ("Kings Cross"), a CRS code ("MAN"), or a group name ("London", "Birmingham", "Glasgow"), which searches all stations in that city. The actor paginates the journey planner automatically — it returns about five journeys per request — so you reliably get the number of connections you asked for.

Example input

{
"from": "London Kings Cross",
"to": "Edinburgh",
"date": "2026-09-15",
"time": "08:00",
"adults": 1,
"fareClass": "ANY",
"maxResults": 20
}

Output

Each item in the dataset describes one train journey, with its legs and the cheapest fares found for it.

Example item

{
"id": "bb796bda-2026-09-15T08:03:00+01:00",
"from": "London Kings Cross",
"to": "Edinburgh",
"fromCrs": "KGX",
"toCrs": "EDB",
"departure": "2026-09-15T08:03:00+01:00",
"arrival": "2026-09-15T12:30:00+01:00",
"realtimeDeparture": null,
"realtimeArrival": null,
"durationMinutes": 267,
"durationText": "4h 27m",
"transfers": 0,
"status": "NORMAL",
"delayMinutes": 0,
"isOvertaken": false,
"operators": ["London North Eastern Railway"],
"priceAmount": 63,
"priceCurrency": "GBP",
"cheapestFare": {
"priceAmount": 63,
"priceCurrency": "GBP",
"fareClass": "STANDARD",
"fareCategory": "ADVANCE",
"typeCode": "BSU",
"typeDescription": "Advance Single",
"ticketRestriction": "GC",
"routeCode": "00430",
"from": "London Kings Cross",
"to": "Edinburgh",
"startLegId": 1,
"endLegId": 1
},
"cheapestStandardFare": { "priceAmount": 63, "fareClass": "STANDARD", "typeDescription": "Advance Single" },
"cheapestFirstFare": { "priceAmount": 131.7, "fareClass": "FIRST", "typeDescription": "Advance Single (1st Class)" },
"legs": [
{
"legId": 1,
"mode": "TRAIN",
"operator": "London North Eastern Railway",
"operatorCode": "GR",
"trainUid": "G80205",
"trainTripId": "G80205|15/09/2026",
"from": "London Kings Cross",
"to": "Edinburgh",
"fromCrs": "KGX",
"toCrs": "EDB",
"departure": "2026-09-15T08:03:00+01:00",
"arrival": "2026-09-15T12:30:00+01:00",
"realtimeDeparture": null,
"realtimeArrival": null,
"delayMinutes": 0,
"durationMinutes": 267,
"fromPlatform": "4",
"toPlatform": "6",
"status": "NORMAL",
"isReplacementBus": false,
"cancelled": false
}
]
}

Field reference

FieldTypeDescription
idstringStable identifier for the journey (deduplication-safe across pages and runs)
from / tostringStation names
fromCrs / toCrsstringCRS codes (KGX, EDB, …) — the standard three-letter UK station codes
departure / arrivalISO 8601 with timezone offsetScheduled times, in UK local time (+01:00 in summer, +00:00 in winter)
realtimeDeparture / realtimeArrivalISO 8601 or nullReal-time times, published only close to departure
durationMinutesintegerTotal travel time
durationTextstringDuration as National Rail formats it (e.g. "4h 27m")
transfersintegerNumber of changes (0 = direct)
statusstringJourney status ("NORMAL", "CANCELLED", …)
delayMinutesinteger or nullCurrent delay
isOvertakenbooleanTrue when a later, faster service overtakes this journey
operatorsstring[]Train operating companies serving the journey
priceAmountnumber or nullCheapest fare of any class, in GBP. Null when no fare is offered
priceCurrencystring or nullAlways "GBP" when a price is present
cheapestFareobject or nullThe fare behind priceAmount, with its class, category, ticket type, and route restriction
cheapestStandardFare / cheapestFirstFareobject or nullCheapest fare in each class, so you can compare without re-querying
fares[]arrayEvery fare offered, cheapest first. Only present when includeAllFares is enabled
legs[]arrayPer-leg breakdown: operator, train ID, board/alight stations and CRS codes, platforms, scheduled and real-time times, delay, cancellation flag

Prices are converted from the pence the API returns into pounds, so 6300 is reported as 63. priceAmount is null when National Rail doesn't offer a bookable through-fare for the journey — this happens on some connections involving replacement buses or partner-operator-only tickets. The journey is still returned with full timing and leg detail; only the price is missing.

Pricing

Pay-per-event — one search-result event is charged for each journey pushed to the dataset. That means a query with maxResults: 10 charges for at most ten events, regardless of how many journey planner calls 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~national-rail-ticket-scraper/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"from": "London Kings Cross", "to": "Edinburgh", "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~national-rail-ticket-scraper/runs?token=<APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"from": "London Kings Cross", "to": "Edinburgh"}'
# 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 how Advance fares on a fixed London → Edinburgh route move as the departure date approaches.
  • Hourly refresh during a peak week — keep a live price board for a route over a bank holiday or a major event.
  • Weekly market scan — compare a basket of intercity routes (London↔Manchester, London↔Leeds, Birmingham↔Glasgow) 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 National Rail 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 London to Edinburgh next Tuesday morning?"
  • "Find me a direct train from Manchester Piccadilly to London Euston on 2026-09-15."
  • "I need to be in Birmingham by 09:00 from Bristol — what should I catch?"
  • "List the next five trains from Leeds to York under £15."
  • "How much more is First Class on the 08:03 from Kings Cross to Edinburgh?"

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.
  • Fares are pre-summarisedcheapestFare, cheapestStandardFare, and cheapestFirstFare mean the agent doesn't have to reason over dozens of raw ticket types to answer "how much?".
  • Predictable pagination — results come back in chronological order with duplicates removed, so the agent doesn't have to reason about paging.
  • Pay-per-event cost control — an agent that asks for five connections pays for five.

Troubleshooting & support

Most issues come from date/time formatting, station naming, or how far ahead you're searching. Try the fixes below before opening an issue.

Common problems

No results returned (reason: "no-results")

The run's status message spells out what was searched and which of your filters could have excluded everything — read that first. The usual causes:

  • The travel date is in the past, or more than ~120 days ahead. National Rail publishes timetables and fares roughly 12 weeks out; beyond that the planner has nothing to return.
  • The route genuinely has no service at that time (e.g. late-night regional lines, or Sunday engineering work). Try a different time of day, or turn off directTrains.
  • directTrains is enabled on a route with no through service (e.g. Oxford → Oxenholme Lake District), so every journey needs a change. The planner rejects such a search outright.
  • onlySearchForSleeper is enabled on a route with no sleeper service — only the Caledonian Sleeper and Night Riviera qualify.

The run failed (reason: "error")

The actor fails loudly rather than finishing with an empty dataset. The status message and the log carry the reason — an unknown station name (with suggestions), an unreachable journey planner, or an HTTP error from National Rail. Re-running usually fixes transient network and 5xx errors.

The wrong station was picked

  • Station names are resolved to the closest match: exact CRS code first, then exact name, then a name that starts with your query, then one that contains it. "London" resolves to the London group (all London terminals), not to a specific station.
  • If nothing matches the whole query, trailing words are dropped and the search retried — National Rail's catalogue omits suffixes that people use in conversation, so "Edinburgh Waverley" becomes Edinburgh (EDB) and "York Station" becomes York (YRK). This is logged as a warning so you can see which station was actually searched.
  • If you need a specific terminal, pass its CRS code (EUS for Euston, KGX for Kings Cross, PAD for Paddington) or its full name. Codes are unambiguous and always win.

priceAmount is null on some journeys

  • National Rail doesn't offer a bookable through-fare for that connection — common where a leg is a replacement bus, or where tickets are sold only by a partner operator. Timing and leg data are still complete.

Fewer results than maxResults

  • The actor pages forward from the last departure it saw and stops when the planner returns nothing new. On a sparsely served route (small stations, late evening), the timetable simply doesn't have more trains that day. Try an earlier time, or a larger nearby hub as the origin.

Invalid date or Invalid time error

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

Different prices than nationalrail.co.uk shows

  • The actor returns the cheapest fare available for that specific journey at the moment it runs. Advance fare quotas sell out continuously, so a price can change within minutes.
  • Railcard discounts are not applied — all fares are quoted undiscounted. Split-ticketing and operator-specific promotions aren't applied either.

Delays show as null

  • Real-time data is only published a few hours before departure. For journeys further out, realtimeDeparture, realtimeArrival, and per-leg delays stay null until live data appears.

FAQs

Can I search by CRS code instead of station name? Yes — from and to accept both. CRS codes (KGX, MAN, EDB) are matched first and are the most reliable way to target a specific station. The codes are also returned as fromCrs / toCrs.

What are "group" stations? National Rail groups all stations in a city under one code — "London", "Birmingham", "Manchester", "Glasgow". Searching from a group returns the best journey from any station in that city, which is usually what you want for intercity travel.

Can I search for a return journey? Not in one run — each run searches one direction. Run the actor twice (outbound and return) and merge the datasets. Note that fares are quoted as singles, so two Advance Singles is the normal way to price a return anyway.

Does it cover the Tube, DLR, trams, or buses? National Rail services only. Underground and DLR stations appear in the station list but are not useful as journey endpoints. Rail replacement buses do appear as legs, flagged with isReplacementBus: true.

Can I apply a railcard discount? No — fares are returned undiscounted. Apply your railcard's percentage yourself if you need an estimate (most give 1/3 off, subject to minimum fares).

Can I get seat availability or book a ticket? No — the actor returns timetable and fare data only. Booking requires a retailer's 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, e.g. console.apify.com/actors/runs/<RUN_ID>), 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.