# Amtrak Scraper (`crawlerbros/amtrak-scraper`) Actor

Scrape Amtrak - America's intercity passenger rail network. Get the full station list, every train route, station-to-station schedules, and per-train service details (train number, route stops, service type).

- **URL**: https://apify.com/crawlerbros/amtrak-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Automation, Developer tools, Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

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

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Amtrak Scraper

Scrape **Amtrak** — America's intercity passenger rail network. Get the complete station list (1,000+ stations), every train route, per-train service details with route stop orders, and station-to-station schedule searches.

### What this actor does

- **Five modes:** `stationsList` (all Amtrak stations), `popularRoutes` (Amtrak's featured origin→destination pairs), `routesList` (all train routes), `byTrainNumber` (train number → service + route stops), `search` (station-to-station schedule search)
- **Station records:** 3-letter station code, full name, facility name (short + long), city, state, country, timezone (+ friendly IANA name), daily departures (+ count), autocomplete label, popular-station flag, popular destinations
- **Route records:** route code, route name, cities served
- **Train records:** train number → service type (e.g. `2150` → Acela), ordered stop list, origin/terminus stations, stop count
- **Schedule records:** train number, origin/destination, departure/arrival times, duration, service type, days of operation
- **Empty fields are omitted**

### Data source

Amtrak (amtrak.com) publishes its network reference data as public JSON/XML resources on its own site, and the schedule search runs in the public web app. This actor loads the site in a full browser (Playwright) — plain HTTP clients are blocked by Amtrak's CDN (verified) — and reads:

- `https://www.amtrak.com/services/data.stations.json` — the complete station list (1,000+ stations)
- `https://www.amtrak.com/services/data.popularstations.json` — popular-station flags
- `https://www.amtrak.com/services/routes-list.json` — every train route
- `.../config/TrainCodesResource.xml` — train number → service name map
- `.../config/stopslist.json` — route → ordered station stops
- `https://www.amtrak.com/tickets/schedule-results.html` — the schedule search flow
- `https://www.amtrak.com/dotcom/travel-service/schedules` — the schedules JSON API the timetable widget calls (primary search path)

If the datacenter IP is blocked, the optional Apify proxy is engaged automatically with rotating sessions.

**Schedule search:** `mode=search` calls Amtrak's own schedules JSON API (`/dotcom/travel-service/schedules`) from inside the browser session — the same request the timetable widget makes (verified live, 40+ trains per corridor). The API returns the full schedule: train number, service name, departure/arrival times, duration, days of operation, status, and the ordered stop list. If that API is ever unavailable, the actor falls back to driving the timetable widget (typing the station codes, picking the date, submitting) — which requires dismissing Amtrak's cookie-consent overlay first. The station, route and train modes read Amtrak's public data resources directly and are fully reliable.

### Output per record

#### Per station (mode = `stationsList`)

- `stationCode` — 3-letter code (`NYP`, `CHI`, `BOS`, …)
- `name`, `stationName`, `city`, `state`, `alias`, `autoFillName`
- `facilityName`, `facilityNameLong` — station facility names (short + long forms)
- `timezone` — Amtrak zone code (`E` / `C` / `M` / `MS` / `P` / `PS`), `timezoneName` — IANA-style name (`America/New_York`, `America/Phoenix`, …)
- `dailyDepartures` — e.g. `3 daily departures`, `dailyDepartureCount` — the parsed number
- `isPopular`, `popularDestinations` — Amtrak's featured origin stations and their top destinations
- `country` — `US` (from upstream; `CA` for Amtrak's Canadian stations)
- `sourceUrl`, `recordType: "station"`, `scrapedAt`

#### Per popular route (mode = `popularRoutes`)

- `originStation` — featured origin code (e.g. `NYP`)
- `destinationStations[]` — its top destinations (e.g. `["WAS", "PHL", "BOS", …]`), `destinationCount`
- `sourceUrl`, `recordType: "popularRoute"`, `scrapedAt`

#### Per route (mode = `routesList`)

- `routeCode` (e.g. `ACEX`, `NERG`, `COAS`), `routeName` (Acela, Northeast Regional, Coast Starlight, …), `citiesServed`
- `sourceUrl`, `recordType: "route"`, `scrapedAt`

#### Per train (mode = `byTrainNumber`)

- `trainNumber` (e.g. `2150`), `serviceType` (e.g. `Acela`)
- `routeName`, `stops[]` — ordered station codes, `stopCount`, `originStation`, `terminusStation`
- `sourceUrl`, `recordType: "train"`, `scrapedAt`

#### Per schedule (mode = `search`)

- `trainId` / `trainNumber`, `origin`, `destination`
- `departureTime`, `arrivalTime`, `duration`, `durationMinutes`
- `travelDate`, `status`, `originFacility`, `destinationFacility`
- `serviceType`, `serviceNameShort` (e.g. "NE Regional"), `frequency[]` (days of operation)
- `trainType` (e.g. "Intercity Train"), `tripId`, `distanceMiles`, `directionCode`
- `operator` (e.g. "Amtrak"), `operatorCode` (e.g. "AMTK")
- `nightTrain`, `isSelfTransfer`, `restricted` (booleans), `amenities[]` (Cafe, Quiet Car, Free WiFi, Seat Selection)
- `stops[]` — ordered station codes, `stopCount`
- `sourceUrl`, `recordType: "schedule"`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `stationsList` | `stationsList` / `popularRoutes` / `routesList` / `byTrainNumber` / `search` |
| `origin` | string | `NYP` | Origin station code (mode=search) |
| `destination` | string | `WAS` | Destination station code (mode=search) |
| `travelDate` | string | – | Departure date `YYYY-MM-DD` (mode=search) |
| `departAfter` | string | – | Earliest departure `HH:MM` 24h (mode=search) |
| `trainNumber` | string | `2150` | Train number (mode=byTrainNumber) |
| `maxItems` | int | `100` | Hard cap (1–1000) |
| `proxyConfiguration` | object | Apify proxy | Optional; auto-engaged on CDN block |

#### Example: popular origin-destination pairs

```json
{
  "mode": "popularRoutes"
}
```

#### Example: full station list

```json
{
  "mode": "stationsList",
  "maxItems": 500
}
```

#### Example: lookup a train

```json
{
  "mode": "byTrainNumber",
  "trainNumber": "2150"
}
```

#### Example: schedule search

```json
{
  "mode": "search",
  "origin": "NYP",
  "destination": "WAS",
  "travelDate": "2026-09-15",
  "departAfter": "06:00"
}
```

#### Example: all routes

```json
{
  "mode": "routesList"
}
```

### Use cases

- **Travel apps** — power station autocomplete with the full official station catalog
- **Transit analytics** — map every route, its corridor and stop sequence
- **Trip planning tools** — train-number lookups and station-to-station schedules
- **Rail research** — network topology data for the US intercity rail system

### Limitations

- **Browser required** — Amtrak's CDN blocks plain HTTP clients (verified); the actor loads the public pages in a real browser (Playwright). The optional Apify proxy is auto-engaged on a CDN block.
- **`search` can fall back to a widget path** — the primary path uses Amtrak's own schedules API; if Amtrak blocks or changes it, the in-browser timetable flow may return 0 rows when Amtrak serves results without an established booking session. The station, route and train modes always work because they read the public data resources directly.
- **Stop-order data covers 33 named routes** — trains whose service is not in `stopslist.json` (or has a section suffix, e.g. "Lake Shore Limited - Boston") emit the train number and service type without a stop list.
- **Station codes are Amtrak's 3-letter codes** — the same codes shown on tickets (`NYP`, `CHI`, `BOS`), not IATA codes.
- **Schedules require a valid future date** — past departure dates are not served by Amtrak's schedule API.

### FAQ

**What is the data source?** Amtrak's own website (amtrak.com), which publishes the station catalog, route list, train-code map and stop lists as public data resources.

**Why does the actor need a browser?** Amtrak's CDN blocks plain HTTP clients (verified). The actor loads the public pages in a real browser, which is the only reliable way to read the data.

**Where do station codes come from?** `stationCode` is Amtrak's official 3-letter code used in the booking system — the same codes shown on tickets (e.g. `NYP` = New York Penn Station).

**Why do some trains have no `stops`?** The stop-order data (`stopslist.json`) covers 33 named routes. Trains whose service is not in that file (or whose name has a section suffix, e.g. "Lake Shore Limited - Boston") emit the train number and service type without a stop list. This is an upstream data limitation, not an omission.

**Why might `search` return no records?** The primary path uses Amtrak's own schedules API, which returns full schedules for any station pair and date. If Amtrak blocks or changes that API, the actor falls back to the in-browser timetable flow; the widget path can return 0 rows if Amtrak serves the results without an established booking session. The station, route and train modes always work because they read the public data resources directly.

**How fresh is the data?** Every run reads Amtrak live; `scrapedAt` records exactly when each record was captured.

**What does `frequency` mean?** The days of operation for a schedule, as 3-letter codes (`MON`, `TUE`, …). Some schedules run only on certain days.

**Is this affiliated with Amtrak?** No. This is an independent third-party actor using Amtrak's public website.

# Actor input Schema

## `mode` (type: `string`):

What to fetch.

## `origin` (type: `string`):

Departure station code (e.g. `NYP`, `CHI`, `BOS`) or station name.

## `destination` (type: `string`):

Arrival station code (e.g. `WAS`, `NOL`, `LAX`) or station name.

## `travelDate` (type: `string`):

Departure date in YYYY-MM-DD format.

## `departAfter` (type: `string`):

Earliest departure time in HH:MM (24-hour). Leave empty for any time.

## `trainNumber` (type: `string`):

Amtrak train number, e.g. `2150`, `92`, `448`.

## `maxItems` (type: `integer`):

Hard cap on emitted records.

## `proxyConfiguration` (type: `object`):

Optional. Used automatically if Amtrak's CDN blocks the datacenter IP (403/429).

## Actor input object example

```json
{
  "mode": "stationsList",
  "origin": "NYP",
  "destination": "WAS",
  "travelDate": "",
  "departAfter": "",
  "trainNumber": "2150",
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `records` (type: `string`):

Dataset containing all scraped Amtrak records.

# 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 = {
    "mode": "stationsList",
    "origin": "NYP",
    "destination": "WAS",
    "travelDate": "",
    "departAfter": "",
    "trainNumber": "2150",
    "maxItems": 100,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/amtrak-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 = {
    "mode": "stationsList",
    "origin": "NYP",
    "destination": "WAS",
    "travelDate": "",
    "departAfter": "",
    "trainNumber": "2150",
    "maxItems": 100,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/amtrak-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 '{
  "mode": "stationsList",
  "origin": "NYP",
  "destination": "WAS",
  "travelDate": "",
  "departAfter": "",
  "trainNumber": "2150",
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call crawlerbros/amtrak-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawlerbros/amtrak-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/FTpyOVLy2qlsNMGtm/builds/0dOFKdCFmqXxBkaFw/openapi.json
