Skip to content
    ↑↓ to choose · Enter to open

    · 14 min read

    Trainline Scraper: Up to 1,000 Free Results a Month (2026)

    By CrawlerBros Engineering Team

    Journey searches return 16 fields per journey record, including departure time, arrival time, operator, duration, and price. When automated access to the live fare engine is challenged, the scraper emits an 18-field journey summary record containing first and last trains, average journey time, frequency, and from-prices. Results cost $5.00 per 1,000 results on the free-plan price, and Apify's free plan includes $5.00 of monthly usage with no credit card. This Actor is built for transport data teams and travel researchers collecting UK and European train schedules and route summaries. It is not for anyone who needs to complete ticket purchases or access private customer booking accounts, which the records do not include.

    Try it before you read further. Apify's free plan includes $5.00 of usage every month with no credit card, enough for up to 1,000 results at $0.005 each before platform usage. Open Trainline Scraper on Apify and run the prefilled example.

    How reliable is Trainline Scraper in production?

    Across the last 30 days of public runs on the Apify platform, Trainline Scraper recorded 77 runs with the following outcomes.

    Outcome Runs Share
    Succeeded 77 100.0%
    Failed 0 0.0%
    Aborted by the user 0 0.0%
    Timed out 0 0.0%
    Total 77 100.0%

    No run failed or timed out in the last 30 days. Keep a retry and an alert on scheduled runs all the same: a clean month is a record, not a guarantee.

    What does it cost to run Trainline Scraper?

    Each result costs $0.005 on Apify's free plan, which is $5.00 per 1,000 results. Starting a run is charged separately at $0.005 per GB of Actor memory. Apify also bills the platform usage each run consumes, at the rates of your Apify plan, on top of these charges.

    Apify plan Per result Per 1,000 results
    FREE $0.005 $5.00
    BRONZE $0.00433 $4.33
    SILVER $0.00367 $3.67
    GOLD $0.003 $3.00
    PLATINUM $0.003 $3.00
    DIAMOND $0.003 $3.00

    Worked example: collecting 10,000 results costs $50.00 in result charges before run-start fees and platform usage. No run failed or timed out in the last 30 days, so the list price is a fair budget; keep a retry in place all the same.

    The maxItems parameter directly bounds your result charges by capping the maximum number of items written to the dataset. For search queries, specifying exact departAfter and arriveBy filters can narrow the output to only relevant trips. To test schema compatibility before running wider batches, run the example input with maxItems set to 10.

    How do you run Trainline Scraper from the API?

    The schema marks 1 of its 13 controls as required: mode. The payload below uses the schema's own prefilled values, so it runs as written once you substitute your API token.

    Call the synchronous endpoint to start a run and receive dataset items in one request:

    curl -X POST "https://api.apify.com/v2/acts/crawlerbros~trainline-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"mode":"search","originStation":"London Euston","destinationStation":"Birmingham New Street","travelDate":"","locale":"en-gb","currency":"GBP","maxItems":10,"proxyConfiguration":{"useApifyProxy":false}}'
    

    The same run from Python, using the official client:

    from apify_client import ApifyClient
    
    client = ApifyClient("<YOUR_APIFY_TOKEN>")
    
    run_input = {
      "mode": "search",
      "originStation": "London Euston",
      "destinationStation": "Birmingham New Street",
      "travelDate": "",
      "locale": "en-gb",
      "currency": "GBP",
      "maxItems": 10,
      "proxyConfiguration": {
        "useApifyProxy": False
      }
    }
    
    run = client.actor("crawlerbros~trainline-scraper").call(run_input=run_input)
    
    for item in client.dataset(run["defaultDatasetId"]).iterate_items():
        print(item)
    

    And from Node.js:

    import { ApifyClient } from 'apify-client'
    
    const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' })
    
    const input = {
      "mode": "search",
      "originStation": "London Euston",
      "destinationStation": "Birmingham New Street",
      "travelDate": "",
      "locale": "en-gb",
      "currency": "GBP",
      "maxItems": 10,
      "proxyConfiguration": {
        "useApifyProxy": false
      }
    }
    
    const run = await client.actor('crawlerbros~trainline-scraper').call(input)
    const { items } = await client.dataset(run.defaultDatasetId).listItems()
    console.log(items)
    

    The synchronous endpoint holds the connection open until the run finishes, which is convenient for small batches and wrong for large ones. For anything long running, start the run asynchronously and poll, or attach a webhook, so a dropped connection does not cost you the results.

    Which Trainline Scraper inputs matter, and which can you skip?

    The mode control dictates whether the Actor runs a journey search, station lookup, or popular route pull. For journey searches, originStation, destinationStation, and travelDate determine the retrieved timetable, while stationQuery is used when mode is set to stationLookup. Optional filters like ticketType and maxPrice should generally be left unset on an initial run until you confirm baseline availability.

    • mode (string): What to fetch. Default: "search".
    • originStation (string): Departure station or city, e.g. London Euston, Paris, Manchester Piccadilly (mode=search). Default: "London Euston".
    • destinationStation (string): Arrival station or city (mode=search). Default: "Birmingham New Street".
    • travelDate (string): Journey date in YYYY-MM-DD (mode=search). Defaults to today + 7 days when empty or in the past. Default: "".
    • locale (string): Site locale used for station search and route pages. Default: "en-gb".
    • currency (string): Currency code passed to the results page, e.g. GBP, EUR. Default: "GBP".
    • maxItems (integer): Hard cap on emitted records. Default: 50.
    • proxyConfiguration (object): Optional Apify proxy. Engaged automatically only when the source blocks direct requests (403/429).
    • stationQuery (string): Free-text station name, e.g. London, Birmingham - returns matching stations with codes, coordinates and connections.
    • departAfter (string): Only emit journeys departing at or after this time, HH:MM (mode=search).
    • arriveBy (string): Only emit journeys arriving at or before this time, HH:MM (mode=search).
    • ticketType (string): Fare class to keep: Standard, First or Season (mode=search). Journeys without a detectable class pass through.

    The other 1 controls, with their defaults, are listed in the input schema on Trainline Scraper on Apify.

    Fixed-choice controls: mode accepts search (Search journeys (origin → destination)), stationLookup (Look up stations (autocomplete)), popularRoutes (Fetch popular routes with prices); ticketType accepts Standard (Standard class), First (First class), Season (Season tickets); locale accepts 18 values (default en-gb), including en-gb (English (UK)), en-us (English (US)), en-fr (English (France)), fr-fr (Français (France)); currency accepts 24 values (default GBP), including GBP (British Pound), EUR (Euro), USD (US Dollar), CHF (Swiss Franc).

    What does Trainline Scraper return?

    The output supplies timetable schedules, station coordinates, connection types, and fare figures across UK and European transit corridors. It does not provide booking confirmations, passenger details, seat reservation maps, or live train GPS tracking.

    Per journey (recordType: "journey")

    • origin, destination - resolved station names
    • departureTime, arrivalTime - HH:MM
    • departureDate - YYYY-MM-DD
    • durationMinutes, changes
    • operator - e.g. Avanti West Coast
    • price, currency - ISO 4217 (GBP, EUR, …)
    • trainId, ticketType, fareClass
    • sourceUrl, scrapedAt, recordType

    Per journey summary (recordType: "journeySummary")

    • fromStation, toStation, fromSlug, toSlug
    • firstTrain, lastTrain - first/last departure times of the day
    • fastestJourneyMinutes, averageJourneyMinutes
    • frequencyPerDay, distanceMiles, distanceKm
    • directTrainsOnly, priceFrom, currency
    • operators[]
    • sourceUrl, scrapedAt, recordType

    Per station (recordType: "station")

    • stationId, stationUrn, name, shortName (e.g. EUS), countryCode
    • latitude, longitude, locationType, timezone, defaultLanguage
    • parentName, subtitle, attributes[], connections[], matchScore
    • sourceUrl - UK stations link to their /stations/<slug> page; other stations link to the public locations-search endpoint that produced them

    Per popular route (recordType: "popularRoute")

    • originName, destinationName, priceFrom, currency
    • durationMinutes, trainTimesPath, trainTimesUrl, isReverse
    • sourceUrl, scrapedAt, recordType

    These are the documented fields. Optional ones can be empty on a given record, so measure how often each field your deliverable depends on is populated across a real sample before automating the handoff.

    How do you build the workflow end to end?

    Open Trainline Scraper and work through these in order. Each step ends with something to check, so a bad configuration surfaces on a small run rather than a scheduled one.

    1. Set mode to search, stationLookup, or popularRoutes based on your target dataset.
    2. For search mode, enter the originStation and destinationStation names such as London Euston and Birmingham New Street.
    3. Specify travelDate in YYYY-MM-DD format with a future date to prevent the automatic fallback to today plus 7 days.
    4. Set departAfter or arriveBy in HH:MM format if you need to constrain the departure or arrival window.
    5. Set maxItems to 10 on your initial run to test the output structure before expanding collection volume.
    6. Run the Actor and inspect the recordType field in the dataset to see if you received journey, journeySummary, station, or popularRoute records.
    7. If DataDome blocks return journeySummary records when you need individual journey records, enable proxyConfiguration.

    How do you apply it? Three worked playbooks

    These are Trainline Scraper's own documented use cases, each worked through as an operating pattern rather than a description.

    Use case 1: Rail price monitoring

    Outcome: Track "from" prices on popular UK/EU corridors

    Configure: Set mode to "popularRoutes", locale to "en-gb", currency to "GBP", and maxItems to 50.

    Working method: Execute a run in popularRoutes mode to collect route listings. Inspect the dataset items to verify the presence of originName, destinationName, and priceFrom values across the returned routes. Store the resulting records in your pricing pipeline to compare against subsequent scheduled runs.

    Deliverable: A dataset of popular routes containing originName, destinationName, priceFrom, currency, durationMinutes, and trainTimesUrl.

    Stop condition: The dataset contains 0 records or priceFrom is missing across all returned items.

    Use case 2: Journey planning data

    Outcome: First/last trains, frequencies and durations for route research

    Configure: Set mode to "search", originStation to "London Euston", destinationStation to "Birmingham New Street", travelDate to a future date, and maxItems to 20.

    Working method: Run the Actor with originStation and destinationStation configured for your corridor. Check the returned records for firstTrain, lastTrain, frequencyPerDay, and averageJourneyMinutes if a journeySummary is emitted, or departureTime and arrivalTime if journey records are returned. Export the records to evaluate transit intervals.

    Deliverable: A dataset containing route operational fields including firstTrain, lastTrain, fastestJourneyMinutes, averageJourneyMinutes, and frequencyPerDay.

    Stop condition: Origin and destination stations resolve to the same location, resulting in 0 records.

    Use case 3: Station reference data

    Outcome: Code, coordinates and connection lookups

    Configure: Set mode to "stationLookup", stationQuery to "Birmingham", locale to "en-gb", and maxItems to 10.

    Working method: Execute the Actor with stationQuery set to the target city or station name. Inspect the resulting station records to verify that stationId, shortName, latitude, longitude, and connections arrays are populated. Integrate the coordinates and identifiers into your location database.

    Deliverable: A dataset of station records containing stationId, stationUrn, name, shortName, latitude, longitude, countryCode, and connections.

    Stop condition: The stationLookup query returns 0 records for a verified existing station query.

    What breaks, and how do you design around it?

    • Trainline's live fare engine (/api/journey-search/) is protected by DataDome bot detection. From some datacenter IPs it returns a CAPTCHA challenge and no journeys. When this happens, search mode automatically falls back to the server-rendered route-summary page, which is reliably accessible and still yields real data (first/last train, fastest & average journey time, trains per day, distance, price-from, operators). The summary fetch retries with backoff on transient 429/500s before giving up. To increase the chance of full per-journey fares (price per train, exact times), enable the optional Apify proxy - it is engaged automatically only when a block is detected (no extra cost on direct traffic).
    • The fallback record is marked recordType: "journeySummary"; full per-journey records are recordType: "journey".
    • Season-ticket filtering (ticketType: "Season") only keeps journeys whose fare class is explicitly detected as seasonal - Trainline rarely exposes it per journey, so this filter may legitimately yield zero records.
    • Trainline requires a valid future travel date; past dates are clamped to today + 7 days.
    • Origin and destination resolving to the same station return 0 records (Trainline serves no data for identical stations) with a clear status message.
    • The train-times summary pages are locale-independent - the locale input affects the station autocomplete and results page, not the summary URL (localized prefixes return 404).

    When DataDome challenges the live fare engine, the Actor automatically emits a server-rendered route summary record instead of failing. Enabling proxyConfiguration increases the chance of receiving full per-journey records. If querying routes, ensure travelDate is set to a future date, as past dates are automatically clamped to today plus 7 days.

    When should you not use Trainline Scraper?

    Do not use this Actor if you need to execute ticket bookings, modify customer reservations, or monitor live GPS vehicle positions. If your data pipeline requires dedicated bus and coach network coverage across Europe or North America, look at FlixBus Scraper. When you require departures aggregated across more than 500 bus and train operators, evaluate Busbud Bus & Train Fare Scraper instead. For multi-modal flight itineraries with virtual interlining, use Kiwi.com Scraper.

    What should you check before trusting the output?

    • Verify that recordType matches journey when exact departure and arrival times are required rather than journeySummary.
    • Check that price and currency are populated when tracking active ticket fares.
    • Confirm that latitude and longitude are present when consuming station lookup outputs.
    • Ensure originStation and destinationStation are not set to identical names, which causes Trainline to return 0 records.
    • Check whether ticketType is set to Season, as Trainline rarely exposes this per journey and it may return zero records.

    None of this proves a record is correct. It gives a scheduled Trainline Scraper run defined points where it should stop instead of quietly passing bad data downstream.

    Frequently asked questions

    What is the cost of running this Actor on Apify?

    Results cost $0.005 per result, which is $5.00 per 1,000 results on the free plan. Apify's free plan includes $5.00 of monthly usage with no credit card, covering up to 1,000 results. The example input caps maxItems at 10, costing at most $0.05 in result charges for a first run. Apify also bills platform usage consumed during the run.

    Why did my search run return a journeySummary record instead of journey records?

    Trainline's live fare engine is protected by DataDome bot detection. When automated traffic is challenged, the Actor automatically emits the server-rendered route summary record (first and last train, fastest and average journey time, frequency, distance, price-from, and operators) instead of failing. Enabling the optional Apify proxy increases the chance of receiving full per-journey fares.

    What happens if I submit a travel date in the past?

    Trainline requires a valid future travel date. If an input date is empty or set in the past, the Actor automatically clamps the travelDate parameter to today plus 7 days before running the query.

    Do I need a Trainline account or session cookies to scrape data?

    No. The scraper accesses public JSON endpoints for station lookups and popular routes, and runs a browser-based journey search flow. No login, account credentials, or cookies are required to run the Actor.

    Why do some records omit specific fields like trainId or priceFrom?

    Empty fields are omitted by design in the Actor output. If Trainline does not publish a specific data point for a journey or route, such as a trainId on a journey card or a priceFrom value on a summary page, that field is left out of the dataset record.

    Where to go next

    When you are ready to run it, open Trainline Scraper on Apify; the free plan covers up to 1,000 results a month.

    Start with the Trainline Scraper Actor page for the current input schema, pricing tier, and run history.

    Other Actors we maintain for related data:

    • FlixBus Scraper: Scrape FlixBus - Europe and North America's largest intercity bus network.
    • Busbud Bus & Train Fare Scraper: Scrape Busbud bus and train departures between any two cities - operators, vehicle types, amenities, schedules, durations, and prices aggregated across 500+ operators.
    • Kiwi.com Scraper: Scrape Kiwi.com - flight metasearch with virtual interlining.
    • Ryanair Scraper: Search Ryanair's price calendar for the cheapest one-way and round-trip fares on any route.

    Related guides:

    Resources

    • Actor documentation, input schema, and pricing: verified against the published Actor on 2026-09-26.

    • Actor last updated by its maintainers on 2026-08-14.

    • Run outcome figures cover the 30 day public window ending 2026-09-26.

    • Trainline Scraper on Apify

    Featured actors

    Trainline Scraper

    Scrape Trainline - the UK & Europe rail/bus metasearch. Journey search (origin → destination → date/time) with departure/arrival times, duration, changes, operator and fares; station autocomplete lookup; and popular routes with 'from' prices.

    Run on Apify ↗