# Airport Departures & Arrivals Board Scraper (`apt_marble/airport-departures-arrivals-board-scraper`) Actor

Every departure and arrival at any airport, as one clean row per flight. Live status, terminal, gate, estimated time and delay. Codeshare duplicates folded into the aircraft that actually flies, so a day at JFK is 656 real movements, not 2,861. Whole day or right now, 25 airports at once.

- **URL**: https://apify.com/apt\_marble/airport-departures-arrivals-board-scraper.md
- **Developed by:** [Hamza](https://apify.com/apt_marble) (community)
- **Categories:** Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 flight movement collecteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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

## Airport Departures & Arrivals Board Scraper

Get every departure and arrival at any airport in the world as one clean row per flight — with live status, terminal, gate, estimated time and delay in minutes attached. Codeshare duplicates are folded into the flight that actually operates, so the 2,861 board entries a day at JFK publishes become the 656 real movements they represent. Built for airport dashboards, travel apps, on-time-performance analysts and operations teams who need a real movement list rather than a screenshot of a screen.

### What you can do with it

- **Power a live board** in your own app, website or lobby screen — every flight, every status, refreshed as often as you like.
- **Measure on-time performance** for an airport, an airline or a route, day after day, using published scheduled and estimated times.
- **Watch disruption as it happens** — switch on *Only disrupted flights* and get just the cancellations, diversions and anything running fifteen minutes or more behind.
- **Map an airport's real network** — which airlines fly where, how often, and at what times of day, from 160 destinations in a single day at a large hub.
- **Feed an operations or ground-handling model** with terminal and gate assignments alongside scheduled and estimated times.
- **Compare hubs** by collecting several airports in one go, in both directions, on the same day.

### What you get

One row per flight movement. A real record, abridged:

```json
{
  "airportIata": "JFK",
  "airportName": "New York John F. Kennedy International Airport",
  "airportTimeZone": "America/New_York",
  "direction": "departures",
  "localDate": "2026-08-16",
  "airline": "JetBlue",
  "airlineIata": "B6",
  "flightNumber": "B6423",
  "codeshares": [
    { "airlineIata": "AT", "airline": "Royal Air Maroc", "flightNumber": "AT9538" },
    { "airlineIata": "EY", "airline": "Etihad Airways", "flightNumber": "EY8341" }
  ],
  "codeshareCount": 2,
  "counterpartAirport": "LAX",
  "counterpartAirportName": "Los Angeles International Airport",
  "counterpartCity": "Los Angeles",
  "scheduledTimeUtc": "2026-08-16T17:10:00.000Z",
  "scheduledTimeLocal": "13:10",
  "estimatedTimeUtc": "2026-08-16T19:30:00.000Z",
  "delayMinutes": 140,
  "status": "DELAYED",
  "terminal": "5",
  "gate": "519",
  "isDiverted": false,
  "statusSource": "live-status",
  "collectedAt": "2026-08-16T19:14:31.932Z"
}
```

### Input reference

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `airports` | array of strings | `["JFK"]` | The airports you want, as three-letter airport codes. City codes such as NYC or LON are refused, with the individual airports suggested instead. Up to 25 in one run. |
| `direction` | string | `departures` | `departures`, `arrivals`, or `both`. Choosing both collects each airport twice, once in each direction. |
| `date` | string | today | The day you want, written as `2026-08-16`, in the airport's own local time. Leave empty for today at each airport. |
| `fullDay` | boolean | `true` | On, you get every movement from midnight to midnight at the airport. Off, you get only the flights around right now — much smaller and quicker. |
| `includeStatus` | boolean | `true` | Attaches live status, terminal, gate, estimated time and delay to every flight that is currently published. Charged separately — see Pricing. |
| `deduplicateCodeshares` | boolean | `true` | Folds a flight sold by several airlines under different numbers into one row for the aircraft that flies, listing the others in `codeshares`. |
| `onlyDisrupted` | boolean | `false` | Keeps only flights that are cancelled, diverted, or fifteen minutes or more behind schedule. Needs `includeStatus` on. |
| `language` | string | `en-GB` | The language airport and airline names come back in. Codes, numbers, times and statuses are the same in every language. |
| `maxRows` | integer | `2000` | The most flights returned for each airport and direction. Maximum 10,000. |
| `parallelAirports` | integer | `4` | How many airports are collected at the same time, 1 to 8. |

### Output fields

| Field | Type | Description |
| --- | --- | --- |
| `airportIata` | string | The airport whose board this row came from. |
| `airportName`, `airportCity`, `airportCountry` | string | That airport's full name, city and country. |
| `airportTimeZone` | string | The airport's time zone, so local times can be turned into instants. |
| `direction` | string | `departures` or `arrivals`. |
| `localDate` | string | The flight's date in the airport's own local time. |
| `localDateIsExact` | boolean | False in the rare case where the airport's time zone is not known. |
| `airline`, `airlineIata` | string | The airline that actually operates the flight. |
| `flightNumber` | string | The operating flight's designator, e.g. `B6423`. |
| `operatedBy` | string | null | The regional partner flying it on the airline's behalf, where one is named. |
| `isCodeshare` | boolean | True only for a marketing-only row, which happens when codeshares are not being combined. |
| `codeshares` | array | The other airlines selling this same flight, each with its own number. |
| `codeshareCount` | integer | How many of them there are. |
| `counterpartAirport` | string | The other end: the destination on a departure, the origin on an arrival. |
| `counterpartAirportName`, `counterpartCity`, `counterpartCountry` | string | That airport's full name, city and country. |
| `scheduledTimeUtc` | string | Scheduled time of the movement at this airport, in UTC. |
| `scheduledTimeLocal` | string | The same moment on the airport's own clock, `HH:MM`. |
| `counterpartScheduledTimeLocal` | string | null | The scheduled time at the other end, on that airport's clock. |
| `estimatedTimeUtc` | string | null | The latest time published for this movement: the estimate while it is still to go, the actual time once the source has published one. Never guessed. |
| `delayMinutes` | integer | null | That time minus the scheduled time, in minutes. Negative when the flight is running early. `null` when the source publishes no such time. |
| `delayLooksImplausible` | boolean | null | True when the published estimate implies a delay of more than twelve hours, which usually means a stale schedule. The number is still given to you unchanged. `null` when there is no delay to judge. |
| `status` | string | null | `SCHEDULED`, `DELAYED`, `OUT_GATE`, `IN_AIR`, `LANDED`, `IN_GATE`, `CANCELLED` or `DIVERTED`. `null` where none is published. |
| `statusText` | string | null | The status as words, where the source gives one. |
| `statusSource` | string | null | Where the status came from, so you always know whether a row is a live one. |
| `terminal`, `gate` | string | null | Terminal and gate at this airport, where published. |
| `isDiverted` | boolean | null | True when the flight is going somewhere other than where it was scheduled, false when a source that publishes diversions says it is not, and `null` when nothing has been published about this flight either way. Most of a whole day is `null`, because that is the same part of the day that has no live status. |
| `divertedToAirport` | string | null | Where it is actually going, when that is different. |
| `flightId` | string | null | The source's own identifier for this exact flight instance. |
| `dataSource` | string | `primary`, or `backup` when the main list was unavailable and a shorter one was used. |
| `language` | string | The language you asked for. |
| `collectedAt` | string | When this row was collected. |
| `movementKey` | string | A stable key for one physical flight, useful for joining runs day over day. |

### Pricing

You pay for what you get, with no monthly fee.

- **Flight movement collected — $1.20 per 1,000 rows** ($0.0012 each). One charge per flight in your dataset.
- **Live status added — $0.020 per airport and direction.** Charged once for each airport and direction where status, terminals, gates and delays were actually attached to at least one flight on the board. It is never charged per flight. It is not charged when nothing could be attached, not charged for a day that is already over (the live layer publishes nothing for one, so none is collected), and not charged at all when `includeStatus` is off.

**Worked example.** One whole day of departures at New York JFK returned **656 movements** with live status attached. That is 656 × $0.0012 = **$0.79**, plus one live-status charge of $0.02 — **$0.81 for the day**.

Five airports, both directions, whole day, around 600 movements each: 6,000 rows = $7.20, plus ten live-status charges = $0.20. **$7.40 for the run.**

Leaving *Combine codeshare duplicates* off will roughly quadruple your row count, because about three quarters of what an airport publishes is the same aircraft sold under other airlines' numbers. It is on by default for that reason.

### Limits & what this actor cannot do

- **A board is a snapshot.** Statuses, gates and estimates change continuously, and what you collect is what was published at that moment.
- **Live status covers roughly from now forwards, not the whole day.** The live layer publishes about a day and a half ahead of the current moment. Collecting a whole day therefore gives every flight its schedule but gives live status only to those still inside that window — at JFK, 371 of 656 movements on the day tested. Flights earlier in the day come back with `status: null` and `statusSource: null` rather than a guessed status. Turn `fullDay` off if you want the highest proportion of live rows.
- **The movement list itself carries no status and no delay.** Everything operational — status, terminal, gate, estimate, delay — comes from the live layer, and every row records that in `statusSource`. Nothing is ever inferred from a schedule.
- **Aircraft type is not published on airport boards** by any source this actor uses, so there is no aircraft column. It is not omitted by accident.
- **A day that has already ended gets no live status.** The live layer publishes from now forwards only, so for a past day every row comes back with `status: null` and `statusSource: null`, no live status is collected, and no live-status charge is made.
- **A day you asked for is never swapped for another one.** If the movements for the day you named cannot be collected, that board is reported as a failed board in the run summary. It is never filled in with a different day's flights.
- **About a week back and a couple of days ahead.** Days outside that are moved to the nearest published day and you are told so in the log. The last day ahead is usually only partly filed.
- **Some airports genuinely publish nothing on some days.** A small regional airport with no flights that day is reported as an empty board, which is an answer, not an error.
- **A board the source refuses to serve is reported as a failed board, never as an airport with no flights.** The run summary counts boards read, boards genuinely empty, boards the source does not publish, and boards that failed, as four separate numbers, and a run where nothing at all could be read ends as a failed run.
- **This actor reports what is published to the public.** It cannot book, hold a seat, check anyone in, or tell you why a flight is late.
- **It does not predict.** There is no forecast column; every time is either a published schedule or a published estimate.
- **Very rarely, an airport publishes a flight twice** under two separate filings of the same number. Both are kept, each with its own `flightId`, rather than one being silently dropped.
- **Speed depends on the size of the job** and on how quickly the sources answer. No fixed throughput is promised.
- **The sources' terms govern automated access.** You are responsible for using the data lawfully and in line with those terms.

### FAQ

**Do I need an account on the source site?**
No. Nothing is needed from you beyond the airports you want.

**Does it need my login or password?**
No. This actor never asks for credentials of any kind.

**Can I schedule it?**
Yes. Schedule it hourly for a live board, or once a day to build an on-time-performance history. `movementKey` is stable, so day-over-day joins are straightforward.

**Are these statuses live?**
They are as published at the moment of collection, and they can lag the aircraft. Rows carrying a live status say so in `statusSource`; rows without one leave `status` empty rather than guessing.

**Why do I get fewer rows than the number of flights shown on the airport's own screens?**
Because those screens list every airline selling a seat. A single aircraft to Los Angeles can appear three times under three different airlines. This actor gives you one row for the aircraft and lists the other airlines in `codeshares`. Switch *Combine codeshare duplicates* off if you want the raw list.

**What happens if an airport can't be collected?**
It is reported as a failed board in the run summary, with the other airports still delivered. It is never reported as an airport with no flights.

**Can I get only the delays and cancellations?**
Yes — switch on *Only disrupted flights*. Rows that are filtered out are never written and never charged for, so the row charge applies only to the flights you keep. The live-status charge still applies as usual, because that switch needs live status turned on.

# Actor input Schema

## `airports` (type: `array`):

The airports whose boards you want, as three-letter airport codes such as JFK, LHR or DXB. City codes like NYC or LON are not accepted, because a board belongs to one airport — list the individual airports instead. Up to 25 airports in one run.

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

Which board to collect. Choosing both collects each airport twice, once in each direction.

## `date` (type: `string`):

The day you want, written as 2026-08-16, in the airport's own local time. Leave it empty for today at each airport. The source publishes roughly a week of past days and a couple of days ahead; anything outside that is moved to the nearest day that is published and you are told in the log.

## `fullDay` (type: `boolean`):

On, the actor collects every movement from midnight to midnight at the airport. Off, it returns only the flights around right now, which is faster and much smaller.

## `includeStatus` (type: `boolean`):

On, every flight that is currently published gets its live status, terminal, gate, estimated time and delay in minutes attached. This is charged separately, once for each airport and direction where status is actually attached to at least one flight — see Pricing.

## `deduplicateCodeshares` (type: `boolean`):

On, a flight sold by several airlines under different numbers becomes ONE row for the aircraft that actually flies, with the other airlines listed in a codeshares column. Around three quarters of raw board entries are these duplicates, so leaving this off will roughly quadruple your row count and your bill.

## `onlyDisrupted` (type: `boolean`):

Keep only flights that are cancelled, diverted, or running fifteen minutes or more behind schedule. Filtered-out flights are never written and never charged for. Needs live status to be switched on, which carries its own charge per airport and direction.

## `language` (type: `string`):

The language airport and airline names come back in. Flight numbers, codes, times and statuses are the same in every language.

## `maxRows` (type: `integer`):

The most flights to return for each airport and direction. A very large hub can publish well over a thousand movements in a day, so raise this if you want all of them and lower it to keep a run small.

## `parallelAirports` (type: `integer`):

How many airports are collected in parallel. Leave this alone unless a run is being throttled; higher is not always faster.

## Actor input object example

```json
{
  "airports": [
    "JFK"
  ],
  "direction": "departures",
  "date": "2026-08-16",
  "fullDay": true,
  "includeStatus": true,
  "deduplicateCodeshares": true,
  "onlyDisrupted": false,
  "language": "en-GB",
  "maxRows": 2000,
  "parallelAirports": 4
}
```

# Actor output Schema

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

One row per flight, with live status, terminal, gate and codeshares where published.

## `flightsCsv` (type: `string`):

The same movements as a spreadsheet-ready file.

## `overview` (type: `string`):

The movements shown as a table with the columns most people want first.

## `runSummary` (type: `string`):

What was read, what was empty, what could not be read, and how many rows each board produced.

# 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 = {
    "airports": [
        "JFK"
    ],
    "direction": "departures",
    "language": "en-GB",
    "maxRows": 2000,
    "parallelAirports": 4
};

// Run the Actor and wait for it to finish
const run = await client.actor("apt_marble/airport-departures-arrivals-board-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 = {
    "airports": ["JFK"],
    "direction": "departures",
    "language": "en-GB",
    "maxRows": 2000,
    "parallelAirports": 4,
}

# Run the Actor and wait for it to finish
run = client.actor("apt_marble/airport-departures-arrivals-board-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 '{
  "airports": [
    "JFK"
  ],
  "direction": "departures",
  "language": "en-GB",
  "maxRows": 2000,
  "parallelAirports": 4
}' |
apify call apt_marble/airport-departures-arrivals-board-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,apt_marble/airport-departures-arrivals-board-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/fbPjnJ6wHZa8rrPfh/builds/KNfG1UKpSky6kltDc/openapi.json
