はじめに
FlightAware AeroAPIとは、航空便データをJSONで取得できるAPIです。
AeroAPI 4.28.0
AeroAPIは、ソフトウェア開発者がFlightAwareの様々なフライトデータにアクセスできる、シンプルなクエリベースのAPIです。ユーザーは現在データや履歴データを取得できます。
AeroAPIは正確で実用的な航空データを提供するRESTful APIです。Foresight™の導入により、顧客は米国における予測航空会社到着時刻(ETA)の半数以上を支えるデータにアクセスできます。
カテゴリ
AeroAPIは、検索しやすくするためにいくつかのカテゴリに分類されています。
- フライト:概要情報、計画ルート、位置情報など
- Foresight:FlightAware Foresight™で強化されたフライト位置情報
- 空港:空港情報とFIDSスタイルのリソース
- 運航会社:運航会社情報と機材稼働状況リソース
- アラート:フライトアラートと配信先設定
- 履歴:各種エンドポイント向け過去フライトデータ
- その他:フライト遅延情報、将来のスケジュール情報、航空機所有者情報
- アカウント:AeroAPI利用統計
今回は、HND(羽田空港)を例に、今日の便一覧と路線スケジュールを取る2パターンを紹介します。
パターン① 空港発着便リスト
/airports/{id}/flights/departures と /arrivals
用途:
- 「今日の羽田発着全便をCSVにまとめたい」
- 「運航実績や遅延を含めて知りたい」
特徴:
- 直近3日間程度のデータを取得可能
- 実際の運航ステータスや機材が取れる
Pythonサンプルコード(CSV出力付き)
使い方
-
今日のRJTT:python flight_list.py
-
指定日のRJTT:python flight_list.py 2025-10-05
-
指定日・指定空港:python flight_list.py 2025-10-05 RJOI
import sys
import csv
import time
import random
import requests
import datetime as dt
import zoneinfo
from urllib.parse import urlencode
API_KEY = "YOUR_AEROAPI_KEY"
BASE = "https://aeroapi.flightaware.com/aeroapi"
# 任意で空港ラベルを追加してください
AIRPORT_LABELS = {
"RJTT": "Tokyo Haneda",
"RJOI": "Iwakuni",
"RJAA": "Tokyo Narita",
"ROAH": "Naha",
}
# レート制限対策:ページ間/窓間のウェイトやリトライ回数
PAGE_SLEEP_SEC = 0.3
WINDOW_SLEEP_SEC = 1.0
MAX_RETRIES = 7
# JSTでの時間窓(初期は4分割)
WINDOWS_JST = [
(dt.time(0, 0, 0), dt.time(5, 59, 59)),
(dt.time(6, 0, 0), dt.time(11, 59, 59)),
(dt.time(12, 0, 0), dt.time(17, 59, 59)),
(dt.time(18, 0, 0), dt.time(23, 59, 59)),
]
def parse_args():
"""
使い方:
python flight_list.py → 今日, 既定空港(RJTT)
python flight_list.py 2025-10-05 → 指定日, 既定空港(RJTT)
python flight_list.py 2025-10-05 RJTT → 指定日, 指定空港
"""
tz_jst = zoneinfo.ZoneInfo("Asia/Tokyo")
target_date = dt.datetime.now(tz_jst).date()
airport_id = "RJTT"
if len(sys.argv) >= 2:
a1 = sys.argv[1]
# YYYY-MM-DD 判定
try:
y, m, d = map(int, a1.split("-"))
target_date = dt.date(y, m, d)
if len(sys.argv) >= 3:
airport_id = sys.argv[2].upper()
except ValueError:
# 1引数目が日付でなければ空港コードとして扱う
airport_id = a1.upper()
return target_date, airport_id
def to_utc_iso(jst_date, t0, t1, tz_jst):
s_jst = dt.datetime.combine(jst_date, t0, tzinfo=tz_jst)
e_jst = dt.datetime.combine(jst_date, t1, tzinfo=tz_jst)
s_utc = s_jst.astimezone(dt.timezone.utc).isoformat().replace("+00:00", "Z")
e_utc = e_jst.astimezone(dt.timezone.utc).isoformat().replace("+00:00", "Z")
return s_utc, e_utc
def request_with_backoff(url, headers, params=None, max_retries=MAX_RETRIES, stage=""):
"""
429/5xx をリトライ。Retry-After があれば尊重、なければ指数バックオフ+ジッター。
"""
attempt = 0
while True:
attempt += 1
if attempt > 1:
print(f" retry {attempt-1}/{max_retries} ({stage})")
r = requests.get(url, headers=headers, params=params, timeout=30)
sc = r.status_code
print(f"HTTP {sc}")
if sc == 200:
return r.json()
if sc in (429, 500, 502, 503, 504) and attempt <= max_retries:
ra = r.headers.get("Retry-After")
if ra:
try:
sleep_sec = float(ra)
except Exception:
sleep_sec = 5.0
else:
base = 2 ** (attempt - 1)
sleep_sec = base + random.uniform(0, 0.5 * base)
print(f" backoff {sleep_sec:.1f}s (stage={stage})")
time.sleep(sleep_sec)
continue
r.raise_for_status()
def resolve_next_url(next_raw, base_endpoint):
"""
links.next を安全に絶対URL化。/aeroapi を必ず保持。
想定される形式:
- 絶対: https://aeroapi.flightaware.com/aeroapi/airports/...
- 先頭?: ?cursor=xxx
- 先頭/: /airports/... or /aeroapi/airports/...
- 相対: airports/...
"""
if not next_raw:
return None
if next_raw.startswith("http"):
return next_raw
if next_raw.startswith("?"):
return f"{BASE}{base_endpoint}{next_raw}"
if next_raw.startswith("/"):
if next_raw.startswith("/aeroapi/"):
return f"https://aeroapi.flightaware.com{next_raw}"
return f"https://aeroapi.flightaware.com/aeroapi{next_raw}"
return f"{BASE}/{next_raw.lstrip('/')}"
def fetch_all_window(endpoint, key_name, start_utc, end_utc, headers, page_limit=100):
"""
1つの時間窓で全ページ取得(到着・出発兼用)
"""
params = {"start": start_utc, "end": end_utc}
url = f"{BASE}{endpoint}"
page = 0
items = []
while url and page < page_limit:
page += 1
q = f"?{urlencode(params)}" if page == 1 else ""
print(f"\n--- Fetching {key_name} page {page} ---")
print(f"URL: {url}{q}")
data = request_with_backoff(url, headers, params if page == 1 else None, stage=f"{key_name} p{page}")
chunk = data.get(key_name, [])
print(f"{key_name}件数(this page): {len(chunk)}")
items.extend(chunk)
next_raw = (data.get("links") or {}).get("next")
print(f"next(raw): {next_raw}")
url = resolve_next_url(next_raw, endpoint) if next_raw else None
time.sleep(PAGE_SLEEP_SEC)
print(f"合計 {key_name}件数(window): {len(items)}")
return items
def main():
tz_jst = zoneinfo.ZoneInfo("Asia/Tokyo")
target_date, airport_id = parse_args()
airport_label = AIRPORT_LABELS.get(airport_id, airport_id)
headers = {"x-apikey": API_KEY}
print(f"=== {airport_label} ({airport_id}) Flight List (paged + windowed) ===")
print(f"対象日(JST): {target_date}")
# 窓ごとに「出発→到着」を交互に叩いてレート分散
collected_dep = []
collected_arr = []
for i, (t0, t1) in enumerate(WINDOWS_JST, 1):
s_utc, e_utc = to_utc_iso(target_date, t0, t1, tz_jst)
print(f"\n=== departures window {i}/{len(WINDOWS_JST)} ===")
print(f"JST {t0}–{t1} → UTC {s_utc}–{e_utc}")
dep_part = fetch_all_window(f"/airports/{airport_id}/flights/departures", "departures",
s_utc, e_utc, headers)
collected_dep.extend(dep_part)
print(f"\n=== arrivals window {i}/{len(WINDOWS_JST)} ===")
print(f"JST {t0}–{t1} → UTC {s_utc}–{e_utc}")
arr_part = fetch_all_window(f"/airports/{airport_id}/flights/arrivals", "arrivals",
s_utc, e_utc, headers)
collected_arr.extend(arr_part)
time.sleep(WINDOW_SLEEP_SEC)
print(f"\n*** departures total (all windows): {len(collected_dep)} ***")
print(f"*** arrivals total (all windows): {len(collected_arr)} ***")
# 正規化
def g(d, *keys):
cur = d
for k in keys:
cur = (cur or {}).get(k)
return cur
def norm(rec, kind_key):
return {
"kind": "departure" if kind_key == "departures" else "arrival",
"fa_flight_id": rec.get("fa_flight_id"),
"ident": rec.get("ident"),
"operator": rec.get("operator"),
"codeshares": ",".join(rec.get("codeshares", [])) if rec.get("codeshares") else None,
"origin_icao": g(rec, "origin", "code_icao"),
"origin_iata": g(rec, "origin", "code_iata"),
"destination_icao": g(rec, "destination", "code_icao"),
"destination_iata": g(rec, "destination", "code_iata"),
"scheduled_off": rec.get("scheduled_off"),
"estimated_off": rec.get("estimated_off"),
"actual_off": rec.get("actual_off"),
"scheduled_on": rec.get("scheduled_on"),
"estimated_on": rec.get("estimated_on"),
"actual_on": rec.get("actual_on"),
"status": rec.get("status"),
"aircraft_type": rec.get("aircraft_type"),
"route_distance_nm": rec.get("distance"),
}
print("\nデータ正規化中…")
rows = [norm(r, "departures") for r in collected_dep] + [norm(r, "arrivals") for r in collected_arr]
print(f"正規化後レコード: {len(rows)}")
outname = f"{airport_id}_flights_{target_date.strftime('%Y%m%d')}.csv"
cols = list(rows[0].keys()) if rows else [
"kind", "fa_flight_id", "ident", "operator", "codeshares", "origin_icao", "origin_iata",
"destination_icao", "destination_iata", "scheduled_off", "estimated_off", "actual_off",
"scheduled_on", "estimated_on", "actual_on", "status", "aircraft_type", "route_distance_nm"
]
print(f"\nCSV出力: {outname}")
with open(outname, "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=cols)
w.writeheader()
for i, r in enumerate(rows, 1):
w.writerow(r)
if i % 100 == 0:
print(f" ...{i}件書き込み")
print(f"\n✅ 完了: {outname} (総レコード: {len(rows)})")
if __name__ == "__main__":
main()
パターン② フライトスケジュール
/schedules/{date_start}/{date_end}
用途:
- 「ダイヤ(定期便)を調べたい」
- 「来月の羽田→伊丹便の本数を調べたい」
特徴:
- 90日先までの予定ダイヤを取得可能
- 路線(origin+destination)を指定
Pythonサンプルコード
import requests
import csv
import os
API_KEY = "YOUR_AEROAPI_KEY"
BASE = "https://aeroapi.flightaware.com/aeroapi"
headers = {"x-apikey": API_KEY}
url = f"{BASE}/schedules/2025-10-01/2025-10-02"
params = {
"origin": "RJTT", # 羽田
"destination": "RJOO", # 伊丹
}
response = requests.get(url, headers=headers, params=params)
data = response.json()
# スケジュール配列を抽出
flights = data.get("scheduled", [])
# 出力フォルダを作成
os.makedirs("out", exist_ok=True)
outfile = "out/flight_schedules_simple.csv"
# CSV書き出し
with open(outfile, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow([
"ident", "actual_ident", "aircraft_type",
"scheduled_out", "scheduled_in",
"origin_icao", "destination_icao",
"meal_service", "seats_cabin_first",
"seats_cabin_business", "seats_cabin_coach",
])
for flight in flights:
writer.writerow([
flight.get("ident"),
flight.get("actual_ident"),
flight.get("aircraft_type"),
flight.get("scheduled_out"),
flight.get("scheduled_in"),
flight.get("origin_icao"),
flight.get("destination_icao"),
flight.get("meal_service"),
flight.get("seats_cabin_first"),
flight.get("seats_cabin_business"),
flight.get("seats_cabin_coach"),
])
print(f"✅ CSVファイルを保存しました: {outfile} ({len(flights)}件)")
アウトプット
ident,actual_ident,aircraft_type,scheduled_out,scheduled_in,origin_icao,destination_icao,meal_service,seats_cabin_first,seats_cabin_business,seats_cabin_coach
ANA17,,B772,2025-10-01T00:00:00Z,2025-10-01T01:05:00Z,RJTT,RJOO,ファースト: 食事なし / エコノミー: 食事なし,28,0,364
THA6064,ANA17,B772,2025-10-01T00:00:00Z,2025-10-01T01:05:00Z,RJTT,RJOO,ファースト: 食事なし / エコノミー: 食事なし,28,0,364
JAL111,,B788,2025-10-01T00:30:00Z,2025-10-01T01:40:00Z,RJTT,RJOO,ファースト: 食事 / ビジネス: 食事なし / エコノミー: 食事なし,6,58,227
BAW4627,JAL111,B788,2025-10-01T00:30:00Z,2025-10-01T01:40:00Z,RJTT,RJOO,ファースト: 食事 / ビジネス: 食事 / エコノミー: 食事,6,58,227
BKP4150,JAL111,B788,2025-10-01T00:30:00Z,2025-10-01T01:40:00Z,RJTT,RJOO,ファースト: 食事なし / ビジネス: 食事なし / エコノミー: 食事なし,6,58,227
ASA7561,JAL111,B788,2025-10-01T00:30:00Z,2025-10-01T01:40:00Z,RJTT,RJOO,ファースト: 食事なし / ビジネス: 食事なし / エコノミー: 食事なし,6,58,227
HAL5019,JAL111,B788,2025-10-01T00:30:00Z,2025-10-01T01:40:00Z,RJTT,RJOO,ファースト: 食事なし / ビジネス: 食事なし / エコノミー: 食事なし,6,58,227
DLH4854,ANA19,,2025-10-01T01:00:00Z,2025-10-01T02:05:00Z,RJTT,RJOO,ファースト: 食事なし / エコノミー: 食事なし,10,0,260
ANA19,,,2025-10-01T01:00:00Z,2025-10-01T02:05:00Z,RJTT,RJOO,ファースト: 食事なし / エコノミー: 食事なし,10,0,260
AAL8397,JAL113,B788,2025-10-01T01:30:00Z,2025-10-01T02:35:00Z,RJTT,RJOO,ファースト: 食事 / ビジネス: 食事 / エコノミー: 食事,6,58,227
JAL113,,B788,2025-10-01T01:30:00Z,2025-10-01T02:35:00Z,RJTT,RJOO,ファースト: 食事 / ビジネス: 食事なし / エコノミー: 食事なし,6,58,227
HAL5021,JAL113,B788,2025-10-01T01:30:00Z,2025-10-01T02:35:00Z,RJTT,RJOO,ファースト: 食事なし / ビジネス: 食事なし / エコノミー: 食事なし,6,58,227
ASA7563,JAL113,B788,2025-10-01T01:30:00Z,2025-10-01T02:35:00Z,RJTT,RJOO,ファースト: 食事なし / ビジネス: 食事なし / エコノミー: 食事なし,6,58,227
AMX7752,JAL113,B788,2025-10-01T01:30:00Z,2025-10-01T02:35:00Z,RJTT,RJOO,ビジネス: 食事なし / エコノミー: 食事なし,0,38,123
AIC8007,ANA21,,2025-10-01T02:00:00Z,2025-10-01T03:05:00Z,RJTT,RJOO,ファースト: 食事なし / エコノミー: 食事なし,10,0,260
使い分けまとめ
発着便一覧が欲しい → /airports/{id}/flights/departures & arrivals
ダイヤ(予定便)を知りたい → /schedules/{date_start}/{date_end}