# Changi Airport (SIN) Flight Scraper – Arrivals & Departures (`rl1987/sin-airport-timetable`) Actor

One row per flight at Singapore Changi Airport (SIN): flight number, airline, origin or destination airport, terminal, gate, baggage belt, scheduled, estimated and actual times, and live status such as Boarding, Delayed or Landed. Filter by arrivals, departures, date and Terminal 1-4.

- **URL**: https://apify.com/rl1987/sin-airport-timetable.md
- **Developed by:** [R.L.](https://apify.com/rl1987) (community)
- **Categories:** Travel
- **Features:** Limited permissions
- **Stats:** 4 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.54 / 1,000 flight rows

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

## Changi Airport (SIN) Flight Scraper – Arrivals & Departures

Pulls live arrival and departure flight data for **Singapore Changi Airport (SIN)** straight from Changi's own flight-status API — the same one powering [changiairport.com](https://www.changiairport.com/en/fly/flight-information.html). You get one dataset row per flight, with flight number, airline, origin or destination airport, terminal, gate, baggage belt, scheduled/estimated/actual times and live status, in structured JSON, CSV, or Excel, without touching a browser. Run it once, on a schedule, or hook it into your own app via the Apify API.

### What can I use this for?

- **Flight tracking dashboards** — build a live arrivals/departures board for a travel app or internal tool.
- **Passenger meeting & logistics** — check gate, belt, and delay status for pickups or ground transport planning.
- **Travel & OTA data feeds** — enrich itineraries with real-time SIN flight status.
- **Research & analytics** — study route frequency, delay patterns, or terminal load over time.

Runs on Apify, so you get scheduling, an API for every result, and monitoring/alerting for free — no server or proxy setup needed.

### How do I run it?

1. Open the Actor's **Input** tab.
2. Pick a **direction**: arrivals, departures, or both.
3. Optionally set a **scheduled date**, **terminal**, and whether to fetch **backwards in time** from that date.
4. Optionally cap the number of results with **max items**.
5. Click **Start** and download results from the **Output** tab once the run finishes.

### What input does it take?

| Field | Type | Description |
|---|---|---|
| `direction` | select | `dep` (departures only, default), `arr` (arrivals only), or `both` |
| `scheduledDate` | date | Flight schedule date, `YYYY-MM-DD`. You get flights for **this date only** — rows for any other date are never returned or charged. For a past or future date that is the complete day; for today it is the part still to come, because the source pages forward from the current time. Defaults to today in Singapore time (Asia/Singapore). |
| `terminal` | select | Filter by terminal: `all` (default), `1`, `2`, `3`, or `4` |
| `prev` | boolean | Return the day **before** `scheduledDate` instead, newest flight first. That is what Changi's own `prev` flag does — it starts at the end of the previous day, not at the elapsed part of the one you asked for. Default `false`. |
| `maxItems` | integer | Cap on rows per direction. Default `100`; raise it for more, or leave it empty (`null`) for every flight the source publishes for that date. |
| `proxyConfiguration` | proxy | Apify Proxy config. A fresh proxy session is used per request to avoid rate limiting. Defaults to Apify Proxy enabled. |

Example input:

```json
{
  "direction": "both",
  "scheduledDate": "2026-08-02",
  "terminal": "1",
  "prev": false,
  "maxItems": 100
}
```

### What does each row contain?

One dataset row is one flight entry. Example (trimmed):

```json
{
  "flight_number": "MF852",
  "direction": "DEP",
  "airline": "MF",
  "airline_details": { "code": "MF", "name": "Xiamen Airlines", "transfer_counters": "C,D" },
  "airport": "XMN",
  "airport_details": { "code": "XMN", "name": "Xiamen", "country_code": "CN", "lat": "24.54400063", "lng": "118.1279984" },
  "terminal": "1",
  "check_in_row": "3",
  "display_gate": "D32",
  "aircraft_type": "B738",
  "scheduled_date": "2026-09-27",
  "scheduled_time": "15:15",
  "estimated_timestamp": "2026-09-27 15:15",
  "offblock_timestamp": "2026-09-27 15:36",
  "flight_status": "Gate Closed",
  "slave_flights": ["GA9735", "MU4900"]
}
```

You can download results as JSON, CSV, Excel, or XML from the Output tab, or pull them programmatically via the [Apify API](https://docs.apify.com/api/v2).

#### Main fields

| Field | Description |
|---|---|
| `flight_number` | Flight code, e.g. `SQ963` |
| `direction` | `ARR` or `DEP` |
| `airline` / `airline_details` | IATA airline code, plus name (English, Simplified and Traditional Chinese), logo URL, transfer counters and transit flag |
| `airport` / `airport_details` | The other end of the route — origin for arrivals, destination for departures — with IATA code, name, country code and latitude/longitude |
| `terminal` | Changi terminal, `1`–`4` |
| `display_gate` / `current_gate` / `previous_gate` | Gate: boarding gate for departures, arrival gate for arrivals, with the previous one if it moved |
| `display_belt` | Baggage belt (arrivals) |
| `check_in_row` / `display_checkinrowctr` | Check-in row and counter range (departures) |
| `drop_off_door` / `pick_up_door` / `nearest_carpark` | Kerbside door numbers and the nearest car park for that flight |
| `scheduled_date` / `scheduled_time` | Scheduled date and local time |
| `estimated_timestamp` / `actual_timestamp` / `display_timestamp` | Estimated, actual, and the timestamp the airport currently displays |
| `offblock_timestamp` / `firstbag_timestamp` / `lastbag_timestamp` | Push-back time, and first/last bag on the belt |
| `flight_status` / `status_mapping` | Current status, plus the airport's own display strings (English and Chinese) and text colour |
| `aircraft_type` | ICAO aircraft type code, e.g. `B738` |
| `origin_dep_*` | For arrivals: the departure country, date, time and terminal at the origin airport |
| `via` / `via_airport_details` | Intermediate stop, when the flight is not direct |
| `slave_flights` / `master_flight_number` | Codeshare flight numbers sold on the same physical flight, and the operating flight number |
| `last_updated_timestamp` | When Changi last updated this entry |

#### What do the less obvious fields mean?

- **Codeshare (`slave_flights`)** — one physical aircraft is often sold under several flight numbers. The row you get is the operating flight (`master_flight_number`), and `slave_flights` lists the marketing flight numbers riding on it, e.g. `MF852` also sold as `GA9735` and `MU4900`. Deduplicate on `master_flight_number` if you want one row per aircraft movement.
- **`display_timestamp` vs `scheduled_time`** — `scheduled_time` is the timetabled time and never moves; `display_timestamp` is what the airport's board currently shows, which follows delays and re-times.
- **`status_mapping`** — the airport's own presentation of the status: `listing_status_en` is the short string for a board (it can include a time, e.g. `Landed 15:24`), `details_status_en` the one for a flight-detail page, `status_text_color` the colour Changi paints it, and `show_gate` whether a gate should be displayed yet. `*_zh` variants are the Chinese equivalents.
- **`nature`, `flight_type`, `technical_flight_status1`, `technical_flight_status2`** — single-letter and short internal codes from Changi's system (values such as `J`, `M`, `ES`, `GC` appear), passed through unchanged. The source does not publish what they mean, so use `flight_status` or `status_mapping` for anything you display.

### Which airlines, airports and statuses show up?

The Actor does not filter by airline — it returns whatever Changi publishes for the date, direction and terminal you ask for. Sample runs across all four terminals returned flights from carriers including:

| Region | Airlines seen |
|---|---|
| Singapore & Southeast Asia | Singapore Airlines, Scoot, Malaysia Airlines, AirAsia, Batik Air, Garuda Indonesia, TransNusa, Thai Airways, Myanmar National Airlines |
| Greater China & East Asia | Cathay Pacific, EVA Air, China Eastern Airlines, Air China, Xiamen Airlines, Hainan Airlines, Sichuan Airlines, Spring Airlines |
| Middle East & South Asia | Emirates, Qatar Airways, Biman Bangladesh Airlines |
| Europe & Africa | KLM Royal Dutch Airlines, Ethiopian Airlines |

Routes in the same sample covered Kuala Lumpur, Jakarta, Bangkok (Suvarnabhumi), Hong Kong, Taipei, Shanghai, Beijing (Daxing), Ho Chi Minh City, Phnom Penh, Penang, Melbourne, Brisbane, Amsterdam, Doha, Dhaka and Yangon, among others.

Statuses observed in `flight_status` include `Scheduled`, `Confirmed`, `Gate Open`, `Boarding`, `Last Call`, `Gate Closing`, `Gate Closed`, `Departed`, `Landed`, `Delayed`, `Re-timed` and `New Gate`. Flights far in the future have no status yet, so `flight_status` is `null` for them.

### How much does a run cost?

The Actor is billed per flight row it pushes (see the **Pricing** tab), so cost scales with how many flights you ask for, not with how long the run takes. `maxItems` is the cap: it applies **per direction**, so `direction: "both"` with `maxItems: 100` returns at most 200 rows. Leaving `maxItems` at its default of `100` keeps a run predictable; empty it only when you genuinely want every flight on the date — about **500 per direction** for a complete day, so roughly 1,000 rows for `direction: "both"`.

It is a plain API-based scraper with no browser rendering, so platform compute is small — a few hundred flights typically finish in well under a minute.

### Tips

- Use `maxItems` to limit runs when you only need a handful of flights.
- Combine `terminal` with `direction` to build a focused board for one terminal.
- Schedule the Actor (Apify's built-in **Schedules**) to keep a dataset continuously refreshed.

### FAQ

**Do I need an account or an API key?** Only an Apify account. There is no Changi or airline API key to obtain, and no login — the Actor talks to the public endpoint the airport's own website uses.

**Does it cover all of Changi?** Yes, Terminals 1, 2, 3 and 4, arrivals and departures. Leave `terminal` on `all` for every terminal, or pick one. The source exposes Terminals 1-4 only.

**Can I get flights for a future date?** Yes. Set `scheduledDate` and the Actor returns the published schedule for it; dates weeks ahead work, though future flights have no live status, gate or belt yet — only the timetable.

**Do I ever get flights for a date I did not ask for?** No. Every row's own `scheduled_date` is checked against the date the run is supposed to return, and paging stops at the edge of it. This was a real bug before: a run asking for one date paged straight through midnight and billed you for the following days too — on the default input, 87 of 100 rows were the wrong day.

**Can I get the earlier part of today?** No, and no setting changes that. Asked for today, Changi's API pages forward from roughly the current time, and `prev` returns the previous day rather than the elapsed part of this one. For a complete day, ask for a date that has finished, or schedule the Actor through the day and accumulate the dataset.

**Can I get historical flights?** Barely. Changi's API only keeps a few recent days, and a `scheduledDate` older than that quietly returns the earliest data it still has rather than failing — always check `scheduled_date` on the rows you get back. For a history, schedule the Actor and accumulate the dataset yourself.

**How do I get the flights that just left or landed?** Leave `prev` off and ask for today — you get the next flights from now onwards. `prev: true` is a different thing: it returns the **day before** `scheduledDate`, newest first, which is Changi's own behaviour for that flag. So `scheduledDate` = tomorrow with `prev: true` is how you walk backwards through today's flights.

**Can I schedule it or call it from my own code?** Yes — like any Actor it has an HTTP API, a CLI, [schedules](https://docs.apify.com/platform/schedules) and [integrations](https://apify.com/integrations) (Make, Zapier, n8n, webhooks, Google Sheets, ...).

**What happens if the source rate-limits the run?** An HTTP 429 is retried up to five times with exponential backoff, each retry through a fresh proxy session, which is why Apify Proxy is on by default. If Changi returns a partially broken page, the usable flights are kept and pushed, and the dropped ones are counted in the run log rather than failing the run.

**Are codeshare flights included?** They are not separate rows. Each row is one physical movement under the operating flight number, with the codeshare numbers listed in `slave_flights`.

**Disclaimer:** This Actor reads publicly accessible flight data exposed by Changi Airport's own website. Flight information can change without notice — always verify time-critical details (gate, delay, cancellation) directly with the airline or airport before travelling. Found a bug or have a feature request? Use the Actor's **Issues** tab.

### Travel & booking toolkit

Part of the **Travel & booking toolkit** — search and pricing data across accommodation, transport, and travel-planning sites worldwide:

- [Booking.com API Scraper](https://apify.com/rl1987/booking-api-scraper) — Scrape Booking.com properties, live prices and full room-rate tables.
- [Agoda API Scraper](https://apify.com/rl1987/agoda-scraper) — Agoda search results, pricing and property details via GraphQL.
- [Airbnb API Scraper](https://apify.com/rl1987/airbnb-api-scraper) — Airbnb listings, details, and reviews via GraphQL API.
- [Hostelworld API Scraper](https://apify.com/rl1987/hostelworld-api-scraper) — Hostelworld hostels & properties with full listing + property detail.
- [FlixBus API Scraper](https://apify.com/rl1987/flixbus-api-scraper) — FlixBus trips between cities — times, stations, duration, prices.
- [eSIMDB Travel eSIM Scraper](https://apify.com/rl1987/esimdb-scraper) — Travel eSIM data plans from esimdb.com by country/region.

### Did you find this useful?

Rate this actor on Apify! Your feedback helps other users find it and helps us keep improving it.

# Actor input Schema

## `direction` (type: `string`):

Flight direction to scrape. Choose 'both' to scrape arrivals and departures.

## `scheduledDate` (type: `string`):

Flight schedule date in YYYY-MM-DD format. You get flights for this date only - never any other date. For a past or future date that is the complete day; for today it is the part still to come, because the source pages forward from the current time. Defaults to today (Asia/Singapore) if not set.

## `terminal` (type: `string`):

Filter flights by terminal. Leave as 'All' to fetch flights from every terminal.

## `prev` (type: `boolean`):

If enabled, returns the day BEFORE the scheduled date, newest flight first - that is what Changi's own prev flag does. Set the scheduled date to tomorrow with this enabled to walk backwards through today.

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

Maximum number of flight entries to fetch per direction. Leave empty to fetch every flight the source publishes for that date - about 500 per direction for a complete day.

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

Proxy servers to rotate requests through, to avoid rate limiting (HTTP 429) from Changi's API.

## Actor input object example

```json
{
  "direction": "dep",
  "scheduledDate": "2026-08-02",
  "terminal": "all",
  "prev": false,
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `flights` (type: `string`):

No description

## `input` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("rl1987/sin-airport-timetable").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("rl1987/sin-airport-timetable").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 '{}' |
apify call rl1987/sin-airport-timetable --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,rl1987/sin-airport-timetable"
        }
    }
}
```

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/nV8Ph11ljBNQaaFH1/builds/p0rlnYW9tBQi5Djbl/openapi.json
