# Italo Train Scraper - High-Speed Fares, Classes & Times (`muhammadafzal/italo-scraper`) Actor

Scrape Italo train fares for any Italian route: train numbers, times, Smart, Prima and Club Executive prices in EUR and seats left. $4 per 1,000 fares.

- **URL**: https://apify.com/muhammadafzal/italo-scraper.md
- **Developed by:** [Muhammad Afzal](https://apify.com/muhammadafzal) (community)
- **Categories:** Travel, Automation, Other
- **Security:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 96.6% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.20 / 1,000 italo fare results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Italo Train Scraper - High-Speed Fares, Classes & Times

Italo Train Scraper returns Italo high-speed train fares between Italian stations such as Milano Centrale, Roma Termini, Firenze S.M.Novella and Napoli Centrale, with train number, departure and arrival times, duration, stops, class (Smart, Prima, Club Executive, Salotto), fare type, EUR price and seats left. It is built for Italy travel planners, fare-comparison sites and AI agents, and costs about $4.00 per 1,000 fare rows on the Apify Free plan plus a start fee of about $0.049 per run.

The Actor reads Italo's public booking search anonymously. It never logs in, reserves seats, enters passenger details or pays, and it charges only for fare rows it saves.

### What Italo train data does it return?

Each dataset row is one fare: one train in one class with one fare type. A single train therefore produces several rows when it offers several classes.

| Field | What it holds |
| --- | --- |
| `fareId` | Stable ID for deduplication |
| `origin`, `destination`, `departureDate` | Resolved Italo stations and the travel date |
| `departureTime`, `arrivalTime` | Local times (`HH:MM`, Europe/Rome) |
| `departureDateTime`, `arrivalDateTime` | Full ISO 8601 times with offset |
| `durationMinutes`, `changes`, `direct`, `stops` | Journey length, changes and intermediate stops |
| `trainNumber`, `service` | Italo train number and service name |
| `travelClass`, `fareType` | Class (for example `Smart`, `Prima`, `Club Executive`, `Salotto`) and fare family |
| `price`, `currency`, `priceIndicative` | Fare in EUR and whether Italo showed it as a "from" price |
| `availability` | Seats left at that fare, for example `75 seats` |
| `bookingUrl`, `sourceUrl`, `scrapedAt` | Link to continue on Italo, provenance and capture time |

Each run also writes `OUTPUT` and `SUMMARY` records with `outcome` (`DATA`, `EMPTY`, `PARTIAL`, `BLOCKED`, `REJECTED` or `FAILED`), the requested route, delivered counts and warnings.

### When should you use it, and when not?

Use it when you need:

- the Italo train schedule and ticket prices for one route and date, by class;
- to compare Smart against Prima or Club Executive fares on the same train;
- a scheduled check of Italo fares on a busy route such as Milan to Rome;
- structured Italo data for an AI travel assistant.

Do not use it when:

- you want to book. The Actor is read-only;
- you need Trenitalia trains (Frecciarossa, Intercity, regional). Use [Trenitalia Train Tickets Scraper](https://apify.com/muhammadafzal/trenitalia-train-ticket-connection-scraper); run both to compare the two operators on one route;
- you need a return journey in one run. This Actor searches one direction; run it again with stations swapped;
- you need trains outside Italy: [Deutsche Bahn Train Tickets Scraper](https://apify.com/muhammadafzal/deutsche-bahn-train-tickets-connections-scraper), [National Rail Tickets Scraper](https://apify.com/muhammadafzal/national-rail-train-ticket-connection-scraper) or [SNCF Scraper](https://apify.com/muhammadafzal/sncf-train-ticket-connection-scraper).

### How do I search Italo train tickets and fares?

Enter a departure and an arrival station. City names that Italo recognises, such as `Rome`, also work.

- `departureDate` is `YYYY-MM-DD` in Italy time. Leave it empty to search tomorrow, which keeps saved Tasks evergreen. Words such as "tomorrow" are not accepted, and past dates are rejected.
- `departureTime` (default `00:00`) keeps only trains leaving at or after that time.
- `adults` (1-9) and `children` (0-9) set the passengers for the quote.
- `travelClass` (`all`, `smart`, `prima`, `club`) and `directOnly` narrow the rows.
- `maxResults` caps the fare rows, from 1 to 20 (default 5). Because each class is its own row, 5 rows may cover only one or two trains; raise it or filter by class to see more departures.

```json
{
  "from": "Milano Centrale",
  "to": "Roma Termini",
  "departureTime": "08:00",
  "adults": 1,
  "travelClass": "smart",
  "directOnly": true,
  "maxResults": 10
}
```

### Use it from an AI assistant

Italo Train Scraper works from Claude, ChatGPT, Cursor or any other MCP client through Apify's MCP server. Add `https://mcp.apify.com/?tools=muhammadafzal/italo-scraper` as a server, and the assistant can call the Actor as one tool. Example prompt: "What do Italo Smart fares cost from Roma Termini to Napoli Centrale tomorrow morning, and how many seats are left on each train?"

### How do I run it from code?

JavaScript (`apify-client`):

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('muhammadafzal/italo-scraper').call({
    from: 'Milano Centrale',
    to: 'Roma Termini',
    travelClass: 'smart',
    maxResults: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((f) => `${f.departureTime} ${f.trainNumber} ${f.travelClass} ${f.price} ${f.currency}`));
```

Python (`apify-client` 3.x):

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("muhammadafzal/italo-scraper").call(
    run_input={"from": "Milano Centrale", "to": "Roma Termini", "travelClass": "smart", "maxResults": 10}
)
for fare in client.dataset(run.default_dataset_id).iterate_items():
    print(fare["departureTime"], fare["trainNumber"], fare["travelClass"], fare["price"], fare["availability"])
```

### What does the output look like?

Two rows from a default run (Milano Centrale to Roma Termini, searched 2026-10-26): the same 05:40 train in two classes.

```json
[
  {
    "fareId": "a67fcfdceaf6c9c9398e",
    "origin": "Milano Centrale",
    "destination": "Roma Termini",
    "departureDate": "2026-10-27",
    "departureTime": "05:40",
    "arrivalTime": "09:19",
    "departureDateTime": "2026-10-27T05:40:00+01:00",
    "arrivalDateTime": "2026-10-27T09:19:00+01:00",
    "durationMinutes": 219,
    "changes": 0,
    "direct": true,
    "trainNumber": "9907",
    "service": "Italo",
    "travelClass": "Smart",
    "fareType": "Base",
    "price": 95.9,
    "currency": "EUR",
    "priceIndicative": false,
    "availability": "75 seats",
    "stops": ["Milano Rogoredo", "Reggio Emilia AV", "Bologna Centrale", "Firenze S.M.Novella", "Roma Tiburtina"],
    "bookingUrl": "https://biglietti.italotreno.com/en/booking/ricerca"
  },
  {
    "fareId": "4c9b1e50da53af053692",
    "departureTime": "05:40",
    "trainNumber": "9907",
    "travelClass": "Prima",
    "fareType": "Base",
    "price": 129.9,
    "currency": "EUR",
    "availability": "52 seats"
  }
]
```

### Pricing: how much does an Italo search cost?

You pay per fare row saved, plus a start fee. Platform usage is included in these prices.

| Event | FREE | BRONZE | SILVER | GOLD |
| --- | ---: | ---: | ---: | ---: |
| Italo fare result (primary event, per saved row) | $0.004001 | $0.0039 | $0.0038 | $0.0032 |
| Actor start (per started GB of allocated memory) | $0.049215 | $0.047984 | $0.046754 | $0.039372 |

PLATINUM and DIAMOND plans pay the GOLD prices. The default memory is 512 MB, so a normal run pays one start event.

Worked totals on the Free plan:

- Default run, 5 fare rows: $0.049215 + 5 × $0.004001 = **about $0.069**.
- Maximum run, 20 fare rows: $0.049215 + 20 × $0.004001 = **about $0.129** (GOLD: about $0.103).

Rejected inputs, empty searches and blocked runs save no rows and charge no fare events; only the start fee applies. Before each row is saved, the Actor checks your maximum cost per run and stops cleanly when it is reached. The cost estimate in the run's status message uses your live per-event prices.

### How reliable is it, and what are its limits?

- The default run (Milano Centrale to Roma Termini, 5 rows) finished in about 7 seconds on 2026-10-26 and saved 5 fare rows.
- One-way searches only, and at most 20 fare rows per run.
- Only structured fare data from Italo's booking response becomes a row; text scraped from the page is never turned into a price.
- Prices and seats change quickly. Each row is a snapshot at `scrapedAt`; confirm on Italo before you buy.
- If Italo refuses the anonymous search, the run ends Failed with `OUTPUT.outcome` `BLOCKED` and no fare charges. Requests go direct; there is no proxy setting.

### FAQ

#### What is the difference between Italo and Trenitalia?

Italo is Italy's private high-speed operator; Trenitalia is the state operator that runs Frecciarossa, Intercity and regional trains. They sell tickets separately, so this Actor covers Italo only. Use the Trenitalia Actor for the other operator.

#### Which Italo classes does it cover?

Every class Italo shows for the train, typically Smart, Prima, Club Executive and Salotto. Set `travelClass` to `smart`, `prima` or `club` to keep one class.

#### Does it buy or hold Italo tickets?

No. It reads public fares only. Open `bookingUrl` on Italo to buy.

#### Why do several rows share one train number?

Each row is one class and fare type. Group by `trainNumber` and `departureDateTime` to get one line per train.

#### Can I search a round trip?

Not in one run. Run the Actor twice with `from` and `to` swapped.

#### What does `priceIndicative` mean?

`true` means Italo labelled the amount as a starting ("from") price rather than an exact fare. In the observed runs it was `false`.

#### Which date does it search by default?

Tomorrow in Italy time. Set `departureDate` for a specific day.

### Related Actors

- [Trenitalia Train Tickets Scraper](https://apify.com/muhammadafzal/trenitalia-train-ticket-connection-scraper): Frecciarossa, Intercity and regional connections and fares.
- [Deutsche Bahn Train Tickets Scraper](https://apify.com/muhammadafzal/deutsche-bahn-train-tickets-connections-scraper): German train tickets and timetables.
- [National Rail Tickets Scraper](https://apify.com/muhammadafzal/national-rail-train-ticket-connection-scraper): UK train times and fares.
- [FlixBus Tickets Scraper](https://apify.com/muhammadafzal/flixbus-route-price-scraper): bus alternatives on Italian and European routes.
- [Greyhound Bus Tickets, Megabus & Amtrak Scraper](https://apify.com/muhammadafzal/us-intercity-rail-bus-scraper): US intercity travel.
- [SNCF Scraper](https://apify.com/muhammadafzal/sncf-train-ticket-connection-scraper): French train schedules and prices.

### Legal and responsible use

Use the data for research, comparison and planning at a reasonable volume, and respect Italo's terms and local law. Do not present scraped fares as guaranteed or as a booking. This Actor is independent and not affiliated with or endorsed by Italo - Nuovo Trasporto Viaggiatori S.p.A.

Last verified: 2026-10-27

# Actor input Schema

## `from` (type: `string`):

Use this to choose the Italo departure city or station, for example Milano Centrale or Rome. Enter a public Italo location name, not a street address or ticket reference.

## `to` (type: `string`):

Use this to choose the Italo arrival city or station, for example Roma Termini or Rome. It must resolve to a different station from departure.

## `departureDate` (type: `string`):

Use this for the requested travel date in Europe/Rome as YYYY-MM-DD, for example 2026-10-15. Leave empty to search tomorrow; past dates are rejected.

## `departureTime` (type: `string`):

Use this to request departures at or after a Europe/Rome local time such as 08:00. The field is applied only when the structured booking response exposes a departure time.

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

Use this to set the number of adult travellers for the public fare quote, from 1 to 9. The Actor does not submit names or purchase tickets.

## `children` (type: `integer`):

Use this to include children in the public fare quote, from 0 to 9. Child age or identity is not collected; the target may require a manual selection for age-specific offers.

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

Use this to keep strict fare objects for a requested Italo class when the structured response exposes class labels. Use all to retain every displayed class; this Actor does not select seats.

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

Use this to keep only direct or non-stop itineraries when the structured response identifies stops or changes. Default false retains all validated Italo itineraries.

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

Use this to cap fare rows, from 1 to 20 (default 5). Each class of each train is its own row, so 5 rows may cover only one or two trains; raise it or filter by class to see more departures.

## Actor input object example

```json
{
  "from": "Milano Centrale",
  "to": "Roma Termini",
  "departureTime": "00:00",
  "adults": 1,
  "children": 0,
  "travelClass": "all",
  "directOnly": false,
  "maxResults": 5
}
```

# Actor output Schema

## `output` (type: `string`):

Structured outcome, counts, route, source diagnostics, warnings, and charge parity.

# 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 = {
    "from": "Milano Centrale",
    "to": "Roma Termini"
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/italo-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 = {
    "from": "Milano Centrale",
    "to": "Roma Termini",
}

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/italo-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 '{
  "from": "Milano Centrale",
  "to": "Roma Termini"
}' |
apify call muhammadafzal/italo-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,muhammadafzal/italo-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/1fQVbuxa4pgOgGB5P/builds/RhvjWgkGGbPtRvU1n/openapi.json
