Skip to content

Flight data APIs (AirLabs / Aviationstack / AeroDataBox / OpenSky)

When you log a flight, TravStats can fill in fields you didn’t type yourself — aircraft type, scheduled times, real distance flown, codeshare resolution — by querying flight-data providers. None of this is required. Manual entry works fine. But the auto-fill is the single feature most users enable first.

Four providers are supported. They overlap in scope but each has a sweet spot.

You want…Use
Just the basics for free, current flights onlyAirLabs (free tier)
Historical flights too without a paid planAdd OpenSky (free, login required)
Historical lookup back to 365 days, free tierAeroDataBox (RapidAPI BASIC, 600 API-units/month)
Everything, willing to payAviationstack ($10/mo entry tier)

You can configure all four at once — TravStats cascades through them in priority order until one returns a usable result.

When you create a flight, the backend calls the providers in sequence, falling back if the previous one returns nothing useful:

1. AirLabs → real-time / scheduled flights, current week
2. Aviationstack → historical + future, paid tier covers more dates
3. AeroDataBox → tertiary fallback for out-of-live-window queries
and historical enrichment (since v1.4.0)
4. OpenSky → real-time tracking position, fallback for live
5. Local cache → previously-fetched results stay cached for re-imports

The order matches the typical use case — you log a flight either right after it happens (AirLabs sweet spot) or for historical backfill (Aviationstack / AeroDataBox / OpenSky sweet spot).

Website: airlabs.co · Endpoint TravStats hits: https://airlabs.co/api/v9/schedules

  • Aircraft IATA / ICAO type (e.g. 74H for 747-8)
  • Scheduled departure / arrival times
  • Operating airline + codeshare resolution
  • Origin / destination terminal info when available
  • Distance, flight duration
  • Free tier: “current schedule” — meaning today in most cases. Older or future dates often return empty or wrong data on the free key.
  • Paid tiers: start around $10/mo for historical access (their /flight endpoint). TravStats prefers the free /schedules endpoint by default and only escalates to paid endpoints if you’ve also added an Aviationstack key.
  1. Sign up at airlabs.co, create an API key.
  2. In TravStats: Admin → Settings → External APIs → AirLabs API key → paste, save.
  3. Test from the same screen with the Test connection button. A green “200 OK” means you’re done.

If you’d rather pin the key as a deploy-time variable instead of storing it in the database (e.g. for scripted bootstraps):

.env
AIRLABS_API_KEY=your_key_here

The admin UI shows “configured via environment” for any service where the env var is set. Database-stored keys take precedence if both are present.

Website: aviationstack.com · Endpoint TravStats hits: https://api.aviationstack.com/v1/flights

  • Same fields as AirLabs (aircraft type, times, codeshare resolution)
  • Plus historical flight lookup that covers the past 30+ days reliably
  • Plus future scheduled flights past today

Paid only — no free tier that’s useful for TravStats. The “Basic” tier (~$10/mo at time of writing) is the entry point and covers most homelab use. Higher tiers add more requests / day.

Worth it if you frequently re-import old emails and want them auto-enriched, or if you fly the same routes regularly and want canonical data.

  1. Sign up, pick a tier, get the API key.
  2. Admin → Settings → External APIs → Aviationstack API key → paste, save, test.
.env
AVIATIONSTACK_API_KEY=your_key_here

Website: aerodatabox.com · Hosted on: RapidAPI · Endpoint TravStats hits: https://aerodatabox.p.rapidapi.com/flights/...

The provider that closes the gap AirLabs and Aviationstack-Free leave open: historical lookup for any date in the past 365 days on the free tier.

Core fields (since v1.4.0):

  • Aircraft IATA / ICAO type, registration, 24-bit Mode-S hex
  • Operating airline + codeshare resolution (operating vs. marketing carrier on shared flights)
  • Scheduled departure / arrival times
  • Terminal, gate, distance, flight duration
  • Status (Departed, Arrived, Cancelled, Diverted)

Extended capture (since v1.5.0):

  • runwayDepartureTime / runwayArrivalTime — actual wheels-up / wheels-down stamps where AeroDataBox has them (more precise than the gate-out / gate-in times the other providers return)
  • isCargo — flag for cargo flights, useful for filtering
  • aerodataboxQualityTags — provider-side quality signals (real vs. forecast, actual vs. scheduled)
  • baggageBelt — for the curious
  • checkInDesk — for the curious

A backfill scheduler walks existing rows during the nightly re-enrichment pass so previously-logged flights get the extended fields too, without overwriting curated values.

The airport-side fields shortName and municipalityName (also added in the v1.5.0 schema migration) are populated by the airport seeder on first boot, not by the AeroDataBox adapter at lookup time.

  • Free tier (RapidAPI BASIC): 600 API-units / month. One flight lookup ≈ 1 unit. Comfortable for a homelab — even an active flier logging 20-30 flights / month uses a small fraction.
  • Paid tiers: higher request limits, no feature gates.
  • TravStats surfaces the live X / 600 API units counter in Admin → Settings → API Keys → AeroDataBox, plus a secondary HTTP-request counter (a separate, higher RapidAPI counter that is not the budget — easy to misread as headroom; the primary counter is the one that matters).
  1. Sign up at RapidAPI, subscribe to the AeroDataBox API on the BASIC plan (free), get the API key under Apps → Default Application → Security.
  2. Admin → Settings → External APIs → AeroDataBox API key → paste, save, Test connection.
  3. Bulk historical refresh — under the same card, the “Refresh all flights in 365-day range” button backfills tail number, Mode-S, codeshare metadata, distance, and airline IATA/ICAO onto flights you logged before this provider was wired up. A confirmation modal shows how many API calls the run will cost so you don’t accidentally burn the budget. Disabled on the demo account by design.
.env
AERODATABOX_API_KEY=your_rapidapi_key

Website: opensky-network.org · Endpoint TravStats hits: https://opensky-network.org/api/flights/callsign?…

OpenSky is a community-run ADS-B aggregation. Free, completely open, but limited compared to commercial APIs.

  • Real-time and recent (~30 days) tracking position for known flight numbers
  • Departure / arrival airport from ground-truth ADS-B data — occasionally catches diversions and delays the schedule APIs miss
  • Aircraft type — OpenSky knows the ICAO 24-bit hex but not the type code TravStats needs
  • Codeshare resolution
  • Anything beyond ~30 days back

So OpenSky is an enrichment fallback, not a primary source.

OpenSky moved to OAuth2 in 2024:

  1. Register at opensky-network.org and create an API client (Account → API → Create API client).
  2. You’ll get a client_id and client_secret.
  3. Admin → Settings → External APIs → OpenSky → Client ID + Client Secret → paste, save, test.

The old username + password flow still works for now. TravStats falls back to it if you’ve configured OPENSKY_USERNAME / OPENSKY_PASSWORD instead of the modern OAuth2 fields. Prefer the OAuth2 path; OpenSky may retire basic auth at any time.

Terminal window
# .env — OAuth2
OPENSKY_CLIENT_ID=your_client_id
OPENSKY_CLIENT_SECRET=your_client_secret
# .env — legacy basic auth
OPENSKY_USERNAME=your_username
OPENSKY_PASSWORD=your_password

When TravStats falls back to “no enrichment”

Section titled “When TravStats falls back to “no enrichment””

If all configured providers return empty (or none are configured), the flight saves with whatever you typed manually plus the airport coordinates and computed great-circle distance. The enrichmentStatus field on the row is set to pending, and a nightly cron (default 02:00 UTC) re-tries enrichment for flights that haven’t been touched in 24 h.

You can also trigger a manual re-enrichment from the flight detail page: Re-enrich → Run now. Useful right after you add an API key.

How to verify enrichment is actually firing

Section titled “How to verify enrichment is actually firing”

After adding a flight that should be enriched:

  1. Look at the flight detail page — fields like aircraft, gate, terminal should populate within seconds.
  2. If they don’t, check /app/data/logs/parser*.log:
    Terminal window
    docker exec travstats-app tail -f /app/data/logs/app.log | grep -i enrichment
  3. Common failures: rate-limited (free-tier AirLabs caps you fast on bulk imports), unknown flight number (typo), or date out of provider’s reach (free-tier AirLabs for non-today flights).

These calls send the flight number, date, and airline IATA code to the provider. They don’t see your name, your other flights, or anything stored in your TravStats. Each provider has its own privacy policy linked from their websites — read it if that matters to you.

The provider response — including aircraft type, route, codeshare data — gets stored against your flight row inside your own Postgres. Nothing flows back outbound from those reads.