What is flying over my house right now?
A small app that shows every few minutes which aircraft are within 25 km of your address, how high, how fast and whether they are climbing or descending, using OpenSky's live positions and without an account.
geef_project("vliegtuigen-boven-je-huis")geef_bron("opensky")geef_bron("schiphol-vluchten")
I have the project, the tested calls and the pitfalls. I'll build a app with OpenSky Network and Schiphol flight data. This is how it could look:
You build this yourself. Copy the prompt into Claude, ChatGPT or Cursor, with the MCP server on, or follow the steps below.
| callsign | dist. | alt. | km/h | hdg | |
|---|---|---|---|---|---|
| RYR275K | 13.6 | 11,400 | 848 | 093° | level |
| TRA16M | 14.6 | 381 | 253 | 057° | desc |
| LIFELN1 | 16.6 | 381 | 193 | 044° | level |
| WMT543 | 24.7 | 10,759 | 847 | 101° | level |
This is a mockup, not a working version, with a real OpenSky reading on Saturday 17 October 2026 at 23:34 around the point in Amstelveen from the project. Turn the MCP server on with the address on For AI.
Who it's for
People living near Schiphol and other airports who want to know what is flying over, children and hobbyists, and local media covering aircraft noise complaints.
What you get
A page (or a widget on a Raspberry Pi with a screen) listing the aircraft within 25 km of your home: callsign, distance, altitude, speed, heading and whether they are climbing, descending or cruising, plus the number of aircraft over the whole country. Click an aircraft for the route it flew today.
Test run on Friday 16 October 2026 at 23:50:54 for a point in Amstelveen, east of Schiphol: 38 aircraft over the Netherlands, 29 of them airborne. Within 25 km: 4 aircraft. Transavia TRA7X descending at 152 metres and 252 km/h, 10.4 km away, TRA81H at 2,934 metres and 510 km/h also descending, and two aircraft on the ground at Schiphol (KLM1682 and THY3EZ). The route of TRA81H came from southern Italy (first point 37.49 N 15.23 E, near Catania). The script used 2 of the 400 free daily credits.
Flights to and from Schiphol: 478 thousand in 2025, 37% more than in 1997
Flights to and from Schiphol, 1997 – 2025, in thousand. The highest point was 2018 (499).
How you could start5 steps
- 1
Request positions inside a box
Without an account OpenSky returns the current state vectors of all aircraft inside a box (lamin, lomin, lamax, lomax in degrees). Each state is a list, not an object: [0] icao24, [1] callsign (padded with spaces to 8 characters), [2] country of registration, [5] longitude, [6] latitude, [7] barometric altitude in m, [8] on ground, [9] speed in m/s, [10] heading, [11] vertical rate in m/s, [13] GPS altitude. Fields can be null. The robots.txt of opensky-network.org returns 403 to everyone; the API itself does answer and is documented at openskynetwork.github.io/opensky-api.
https://opensky-network.org/api/states/all?lamin=52.1&lomin=4.5&lamax=52.5&lomax=5.2
x-rate-limit-remaining: 392 {"time":1792187264,"states":[["4bb101","THY3EZ ","Turkey",1792187253,1792187253,4.7303,52.3232,null,true,10.8,317.81,null,null,null,null,false,0], ["48520b","TRA7X ","Kingdom of the Netherlands",1792187264,1792187264,4.5078,52.209,899.16,false,118.85,90.99,-7.15,null,1036.32,"1000",false,0], ["486258","KLM1682 ","Kingdom of the Netherlands",1792187264,1792187264,4.6969,52.2736,91.44,false,65.93,5… - 2
Count your credits
Anonymously you get 400 credits a day for /states. A box up to 25 square degrees costs 1 credit (the whole Netherlands is 11.9), larger costs 2 to 4. The X-Rate-Limit-Remaining header says how many are left; at 0 you get 429 with X-Rate-Limit-Retry-After-Seconds. Two requests every 5 minutes is 576 a day: too many. So ask every 4 minutes, only during the day, or create a free account (4,000 a day, OAuth2 client credentials).
- 3
Filter on distance and work out climbing or descending
The box is rectangular; then compute the real distance with the haversine formula and keep only what falls within your radius. Vertical rate below -2 m/s = descending, above 2 = climbing. Speed is in m/s: multiply by 3.6 for km/h. Use the GPS altitude when present; the barometric altitude depends on air pressure. Aircraft on the ground often have no altitude.
- 4
Show where an aircraft came from
/tracks/all with icao24 and time=0 returns the route of the current flight as a list of points [time, latitude, longitude, altitude, heading, on ground]. It has its own allowance (separate from /states). The flight lists (/flights/arrival, /flights/departure) return 403 anonymously: 'You cannot access historical flights'.
https://opensky-network.org/api/tracks/all?icao24=48683e&time=0
{"icao24":"48683e","callsign":"TRA81H ","startTime":1.792178801E9,"endTime":1.792187882E9, "path":[[1792178801,37.4879,15.2291,1219,82,false],[1792178801,37.4879,15.2291,1219,82,false], [1792178828,37.4923,15.2694,1828,79,false], … ]} (160 punten; het laatste: [1792187882, 52.2609, 4.6641, 0, 57, false]) - 5
Departure and arrival times? Only with a Schiphol account
OpenSky doesn't know about gates or delays. Those are in Schiphol's Public Flight API, but that needs an account, an application and an OAuth2 token (valid 30 minutes) and has strict terms: only to help travellers, nothing kept longer than 24 hours, no statistics or historical analysis and no passing on to third parties. A 'what is flying over my house' counter does not fit that. Without a token the API returns 401. We have no account and did not test further.
https://api.schiphol.nl/public/public-flights/v4/flights?scheduleDate=2026-10-16&flightDirection=A&page=0
{ "message":"Unauthorized", "request_id":"ba1cf8526ebba40cf84f856b56520549" } (HTTP 401)
How the sources fit together
- OpenSky NetworkSchiphol flight datavia
roepnaam (callsign 'KLM1682') ≈ vluchtnummer (flightName 'KL1682', IATA-code in plaats van ICAO)OpenSky uses the ICAO callsign (KLM1682), Schiphol the IATA flight number (KL1682). Converting needs an airline table (/airlines in the Schiphol API). For traveller information only; storing for statistics is not allowed.
- OpenSky NetworkCBS StatLinevia
maand (CBS 37478hvv, luchthaven Schiphol)For trends (aircraft movements per month), use CBS table 37478hvv instead of hoarding live positions.
Let AI build it
Paste this prompt into Claude, ChatGPT, Cursor or Lovable. It names the real endpoints, the join keys and the pitfalls, so the assistant writes working code straight away. Always check the result against the source itself.
Build a small web app 'What is flying over my house?' with Python 3 (FastAPI or Flask) or Node 20, plus one HTML page with a Leaflet map. Data: OpenSky Network REST API, anonymous, docs at https://openskynetwork.github.io/opensky-api/rest.html - Positions: GET https://opensky-network.org/api/states/all?lamin=…&lomin=…&lamax=…&lomax=… - Route of one aircraft: GET https://opensky-network.org/api/tracks/all?icao24=<hex>&time=0 What you need to know: - Each state is a list: [0] icao24, [1] callsign (strip spaces), [2] country of registration, [3] time_position, [4] last_contact, [5] lon, [6] lat, [7] baro_altitude (m), [8] on_ground, [9] velocity (m/s), [10] true_track (degrees), [11] vertical_rate (m/s), [13] geo_altitude (m), [14] squawk. Almost anything can be null. - Anonymous: 400 credits a day for /states; a box up to 25 square degrees costs 1. /tracks has its own allowance. Read X-Rate-Limit-Remaining and show it. On 429: respect X-Rate-Limit-Retry-After-Seconds. - Refresh at most once every 4 minutes (or let the user click 'refresh now') and cache the response on the server: all visitors share one call. - /flights/* returns 403 anonymously. Don't scrape the website (robots.txt returns 403). - Licence: non-commercial use only. Credit 'Data: OpenSky Network'. - Compute the real distance to the house with haversine and filter on a 25 km radius. Climbing/descending from vertical_rate (±2 m/s). Speed × 3.6 = km/h. - Don't show departure or arrival times: those are only in the Schiphol Public Flight API (account, OAuth2, travellers only, keep at most 24 hours, no statistics). Output: an input for an address or coordinates (don't geocode via Nominatim without reading its usage policy; let the user click on the map), a map with the aircraft as arrows in their direction of flight, a list sorted by distance (callsign, altitude, km/h, climbing/descending), and on click today's route as a line. At the bottom: time of the data, number of aircraft over the Netherlands and remaining credits.
Try it yourself
The core of the project in a small script. Needs: Python 3.9+ en requests. Tested on 2026-10-16; below is the real output of that run.
# Wat vliegt er nu boven mijn huis? OpenSky Network, anoniem (400 credits per dag, dit script kost er 2).
# Python 3.10+ en requests. Gebruik alleen niet-commercieel (voorwaarden OpenSky).
import math, requests
from datetime import datetime
from zoneinfo import ZoneInfo
UA = {"User-Agent": "mijn-vliegtuigradar/1.0 (+jij@voorbeeld.nl)"}
API = "https://opensky-network.org/api/states/all"
HUIS = (52.3017, 4.8640) # breedte, lengte: Amstelveen, ten oosten van Schiphol; vul je eigen adres in
STRAAL_KM = 25
def toestanden(lamin, lomin, lamax, lomax):
r = requests.get(API, params={"lamin": lamin, "lomin": lomin, "lamax": lamax, "lomax": lomax},
headers=UA, timeout=30)
if r.status_code == 429:
raise SystemExit(f"Dagtegoed op, wacht {r.headers.get('X-Rate-Limit-Retry-After-Seconds')} s")
r.raise_for_status()
return r.json(), r.headers.get("X-Rate-Limit-Remaining")
def afstand_km(a, b): # haversine
p1, p2 = math.radians(a[0]), math.radians(b[0])
d = math.sin((p2 - p1) / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(math.radians(b[1] - a[1]) / 2) ** 2
return 6371 * 2 * math.asin(math.sqrt(d))
# 1. Heel Nederland (11,9 vierkante graad = 1 credit)
nl, _ = toestanden(50.75, 3.2, 53.7, 7.22)
lucht = [s for s in nl["states"] or [] if not s[8]]
tijd = datetime.fromtimestamp(nl["time"], ZoneInfo("Europe/Amsterdam")).strftime("%d-%m-%Y %H:%M:%S")
print(f"{tijd}: boven Nederland {len(nl['states'] or [])} toestellen, {len(lucht)} in de lucht")
# 2. Rond mijn huis: vak van ruim 50 bij 50 km (1 credit), daarna precies op afstand filteren
dlat, dlon = STRAAL_KM / 111, STRAAL_KM / (111 * math.cos(math.radians(HUIS[0])))
hier, over = toestanden(HUIS[0] - dlat, HUIS[1] - dlon, HUIS[0] + dlat, HUIS[1] + dlon)
rijen = []
for s in hier["states"] or []:
icao, roep, land, _, contact, lon, lat, baro, grond, snelheid, koers, stijg = s[:12]
if lat is None or lon is None:
continue # geen positie in de laatste 15 seconden
km = afstand_km(HUIS, (lat, lon))
if km > STRAAL_KM:
continue
hoogte = s[13] if s[13] is not None else baro # geo_altitude, anders barometrisch
fase = "aan de grond" if grond else ("daalt" if (stijg or 0) < -2 else "stijgt" if (stijg or 0) > 2 else "kruist")
rijen.append((km, (roep or "?").strip(), icao, land, hoogte, (snelheid or 0) * 3.6, koers, fase))
print(f"Binnen {STRAAL_KM} km van {HUIS}: {len(rijen)} toestellen (nog {over} credits vandaag)")
for km, roep, icao, land, h, v, k, fase in sorted(rijen)[:8]:
hoogte = f"{h:,.0f} m".replace(",", ".") if h is not None else "? m"
print(f" {km:5.1f} km {roep:8} {icao} {land:15.15} {hoogte:>8} {v:4.0f} km/u koers {k or 0:3.0f} {fase}")
16-10-2026 23:50:54: boven Nederland 38 toestellen, 29 in de lucht
Binnen 25 km van (52.3017, 4.864): 4 toestellen (nog 389 credits vandaag)
6.8 km KLM1682 486258 Kingdom of the ? m 32 km/u koers 239 aan de grond
10.4 km TRA7X 48520b Kingdom of the 152 m 252 km/u koers 58 daalt
10.7 km THY3EZ 4bb101 Turkey ? m 25 km/u koers 222 aan de grond
12.2 km TRA81H 48683e Kingdom of the 2.934 m 510 km/u koers 268 daalt
Where it goes wrong
- The allowance runs out fast. 400 credits a day is one request every 3.6 minutes, all day. A page refreshing every 10 seconds is out within an hour. Cache on the server and refresh only when someone is looking.
- Country is the country of registration, not the airline. SAS1804 showed up as 'Ireland' in our first test, THY3EZ as 'Turkey'. The country follows from the transponder's ICAO24 address.
- Not everything is visible. OpenSky only hears aircraft picked up by a receiver nearby; low aircraft and aircraft without ADS-B (some military, small planes) are missing. At 23:50 there were 38 over the Netherlands; during the day many more.
- Altitude and position can be missing. Aircraft on the ground had no altitude; positions older than 15 seconds become null. Skip aircraft without a position and show '?' for an empty altitude.
- Schiphol data may not be used for this. The Public Flight API forbids statistics, historical analysis and storing longer than 24 hours. A nuisance counter or an archive of flights over your house is built with OpenSky, not with Schiphol.
Take it further
- Add noise: count per hour how many aircraft below 1,000 metres come within 5 km of your house and put that next to the noise measurements of an airport monitoring station.
- An e-ink display on the window sill: a Raspberry Pi showing the nearest aircraft every 5 minutes with callsign, altitude and the city of departure (first point of /tracks).
- Monthly trends: for aircraft movements per month use CBS table 37478hvv (Schiphol and the other airports of national importance) instead of storing live data.
- Free account: with 4,000 credits a day you can refresh every minute and, with extended=1, also see each aircraft's category (light, heavy, helicopter).
The sources2

OpenSky Network
Aircraft positions picked up by a global network of receivers. Without an account, limited to current positions.

Schiphol flight data
Schiphol's Public Flight API gives per flight the scheduled and expected times, status, gate, terminal, baggage belt, aircraft type and route, plus lists of airlines, destinations and aircraft types. Free after registration, but the terms are strict: only to help travellers, nothing stored for more than 24 hours and no statistics.
Dashboard of traffic jams, speeds and roadworks
Train delay and disruption dashboard
How fast is your municipality going electric?
Charging points per neighbourhood and per electric car
Live map of buses, trams and metros