Singapore Transit Scraper — MRT, LRT, Bus via LTA DataMall
Singapore Transit Scraper — MRT, LRT, Bus Arrival & LTA DataMall API
Extracts Singapore public transport data from the LTA DataMall REST API. This LTA DataMall API scraper covers real-time Singapore bus arrival times, a full bus stop database with GPS coordinates, bus service definitions, bus route-stop sequences, and live MRT/LRT service alerts — five modes, one actor, free government API. Pulls data for SMRT, SBS Transit, Tower Transit, and Go-Ahead Singapore.
Singapore Transit Scraper Features
- Extracts real-time bus arrival ETAs for any bus stop — next three buses, loading status, wheelchair access, bus type (single/double deck/bendy)
- Returns the complete inventory of ~5,400 bus stops with latitude, longitude, and road name
- Collects all ~600 bus service definitions including operator, first/last bus times by day type, and service category
- Downloads ~30,000 bus route-stop records with stop sequences and cumulative distance from origin
- Fetches current MRT/LRT service alerts — normal-service status or disruption details with affected segments and timestamps
- Pure API — no browser, no HTML parsing. DataMall is a public government endpoint with no geo-restrictions.
- A valid LTA DataMall AccountKey is required. Registration is free at datamall.lta.gov.sg.
What Can You Do with Singapore Transit & Bus Arrival Data?
- MaaS developers — Build real-time arrival displays, trip planners, or transit apps on top of official LTA data rather than scraping operator websites
- Logistics and last-mile planners — Map bus stop locations against delivery zones to identify coverage gaps or plan fleet positioning
- Urban mobility researchers — Analyse Singapore's route network structure, service frequency by area, or first/last bus timing coverage
- Transit alert systems — Monitor MRT/LRT disruptions in real time and trigger notifications for affected commuters or services
- Data journalists — Track service changes, new routes, and LTA schedule adjustments over time
- Fintech and PropTech analysts — Score properties by transit access using bus stop density, route counts, and MRT proximity
How Singapore Transit Scraper Works
- Choose a mode. Five modes cover different data types:
bus_arrivalfor real-time ETAs,bus_stops/bus_services/bus_routesfor the static network inventories, andtrain_alertsfor live MRT/LRT status. - Provide your LTA AccountKey. The scraper passes it as the
AccountKeyheader on every request. The DataMall API enforces authentication on all endpoints. - The scraper calls the API. For
bus_arrivalandtrain_alerts, it's a single request. For the three static modes, it pages through results in batches of 500 using$skipuntil it hits yourmaxItemslimit or exhausts the dataset. - Results land in the Apify dataset. Each record is a flat JSON object. A
record_typefield tells you which mode produced it, so mixed exports stay sorted.
Input
{
"mode": "bus_arrival",
"ltaApiKey": "your-lta-accountkey-here",
"busStopCode": "83139",
"serviceNo": "",
"maxItems": 15,
"sp_intended_usage": "Real-time bus arrival display for commuters",
"sp_improvement_suggestions": "n/a"
}
| Field | Type | Default | Description |
|---|---|---|---|
mode |
string | bus_arrival |
Which dataset to fetch. One of: bus_arrival, bus_stops, bus_services, bus_routes, train_alerts. |
ltaApiKey |
string | (required) | Your LTA DataMall AccountKey. Free registration at datamall.lta.gov.sg. |
busStopCode |
string | 83139 |
5-digit bus stop code. Required for bus_arrival mode. Run bus_stops mode to find codes. |
serviceNo |
string | (empty) | Filter by bus service number (e.g. 857, 65M). Optional — omit to return all services at the stop. |
maxItems |
integer | 15 |
Max records to return. Caps pagination for bus_stops, bus_services, and bus_routes. Set high for full inventories. |
Additional examples by mode:
{
"mode": "bus_stops",
"ltaApiKey": "your-lta-accountkey-here",
"maxItems": 5400
}
{
"mode": "train_alerts",
"ltaApiKey": "your-lta-accountkey-here",
"maxItems": 10
}
Singapore Transit Scraper Output Fields
Output varies by mode. Every record includes record_type, source_url, and scraped_at.
bus_arrival
{
"record_type": "arrival",
"operator": "SMRT",
"service_no": "858",
"stop_code": "83139",
"eta_1_time": "2026-05-05T06:14:22.000Z",
"eta_1_minutes": 3,
"eta_1_load": "SEA",
"eta_1_feature": "WAB",
"eta_1_type": "SD",
"eta_2_time": "2026-05-05T06:23:10.000Z",
"eta_2_minutes": 12,
"eta_2_load": "SDA",
"eta_2_type": "DD",
"eta_3_time": "2026-05-05T06:34:55.000Z",
"eta_3_minutes": 23,
"eta_3_load": "SEA",
"eta_3_type": "SD",
"bus_load": "SEA",
"bus_feature": "WAB",
"source_url": "https://datamall2.mytransport.sg/ltaodataservice/v3/BusArrival?BusStopCode=83139",
"scraped_at": "2026-05-05T06:11:00.000Z"
}
| Field | Type | Description |
|---|---|---|
record_type |
string | Always arrival for this mode |
operator |
string | Bus operator: SMRT, SBS Transit, Tower Transit, Go-Ahead Singapore |
service_no |
string | Bus service number |
stop_code |
string | 5-digit bus stop code |
eta_1_time |
string | ISO 8601 arrival time for next bus |
eta_1_minutes |
number | Minutes until next bus arrives |
eta_1_load |
string | SEA (seats available), SDA (standing available), LSD (limited standing) |
eta_1_feature |
string | WAB = wheelchair accessible bus |
eta_1_type |
string | SD = single deck, DD = double deck, BD = bendy |
eta_2_time |
string | Arrival time for 2nd next bus |
eta_2_minutes |
number | Minutes until 2nd next bus |
eta_2_load |
string | Passenger load for 2nd bus |
eta_2_type |
string | Bus type for 2nd bus |
eta_3_time |
string | Arrival time for 3rd next bus |
eta_3_minutes |
number | Minutes until 3rd next bus |
eta_3_load |
string | Passenger load for 3rd bus |
eta_3_type |
string | Bus type for 3rd bus |
bus_load |
string | Alias for eta_1_load |
bus_feature |
string | Alias for eta_1_feature |
source_url |
string | DataMall endpoint URL |
scraped_at |
string | ISO 8601 extraction timestamp |
bus_stops
{
"record_type": "stop",
"stop_code": "83139",
"stop_name": "Bedok Int",
"road_name": "New Upper Changi Rd",
"latitude": 1.32479,
"longitude": 103.93002,
"source_url": "https://datamall2.mytransport.sg/ltaodataservice/BusStops?$skip=0",
"scraped_at": "2026-05-05T06:11:00.000Z"
}
| Field | Type | Description |
|---|---|---|
record_type |
string | Always stop |
stop_code |
string | 5-digit bus stop code |
stop_name |
string | Official stop name / description |
road_name |
string | Road the stop is on |
latitude |
number | WGS84 latitude |
longitude |
number | WGS84 longitude |
bus_services
{
"record_type": "service",
"service_no": "858",
"operator": "SMRT",
"direction": 1,
"service_type": "TRUNK",
"category": "TRUNK",
"origin_code": "46009",
"destination_code": "84009",
"loop_desc": "",
"first_bus_weekday": "0430",
"last_bus_weekday": "2330",
"first_bus_saturday": "0500",
"last_bus_saturday": "2330",
"first_bus_sunday": "0530",
"last_bus_sunday": "2330",
"source_url": "https://datamall2.mytransport.sg/ltaodataservice/BusServices?$skip=0",
"scraped_at": "2026-05-05T06:11:00.000Z"
}
| Field | Type | Description |
|---|---|---|
record_type |
string | Always service |
service_no |
string | Bus service number |
operator |
string | Bus operator |
direction |
number | 1 = outbound, 2 = inbound |
service_type |
string | Service category |
category |
string | EXPRESS, FEEDER, INDUSTRIAL, TOWNLINK, TRUNK, NIGHT SERVICE, CITY_DIRECT, FLAT_FEE_BUS |
origin_code |
string | First stop code |
destination_code |
string | Last stop code |
loop_desc |
string | Loop terminal description (looping services only) |
first_bus_weekday |
string | First departure time on weekdays (HHMM) |
last_bus_weekday |
string | Last departure time on weekdays (HHMM) |
first_bus_saturday |
string | First departure on Saturdays (HHMM) |
last_bus_saturday |
string | Last departure on Saturdays (HHMM) |
first_bus_sunday |
string | First departure on Sundays/PH (HHMM) |
last_bus_sunday |
string | Last departure on Sundays/PH (HHMM) |
bus_routes
{
"record_type": "route_stop",
"service_no": "858",
"operator": "SMRT",
"direction": 1,
"sequence": 1,
"stop_code": "46009",
"distance_km": 0.0,
"first_bus_weekday": "0430",
"last_bus_weekday": "2330",
"first_bus_saturday": "0500",
"last_bus_saturday": "2330",
"first_bus_sunday": "0530",
"last_bus_sunday": "2330",
"source_url": "https://datamall2.mytransport.sg/ltaodataservice/BusRoutes?$skip=0",
"scraped_at": "2026-05-05T06:11:00.000Z"
}
| Field | Type | Description |
|---|---|---|
record_type |
string | Always route_stop |
service_no |
string | Bus service number |
operator |
string | Bus operator |
direction |
number | 1 = outbound, 2 = inbound |
sequence |
number | Stop sequence on this route (1-based) |
stop_code |
string | 5-digit bus stop code |
distance_km |
number | Cumulative distance from route origin in km |
first_bus_weekday |
string | First departure from this stop on weekdays (HHMM) |
last_bus_weekday |
string | Last departure from this stop on weekdays (HHMM) |
first_bus_saturday |
string | First departure on Saturdays (HHMM) |
last_bus_saturday |
string | Last departure on Saturdays (HHMM) |
first_bus_sunday |
string | First departure on Sundays/PH (HHMM) |
last_bus_sunday |
string | Last departure on Sundays/PH (HHMM) |
train_alerts
{
"record_type": "train_alert",
"train_alert_status": "Disrupted",
"affected_segments": "CCL:City:Caldecott:Bartley",
"train_alert_message": "05:00-CCL-Planned Service Adjustment. Circle Line train services will end earlier at 11.00pm on Saturday nights.",
"train_alert_created_at": "2026-04-10T00:15:58.000Z",
"source_url": "https://datamall2.mytransport.sg/ltaodataservice/TrainServiceAlerts",
"scraped_at": "2026-05-05T06:11:00.000Z"
}
| Field | Type | Description |
|---|---|---|
record_type |
string | Always train_alert |
train_alert_status |
string | Normal or Disrupted |
affected_segments |
string | MRT/LRT segments affected, comma-separated (empty when Normal) |
train_alert_message |
string | Human-readable alert text from LTA |
train_alert_created_at |
string | ISO 8601 timestamp of the alert |
FAQ
How do I get a list of all Singapore bus stops with coordinates?
Run bus_stops mode with maxItems set to 5400 or higher. Singapore Transit Scraper pages through the full LTA DataMall bus stop database — roughly 5,400 stops — and returns each one with its 5-digit code, name, road name, and WGS84 latitude/longitude. That gives you a complete Singapore bus stop list ready for mapping or geospatial joins.
How do I get an LTA DataMall API key?
Singapore Transit Scraper requires a free AccountKey from LTA DataMall. Fill out the registration form — name, company, email, intended use — and LTA sends the key by email. Registration is free for any use case. Most applicants receive their key within a business day.
How much does Singapore Transit Scraper cost to run?
Singapore Transit Scraper charges $0.10 per actor start plus $0.0008 per record. A full bus stop inventory (~5,400 records) runs roughly $4.40 total. Real-time arrivals for a single stop return 3–15 records, making it a few cents per call. Train alerts return a handful of messages at most.
What transit data does LTA DataMall provide?
Singapore Transit Scraper wraps five LTA DataMall datasets: real-time bus arrivals, the complete bus stop network (5,400 stops with GPS), all bus services (600), bus route-stop sequences (~30,000 records), and current MRT/LRT alerts. MRT timetables, taxi availability, ERP gantry data, and parking availability are separate DataMall endpoints not covered in this actor.
Can I get arrivals for multiple bus stops in one run?
bus_arrival mode takes one busStopCode per actor run. To query multiple stops, trigger parallel runs via the Apify API. Use bus_stops mode first to build a complete stop list with codes.
Need More Features?
Need additional DataMall endpoints, multi-stop arrival batching, or scheduled monitoring? File a request or get in touch.
Why Use Singapore Transit Scraper?
- Official data — Sourced directly from LTA DataMall, Singapore's authoritative transit data source. The same data that powers the MyTransport.SG app.
- Five datasets in one actor — Real-time arrivals, bus stops, services, routes, and train alerts without managing multiple API integrations or paginating $skip loops yourself.
- No infrastructure overhead — No browser, no fragile HTML parsing. DataMall returns clean JSON; Singapore Transit Scraper normalizes it into flat records and handles pagination, retries, and rate limiting.
Further reading: How to Get Airbnb Data, Hotel Prices, and Transit Feeds in Bulk