Skip to content

Flight data APIs (AirLabs / Aviationstack / 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.

Three 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)
Everything, willing to payAviationstack ($10/mo entry tier)

You can configure all three 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. OpenSky → real-time tracking position, fallback for live
4. 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 / 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: 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.