A Flask-based API that finds the closest aircraft to given coordinates using FlightRadar24 data.
The service was designed to be consumed by lower-power WiFi-enabled devices (eg. Raspberry Pi Pico 2 W) hooked up to a display of sorts in order to show nearby flight data.
See the interstate75 directory for an example project using a Pimoroni "Interstate 75 W" (RP2350) controller with an LED matrix display, along with the accompanying blog post:
https://blog.gregdev.com/posts/2025-11-19-flight-finder-display
There's also an example Python script for monitoring nearby aircraft by type, and alerting if certain conditions are met.
-
Install Python via asdf (recommended):
asdf install
-
Create a virtual environment:
python3 -m venv venv source venv/bin/activate -
Install dependencies:
pip install -r requirements.txt
-
Configure an API key in a
.envfile to enable optional authentication:SERVICE_API_KEY=
-
Run the development server:
python flight_service.py
The service will start on: http://0.0.0.0:7478
-
Run the tests:
pytest
Test if the service is running:
curl http://localhost:7478/healthResponse:
{"status": "ok"}Basic request:
curl "http://localhost:7478/closest-flight?lat=37.7749&lon=-122.4194&radius=25"With API key authentication:
curl -H "X-API-Key: your_secret_key_here" \
"http://localhost:7478/closest-flight?lat=37.7749&lon=-122.4194&radius=25"| Parameter | Type | Required | Description | Range |
|---|---|---|---|---|
lat |
float | Yes | Latitude | -90 to 90 |
lon |
float | Yes | Longitude | -180 to 180 |
radius |
float | No | Search radius in km | 1 to 500 (default: 10) |
max_altitude |
float | No | Altitude ceiling in feet - flights above this are ignored (useful to filter out cruise-altitude overflights and focus on flights arriving/departing nearby airports) | ≥ 0 (default: no ceiling) |
Success (flight found):
{
"found": true,
"distance_km": 45.23,
"flight": {
"id": "2f3a4b5c",
"number": "UA123",
"callsign": "UAL123",
"icao_24bit": "A12345",
"position": {
"latitude": 37.8,
"longitude": -122.5,
"altitude": 33000,
"heading": 270,
"ground_speed": 450,
"vertical_speed": 1500
},
"aircraft": {
"code": "B738",
"model": "Boeing 737-800",
"registration": "N12345"
},
"airline": {
"icao": "UAL",
"iata": "UA"
},
"route": {
"origin_iata": "SFO",
"destination_iata": "LAX",
"origin_name": "San Francisco International Airport",
"destination_name": "Los Angeles International Airport"
}
}
}No flights found:
{
"found": false,
"message": "No flights found in search area"
}Error:
{
"error": "Invalid parameters. Required: lat, lon. Optional: radius"
}This works similarly to the /closest-flight endpoint but returns all flights within the specified radius.
curl "http://localhost:7478/flights-in-radius?lat=37.7749&lon=-122.4194&radius=25"The response is a flights array containing all flights within the specified radius (each flight object has the same structure as in the /closest-flight response, see above).
The flight endpoints (and the device check-in/OTA endpoints) authenticate with an X-API-Key header. A request is accepted if its key matches either:
SERVICE_API_KEY- a single shared key set in the environment (.envlocally,dokku config:setin production).- A per-client key - created and revoked from the admin
/fleetpage's "API keys" panel, one per device, stored in the fleet database. Disabling or deleting a key blocks just that device on its next request, leaving the rest of the fleet untouched.
Both are honoured at once, so you can keep the shared key working while migrating a fleet to per-client keys, then retire it. If neither is configured the endpoints stay open (handy for local dev - lock it down before exposing the service publicly). Managing per-client keys needs ADMIN_TOKEN; for production setup see docs/dokku.md.
Returns API documentation and available endpoints.
Health check endpoint for monitoring.
Response:
{"status": "ok"}Find the closest in-flight aircraft to given coordinates.
Query Parameters:
lat(required): Latitudelon(required): Longituderadius(optional): Search radius in km (default: 10)max_altitude(optional): Altitude ceiling in feet - flights above this are ignored (default: no ceiling)
Headers:
X-API-Key(optional): API key, if authentication is enabled / required
Find all in-flight aircraft within a given radius of the specified coordinates.
Query Parameters:
lat(required): Latitudelon(required): Longituderadius(optional): Search radius in kilometers (default: 10)max_altitude(optional): Altitude ceiling in feet - flights above this are ignored (default: no ceiling)
Headers:
X-API-Key(optional): API key, if authentication is enabled / required
Every authenticated call to /closest-flight and /flights-in-radius doubles as a device heartbeat: the service records the caller (from the User-Agent and an optional X-Device-Id header), its reported code version, source IP, and last-seen time into a small SQLite database (FLEET_DB_PATH, default fleet.db). This is how the Interstate 75 displays report in - see examples/interstate75. No extra requests are made; it piggybacks on the polling the devices already do.
The fleet endpoints are guarded by a separate ADMIN_TOKEN env var (independent of SERVICE_API_KEY). When ADMIN_TOKEN is unset they return 503 rather than exposing device data.
Human-readable HTML table of known devices (ID, label, version, last-seen with an offline flag, IP, request count). In a browser it prompts for HTTP Basic Auth - enter the admin token as the password.
The same data as JSON.
Headers / auth (both endpoints):
X-Admin-Token: <token>,Authorization: Bearer <token>, HTTP Basic Auth password, or?token=<token>.
For persistent storage across deploys, see docs/dokku.md.
app.run(host='0.0.0.0', port=port, debug=True) # enable debug modeThis README covers development setup. For production deployments, see the deployment guides in /docs.
This service uses data from FlightRadar24 via the unofficial FlightRadarAPI library.
Important: This service is for educational and personal use only. For commercial use, contact business@fr24.com or use the official FlightRadar24 API.
