A real-time airport flight information display built for TV screens and monitors. Shows arrivals and departures in a split-board layout, pulling live data from the AirLabs API.
Live Demo: flight-data.shankarsanjay0.workers.dev
- Live Flight Data — Real-time arrivals and departures with status (scheduled, active, landed, cancelled)
- Professional Airport Display — Departures on the left, arrivals on the right, with prominent airport name header — inspired by real airport FIDS boards
- 5-Column Layout — Time | City | Flight (with airline logo + name) | Status | Terminal/Gate — clean and readable
- International / Domestic Filter — Pass
?mode=intfor international only,?mode=domfor domestic only, or?mode=allfor everything - Arrival / Departure Filter — Pass
?type=arrivalor?type=departureto show a single full-width panel, or?type=allfor both - Cloudflare KV Caching — Lazy caching via Cloudflare KV reduces API calls. Cache TTL syncs with the refresh interval set in Settings
- Airline Logos — Each flight displays the airline's logo next to the flight code with airline name below
- Time Window Filter — Only show flights within ±N hours of now (default ±2h). No morning flights cluttering the evening board.
- Slideshow Mode —
?show=1rotates through Arrivals International → Arrivals Domestic → Departures International → Departures Domestic every 10 seconds. Uses cached data — zero extra API calls. Slide counter shown in top bar. - URL Parameters — Pass
?airport=JFK,?mode=int,?type=arrival,?show=1etc. for direct/shareable links (all combinable) - TV/Kiosk Mode — Full-screen, auto-scrolling display designed for wall-mounted monitors
- 235 Countries, 4,600+ Airports — Comprehensive airport database sourced from OurAirports
- Auto-Scroll — Long flight lists scroll automatically with configurable speed
- Configurable Settings — Country/airport picker, mode/type filters, time window, 12/24h format, refresh interval, scroll speed, dark/light theme
- Server-Side API Proxy — Cloudflare Worker proxies AirLabs API calls (API key stays server-side), with KV caching and stale-while-error fallback
- Backup API Key — Supports a fallback AirLabs key that activates automatically when the primary key is rate-limited (429) or fails
- Smart Column Hiding — Terminal/Gate column auto-hides when no data is available (free plan), reappears on Pro plans
- Fully Responsive — Adapts from large TV screens (1920px+) down to small phones (360px). Columns, fonts, and layout adjust at 1024px, 768px, 480px, and 360px breakpoints
- Dark & Light Themes — Professional dark blue-black theme (default) and clean light theme, switchable from Settings
- No Framework — Pure HTML, CSS, and JavaScript — lightweight and fast
- The app queries the AirLabs Schedules API for arrivals and departures at the selected airport
- API requests go through a Cloudflare Worker proxy (
worker.js) which adds the API key server-side, avoiding CORS issues and keeping the key secret - Responses are cached in Cloudflare KV with a TTL matching the user's refresh interval. Subsequent requests within the TTL are served from cache without calling AirLabs
- If the primary API key is rate-limited (429/401/403), the Worker automatically retries with the backup key
- If AirLabs is down or times out, the Worker serves stale cached data as a fallback
- Flight data is displayed in a TV-optimized split-board layout with auto-scrolling
- Schedules cover up to ~10 hours ahead with real-time status updates
All parameters can be combined in the URL for shareable, filtered views.
| Parameter | Values | Description |
|---|---|---|
airport |
IATA or ICAO code | Select airport, e.g. ?airport=JFK or ?airport=KJFK |
mode |
int, dom, all |
int = international only, dom = domestic only, all = both (default) |
type |
arrival, departure, all |
arrival = arrivals only (full width), departure = departures only, all = both panels (default) |
show |
1, all |
1 = slideshow mode (rotates 4 views every 10s), all = normal display (default) |
Examples:
?airport=JFK&mode=int&type=arrival # International arrivals at JFK
?airport=DXB&mode=dom # Domestic flights at Dubai
?airport=LHR&type=departure # All departures from Heathrow
?airport=SIN&mode=int&type=all # All international flights at Singapore
?airport=COK&show=1 # Slideshow at Cochin: rotates Arr Int → Arr Dom → Dep Int → Dep Dom
?airport=COK&show=1&type=arrival # Slideshow ignored when type is set (type takes priority)
Filters are also available in the Settings panel and persist in the URL when saved.
This project uses the AirLabs Schedules API:
| Endpoint | Description | Free Limit |
|---|---|---|
GET /api/v9/schedules?dep_iata=XXX |
Departures from airport | 50 results |
GET /api/v9/schedules?arr_iata=XXX |
Arrivals at airport | 50 results |
Authentication: API key (passed server-side via Cloudflare Worker)
Free Plan Limits:
- 1,000 API requests/month
- 250 requests/minute
- 50 results per request
For full API documentation, see: AirLabs Schedules Docs
- A Cloudflare account (free tier works)
- A GitHub account
- An AirLabs account (free tier available)
git clone https://github.com/sanjuacodez/flight-data.git
cd flight-data- Sign up at airlabs.co
- Go to your account dashboard
- Copy your API key
- Go to Cloudflare Dashboard → Workers & Pages → KV
- Click Create a namespace
- Name it
FLIGHT_CACHE - Copy the Namespace ID
- Open
wrangler.jsoncand replaceYOUR_KV_NAMESPACE_ID_HEREwith the actual ID:
Option A: Via Cloudflare Dashboard (recommended)
- Go to Cloudflare Dashboard → Workers & Pages → Create
- Connect your GitHub repository
- Set build settings:
- Build command:
npx wrangler deploy - Build output directory:
public
- Build command:
- After deployment, add secrets and bindings:
- Go to Settings → Variables and Secrets
- Add
AIRLABS_API_KEY= your API key - Go to Settings → Bindings and bind the
FLIGHT_CACHEKV namespace (if not auto-detected fromwrangler.jsonc)
- Redeploy for secrets to take effect
Option B: Via Wrangler CLI
# Install wrangler and login
npx wrangler login
# Set your AirLabs API key
echo "your-api-key" | npx wrangler secret put AIRLABS_API_KEY
# Deploy (KV binding is picked up from wrangler.jsonc)
npx wrangler deploy# Serve the public/ directory with any static server
cd public
python3 -m http.server 8080
# Or use wrangler for full worker support
npx wrangler devNote: When opening
index.htmldirectly (file://protocol), the airport data loads via<script src="airports.js">instead offetch(), so the dropdown will work. However, the AirLabs API proxy requires the Worker — usewrangler devfor full functionality.
flight-data/
├── public/
│ ├── index.html # Main app (HTML + CSS + JS)
│ ├── airports.js # Airport data as JS (file:// compatible)
│ └── airports.json # Airport data as JSON (fetch fallback)
├── data/
│ └── build_airports.py # Script to rebuild airport data from OurAirports
├── worker.js # Cloudflare Worker (API proxy + KV caching)
├── wrangler.jsonc # Cloudflare Workers config (KV binding)
└── .gitignore
To refresh the airport database from OurAirports:
cd data
# Download latest data
curl -sL -o airports.csv "https://davidmegginson.github.io/ourairports-data/airports.csv"
curl -sL -o countries.csv "https://davidmegginson.github.io/ourairports-data/countries.csv"
# Build airports.json and airports.js
python3 build_airports.pyThis generates:
airports.json— 235 countries, 4,600+ airports (large and medium airports with ICAO codes)airports.js— Same data as a JS global variable- Continent CSVs for reference (africa.csv, asia.csv, etc.)
The Worker uses Cloudflare KV as a lazy cache to minimize AirLabs API calls:
- Cache key format:
flights:<IATA>:arrival/flights:<IATA>:departure - TTL: Matches the refresh interval from Settings (passed via
?ttl=query param) - Staleness check: Each cached entry stores a
fetchedAttimestamp in metadata. If the age exceeds the TTL, the Worker fetches fresh data - Stale fallback: If AirLabs is unreachable, the Worker returns the last cached response (even if stale)
- Response headers:
X-Cache: HIT,MISS, orSTALEto help debug caching behavior
Verify caching: After the first request, check the KV dashboard — you should see keys like flights:COK:arrival appear.
The Worker supports a fallback AirLabs API key. If the primary key hits its rate limit (HTTP 429, 401, or 403), the Worker automatically retries with the backup key before falling back to stale cache.
Setup:
# Set the backup key as a Cloudflare secret
echo "your-backup-api-key" | npx wrangler secret put AIRLABS_API_KEY_BACKUPFor local development, add it to .dev.vars:
AIRLABS_API_KEY=your-primary-key
AIRLABS_API_KEY_BACKUP=your-backup-key
This effectively doubles your monthly API quota (~2,000 calls/month with two free keys). The backup key is optional — if not set, the Worker uses only the primary key.
- Free plan: 1,000 requests/month — Each refresh uses 2 API calls (arrivals + departures). Default 2h interval = ~720 calls/month, safe for 24/7.
- 50 results per request on free plan — Busy airports may have more flights than the limit shows.
- Schedules up to ~10 hours ahead — AirLabs returns current and near-future schedules, not full-day flight plans.
- Small airports may have limited data — Coverage depends on AirLabs' data sources.
The refresh rate is controlled by the API_PLAN config block at the top of public/index.html:
const API_PLAN = {
refreshInterval: 7200, // Default refresh in seconds (2 hours)
timeWindow: 12, // Show flights within ±N hours of now (0 = all)
};Recommended values by plan:
| Plan | Monthly Limit | Refresh Interval | Time Window | Calls/Month (24/7) |
|---|---|---|---|---|
| Free | 1,000 | 7200 (2 hours) |
2 (±2h) |
~720 |
| Basic | 10,000 | 600 (10 minutes) |
4 (±4h) |
~8,640 |
| Pro | 100,000 | 120 (2 minutes) |
6 (±6h) |
~43,200 |
To upgrade: change API_PLAN values, and optionally update AIRLABS_API_KEY in Cloudflare secrets with your new key.
Formula:
(30 days × 24h × 60min) / (interval_minutes) × 2 = monthly calls
- Frontend: Vanilla HTML, CSS, JavaScript
- API: AirLabs Schedules API — real-time flight schedules
- Hosting: Cloudflare Workers (free tier)
- Airport Data: OurAirports (public domain)
This project is open source. Airport data is from OurAirports (Public Domain).
Sanjay Shankar
- Website: sanjayshankar.me
- GitHub: @sanjuacodez
- Buy Me a Coffee: buymeacoffee.com/sanjayshankar
If you find this project useful, consider buying me a coffee! ☕