US Airline On-Time & Flight Delay Statistics by Route (BTS) avatar

US Airline On-Time & Flight Delay Statistics by Route (BTS)

Pricing

from $4.40 / 1,000 route climatology results

Go to Apify Store
US Airline On-Time & Flight Delay Statistics by Route (BTS)

US Airline On-Time & Flight Delay Statistics by Route (BTS)

Historical US flight delay, cancellation and on-time statistics from official BTS data. Rollups by route, airline or airport and month with full delay-cause breakdowns — carrier, weather, NAS, security, late aircraft — honest denominators, cancellation causes and data-vintage on every row.

Pricing

from $4.40 / 1,000 route climatology results

Rating

0.0

(0)

Developer

Kyle Maloney

Kyle Maloney

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Airline On-Time & Delay-Cause Climatology (BTS)

Historical US airline on-time performance, flight delay statistics and delay-cause decomposition by route, airline or airport — computed straight from the US Department of Transportation's official BTS TranStats "Reporting Carrier On-Time Performance (1987-present)" monthly bulk files. Keyless, no scraping, no API key.

Real-time trackers tell you a flight is late today. This actor answers the question the money actually rides on: how often is this route/airline/airport late, how bad is it when it is late, and WHY — air-carrier problems vs extreme weather vs National Aviation System congestion vs security vs late-inbound aircraft — month by month, from the government dataset the airlines themselves are legally required to file. Nobody else on the Store sells the historical, cause-decomposed rollup.

Who this is for

  • Air-passenger-rights claim firms (EU261 / ANAC / DOT refunds): establish whether a delay pattern on a route is habitual and carrier-caused (compensable operational issues) or weather/NAS-driven — with the official CancellationCode (A=carrier, B=weather, C=NAS, D=security) counted per month.
  • Travel insurers and underwriting analysts: price delay/cancellation cover per route and season from actual disruption base rates, not anecdotes.
  • Corporate travel managers and TMCs: pick the reliably-on-time carrier and connection airport per city pair before signing preferred-carrier deals.
  • Journalists and analysts: "which airline blames weather the most", "worst day at EWR in June", "cancellation causes at DEN" — one run, official numbers.
  • Airport / airline ops and consultants: benchmark on-time performance and the cause mix against competitors, monthly.
  • AI agents (MCP): a clean, typed, chainable rollup tool over a 39-year federal dataset that agents cannot practically download and parse themselves (each month is a ~30 MB zip holding a ~277 MB CSV).

What one run costs — read this first

Pricing is pay-per-result: one dataset row = one (group x month) rollup. At $8 per 1,000 rows a row costs $0.008.

RunTypical rowsTypical cost
One airline, one month, by route (airlines:["WN"])~600 routes~$4.80 (cap with maxResults)
All airlines, 2 months, by airline~26~$0.21
One airport's routes, 2 months (origins:["DEN"])~200-400~$1.60-$3.20
One route, 12 months (origins:["DEN"], dests:["LGA"])12~$0.10
Default prefill (WN routes, 2 months, maxResults 500)500$4.00

maxResults (default 500) is a hard billing cap enforced in one audited code path for every grouping mode. When it bites, rows are dropped smallest-groups-first and every emitted row carries results_truncated: true — a capped answer is never passed off as a complete one. A run that cannot answer (bad input, BTS outage, missing month without allowPartial) fails loudly and bills nothing.

A single run is capped at 12 months (each month is a ~30 MB download from BTS). Split longer histories across runs.

Example input

{
"sinceMonth": "2026-05",
"untilMonth": "2026-06",
"airlines": ["WN"],
"groupBy": "route",
"minFlights": 30,
"maxResults": 500
}

Or a single route across a year, all carriers:

{
"sinceMonth": "2025-07",
"untilMonth": "2026-06",
"origins": ["DEN"],
"dests": ["LGA"],
"groupBy": "airline_route",
"minFlights": 10,
"maxResults": 100
}

Leave sinceMonth/untilMonth blank and the run defaults to the two most recent published months — discovered live: BTS publishes with a ~2-3 month lag, and the actor probes backward from the current month until a file answers. The month it lands on is reported on every row as latest_month_available.

Grouping modes

groupByOne row perUse it for
route (default)origin-dest x month, across airlines"How is DEN-LGA overall?"
airlinereporting carrier x monthLeague tables, carrier benchmarking
origin_airportdeparture airport x monthAirport ops, connection choice
dest_airportarrival airport x monthInbound reliability
airline_routecarrier x origin-dest x monthClaims and carrier-level route analysis

Output fields (the important ones)

Every row is one (group x month). All fields are nullable and documented in the dataset schema; the key ones:

  • Identity: group_by, month, airline, origin, dest, route, airlines_included
  • Volumes: total_flights (scheduled, incl. cancelled/diverted), completed_flights, cancelled_flights, diverted_flights, arr_assessed_flights (the arrival-performance denominator)
  • Rates: on_time_rate_pct, arr_delay_rate_pct (>=15 min, over assessed flights), cancellation_rate_pct, disrupted_rate_pct (late + cancelled + diverted over ALL scheduled flights — the passenger's-eye number)
  • Severity: avg_arr_delay_all_min (early arrivals count negative), avg_arr_delay_delayed_only_min, avg_dep_delay_all_min
  • Cancellation causes: cancelled_carrier_count (code A), cancelled_weather_count (B), cancelled_nas_count (C), cancelled_security_count (D), cancelled_unknown_code_count
  • Delay-cause decomposition: carrier_delay_minutes, weather_delay_minutes, nas_delay_minutes, security_delay_minutes, late_aircraft_delay_minutes, total_cause_delay_minutes, and the five *_share_pct columns — with the denominator published on the row (delayed_flights_with_cause_data / delayed_flights_without_cause_data, see the honesty section below)
  • Worst day: worst_day (YYYY-MM-DD) and worst_day_disrupted_flights
  • Provenance & completeness: months_requested, months_fetched, months_missing, data_complete, month_rows_scanned, groups_suppressed_below_min_flights, results_truncated, latest_month_available, data_source (the exact BTS zip URL), data_vintage (the file's HTTP Last-Modified — BTS revises files in place), retrieved_at

How the numbers stay honest

This dataset has traps, and the actor is built around them rather than on top of them:

  • Blank is not zero. BTS populates the five delay-cause columns ONLY on flights that arrived 15+ minutes late. Measured on the live May-2026 file: exactly the 129,511 delayed flights of 611,735 carry all five; every other row carries five blanks. A naive Number(x) || 0 would fabricate ~482,000 zero-cause flights per month. Here a blank is null, cause columns are null (never 0) for a group with no cause data, and every row publishes its own cause denominator.
  • Cancelled and diverted flights carry NO arrival-delay figures (measured: all 5,655 cancelled and all 1,734 diverted rows have blank ArrDel15). They are counted in their own columns and excluded from arrival-performance denominators — disrupted_rate_pct is the all-in view.
  • Delay-cause data only exists from June 2003. A pre-2003 month still rolls up delays and cancellations correctly; its cause columns read null and delayed_flights_without_cause_data says why.
  • A live drift gate runs before anything billable, per month: the real header must contain every column this actor reads (a renamed column fails the run, never emits nulls); the file must hold >=50,000 rows, >=5 carriers and >=100 origin airports; at least one pinned mega-hub (ATL/ORD/DFW/DEN) must appear as an origin; the file's own Year/Month columns must match the month requested (a stale or mislabeled file fails instead of billing); and a negative-control carrier code must match zero rows.
  • A missing month never silently vanishes. If BTS hasn't published a requested month (or the download fails), the run FAILS by default with the per-month status. Opt in with allowPartial: true and every row then reports months_missing and data_complete: false.
  • Cheap trap coverage: the BTS 404 page is HTML at a real 404; a maintenance page at HTTP 200 is detected by byte shape (a zip must start with PK) and treated as an outage, never parsed. The zip's month is probed with a ranged GET, not HEAD. The zip's CSV entry is selected by extension because the archive also contains a readme.html.

Use as an MCP tool

This actor works as a chainable MCP tool via https://mcp.apify.com — the input schema is fully described, month strings are validated with actionable error messages (an agent sending "Jan 2026" or "2026-13" gets a named reason, not a silent empty dataset), and a filter that matches nothing fails with the list of carrier codes actually present in the window. Billing is unchanged when called as a tool; a run that cannot answer fails without billing.

Example agent prompt: "Using the BTS on-time climatology tool, compare Southwest and United on Denver routes for the last two published months, grouped by airline_route, and tell me which routes have the worst late-aircraft share."

FAQ

How fresh is the data? BTS publishes with a ~2-3 month lag (measured 2026-08-31: latest file is June 2026, published mid-August). The run discovers the latest month live and reports it on every row. BTS also revises files in place — data_vintage carries each file's Last-Modified.

Which airlines are covered? The carriers required to report on-time data — 13 as of 2026-05: AA, AS, B6, DL, F9, G4, MQ, NK, OH, OO, UA, WN, YX. Regional partners report under their own codes (OO = SkyWest, MQ = Envoy, OH = PSA, YX = Republic).

Is this per-flight data? No — this is the climatology rollup (one row per group per month). If you need raw per-flight records, download the BTS PREZIP file directly; it is free.

Can I get 5 years of history? Yes, 12 months per run (each month is a ~30 MB federal download). Loop runs across windows; months back to 1987-10 work, with cause columns starting June 2003.

Why do on_time_rate + delay_rate not include cancellations? They are arrival-performance rates over flights that actually arrived, matching how DOT reports them. disrupted_rate_pct gives the all-in probability a scheduled flight was late, cancelled or diverted.

What do the delay-cause shares mean? Shares of cause-attributed delay minutes on flights that arrived 15+ min late, as filed by the carriers under DOT rules: carrier (maintenance, crew, ops), extreme weather, NAS (airspace congestion, non-extreme weather, airport ops), security, late inbound aircraft. The denominator is on the row.

Data source

US DOT Bureau of Transportation Statistics, TranStats "Reporting Carrier On-Time Performance (1987-present)", monthly PREZIP bulk files at transtats.bts.gov/PREZIP/ — public domain, keyless. Each output row names the exact source zip (data_source) and its Last-Modified (data_vintage).