Free airport API
Every airport, city, country and busy route on this site is also a JSON file under https://airportmiles.com/api/v1. The files are static: fetch them like any web page, from a browser, a server or an AI agent. Built from open data (OurAirports, Wikidata, US DOT / BTS, Eurostat, and the national offices of Canada, Brazil, Chile, Australia, Korea, Japan and India) and refreshed monthly; this version was built on 14 October 2026.
Files
| Path under /api/v1 | What |
|---|---|
| /index.json | Every code and slug that has a file, the file URL templates, counts |
| /airports/{iata}.json | One of 4,008 airports: codes, names, position, runways, passengers, nonstop routes with km and passengers, airlines, nearby airports |
| /routes/{iata}-{iata}.json | One of 2,785 busy routes (codes in alphabetical order): km, miles, nautical miles, flight time, EU261 / UK261 band, CO2, airlines, aircraft, passengers by month; on US domestic routes, average fares paid and a fare outlook; on Brazilian domestic routes, fares sold |
| /cities/{slug}.json | One of 70 cities with several airports: the airports, the city code, distances between them |
| /countries/{slug}.json | One of 233 countries: every airport with scheduled flights, busiest first |
| /openapi.json | OpenAPI 3.1 description, for code generators and AI agents |
Codes and slugs are lowercase. A route file exists only for the pairs in index.json; the whole airport list as one file is the CC0 dataset (JSON and CSV).
Quick start
Shell
curl https://airportmiles.com/api/v1/airports/lhr.jsonJavaScript (browser or Node)
const res = await fetch('https://airportmiles.com/api/v1/routes/jfk-lhr.json')
const route = await res.json()
console.log(route.distance.km, route.flightTime.estimateMin)Python
import requests
airport = requests.get('https://airportmiles.com/api/v1/airports/lhr.json',
headers={'User-Agent': 'my-app/1.0'}).json()
print(airport['airport']['icao'], len(airport['routes']))What a file looks like
/api/v1/airports/lju.json, shortened:
{
"api": "AirportMiles API v1",
"generated": "2026-10-14",
"docs": "https://airportmiles.com/api",
"self": "https://airportmiles.com/api/v1/airports/lju.json",
"page": "https://airportmiles.com/airport/lju",
"licence": "CC0-1.0",
"licenceNote": "Fields a source in \"sources\" covers keep that source's licence: name the source when you reuse them.",
"airport": {
"iata": "LJU",
"icao": "LJLJ",
"name": "Ljubljana Jože Pučnik Airport",
"municipality": "Zgornji Brnik",
"city": "Ljubljana",
"multiAirportCity": null,
"country": {
"code": "SI",
"name": "Slovenia",
"self": "https://airportmiles.com/api/v1/countries/slovenia.json",
"page": "https://airportmiles.com/airports/slovenia"
},
"region": "Cerklje na Gorenjskem Municipality",
"continent": "EU",
"latitude": 46.2237,
"longitude": 14.4576,
"elevationM": 388,
"size": "large",
"wikidata": "Q1109558",
"website": "http://www.lju-airport.si/",
"wikipedia": "https://en.wikipedia.org/wiki/Ljubljana_Jo%C5%BEe_Pu%C4%8Dnik_Airport",
"opened": 1963,
"operator": [
"Fraport"
],
"runways": [
{
"ids": "12/30",
"lengthM": 3300,
"widthM": 45,
"surface": "Asphalt",
"lighted": true
}
]
},
"passengers": {
"count": 1591308,
"previousYear": 1437662,
"period": "2025",
"source": "eurostat",
"yearToDate": {
"months": "2026-01…2026-07",
"count": 998794,
"sameMonthsYearBefore": 870784
}
},
"routes": [
{
"to": {
"iata": "IST",
"icao": "LTFM",
"name": "İstanbul Airport",
"city": "Istanbul",
"country": "TR",
"latitude": 41.2749,
"longitude": 28.7321,
"self": "https://airportmiles.com/api/v1/airports/ist.json"
},
"km": 1272,
"passengers": 190788,
"nationalSource": null,
"self": null,
"page": null
},
{
"to": {
"iata": "FRA",
"icao": "EDDF",
"name": "Frankfurt Main Airport",
"city": "Frankfurt",
"country": "DE",
"latitude": 50.0267,
"longitude": 8.5584,
"self": "https://airportmiles.com/api/v1/airports/fra.json"
},
"km": 609,
"passengers": 137259,
"nationalSource": null,
"self": null,
"page": null
},
"… 18 more"
],
"airlines": null,
"punctuality": null,
"cityAirports": [],
"nearby": [
{
"iata": "KLU",
"icao": "LOWK",
"name": "Klagenfurt Airport",
"city": "Klagenfurt am Wörthersee",
"country": "AT",
"latitude": 46.6425,
"longitude": 14.3377,
"self": "https://airportmiles.com/api/v1/airports/klu.json",
"km": 47
}
],
"sources": [
{
"id": "ourairports",
"name": "OurAirports",
"url": "https://ourairports.com/data/",
"licence": "Public domain",
"fields": [
"iata",
"icao",
"name",
"municipality",
"country",
"region",
"latitude",
"longitude",
"elevationM",
"size",
"runways",
"website",
"wikipedia"
]
},
"… 4 more"
]
}Any two airports
Route files cover the busiest pairs with traffic data. For any other pair, take both airports' coordinates from their files and compute the distance; the calculator does the same in the browser, with stops.
// Any two airports: great circle on a sphere (R = 6,371 km), the EU261 method.
// Within about 0.5% of the WGS-84 distance the route files carry.
const rad = d => d * Math.PI / 180
function km(a, b) {
const h = Math.sin(rad(b.latitude - a.latitude) / 2) ** 2
+ Math.cos(rad(a.latitude)) * Math.cos(rad(b.latitude)) * Math.sin(rad(b.longitude - a.longitude) / 2) ** 2
return 2 * 6371 * Math.asin(Math.sqrt(h))
}Licence and fair use
- Airport, city, country and distance fields: CC0, use them for anything, no attribution needed.
- Traffic, punctuality and fare fields keep their source's terms. Each file lists them in
sourceswith the fields they cover: US DOT / BTS is public domain, JAC (Chile) CC0; Eurostat, UK DESNZ, Statistics Canada, ANAC, Incheon Airport, Korea Airports Corporation, BITRE, MLIT and DGCA figures need the source named. Details on Sources. - No time zones: their source (timezone-boundary-builder) is under the ODbL, so they stay on the pages.
- No key and no quota. Cache what you fetch, keep requests reasonable, and send a User-Agent that names your app. v1 keeps its fields; a breaking change would come as v2.
Questions or a field you need: [email protected].