Skip to content

Mobile and web apps

Spotting apps, alerts for unusual flights, maps: the key stays on your server and the app asks you. That way usage depends on how often you refresh, not on how many users you have.

The pattern

Your server fetches the data at fixed intervals and keeps it in memory; the app asks the server only for what it needs. You can fetch one area per region you serve or, on Pro and above, the whole world in one request.

Your app
asks your server, without a key
Your server
in-memory cache, refreshed every 30 s
FlyItalyADSB API
the key lives only here

What it uses

With the cache the count doesn’t depend on users. Two examples, refreshing every 30 seconds:

PatternPer monthPlan
The whole world in one request86,400Pro (only Pro and Fleet have a worldwide radius)
10 areas of 500 km864,000Starter (up to 1,000 km and 1 million a month)
Every user asks the API directly, 1,000 active users86,400,000beyond the self-serve plans: avoid it

The whole world is about 7.2 MB of JSON, 1.1 MB with gzip: there are no bandwidth limits, only requests count. Ask for compression (Accept-Encoding: gzip; fetch does it by itself).

The code

Your server, one cache for everyone (Node 18+)
// server.mjs: one cache for all your app’s users (Node 18+, no dependencies)
import http from 'node:http';

const KEY = process.env.FIA_API_KEY;
// Pro and Fleet: the whole world. On Free use your area (up to 500 km).
const SOURCE = 'https://api.flyitalyadsb.com/v2/lat/0/lon/0/dist/20000';
const ATTRIBUTION = 'Flight data: FlyItalyADSB (flyitalyadsb.com), CC BY-SA 4.0';
let snapshot = { now: 0, ac: [] };

async function refresh() {
  try {
    const r = await fetch(SOURCE, { headers: { 'X-Api-Key': KEY, 'User-Agent': 'my-app-backend/1.0' } });
    if (r.ok) snapshot = await r.json(); // fetch handles gzip by itself
    else console.log('refresh', r.status, r.headers.get('retry-after') ?? '');
  } catch (e) {
    console.log('refresh failed', e.message); // the last copy stays
  }
}
refresh();
setInterval(refresh, 30_000);

const km = (la1, lo1, la2, lo2) => {
  const p = Math.PI / 180;
  const h = Math.sin(((la2 - la1) * p) / 2) ** 2
    + Math.cos(la1 * p) * Math.cos(la2 * p) * Math.sin(((lo2 - lo1) * p) / 2) ** 2;
  return 12742 * Math.asin(Math.sqrt(h));
};

// GET /near?lat=40.64&lon=-73.78&km=25
http.createServer((req, res) => {
  const q = new URL(req.url, 'http://localhost').searchParams;
  const lat = Number(q.get('lat')), lon = Number(q.get('lon'));
  const radius = Math.min(Number(q.get('km')) || 25, 250);
  const ac = snapshot.ac.filter((a) => a.lat != null && km(lat, lon, a.lat, a.lon) <= radius);
  res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'max-age=5' });
  res.end(JSON.stringify({ now: snapshot.now, attribution: ATTRIBUTION, ac }));
}).listen(8080);
In the app
// In the app: ask your server, never the API with the key
const res = await fetch('https://your-server.example/near?lat=40.64&lon=-73.78&km=25');
const { ac, attribution } = await res.json();
// show "attribution" where the user sees the aircraft

Replace YOUR_KEY with your key: it’s free, get it here. Full specification in openapi.json. Complete examples (curl, Python, Node.js and an ESP32 desk display) on GitHub.

More for an app

  • History: one aircraft’s track on a day, today included, with /v2/history/{hex}/{date}. History.
  • Overflights: the aircraft that passed over a point between two times, with /v2/overflights.
  • Emergencies and notable aircraft: emergency squawks, military and other notable aircraft in an area with /v2/events, and on paid plans the same events pushed to your URL. Alerts and webhooks.
  • An aircraft’s profile in one request: identity, whether we see it now, first and last day in the history, with /v2/profile/{hex}.

An app that already does it

Products built on the network’s data, shown with their makers’ permission.

Mobile app · Pro plan

FlyScoutby Apptractor

Finds unusual flights worldwide, such as go-arounds, holds, emergencies and tankers flying racetracks, and replays them on a map.

How it uses the dataFlyScout’s server reads the whole world from our API and derives the events; the app only talks to that server.

“Really happy I switched to your service.”

Stan, Apptractor

Built something on FlyItalyADSB data? Write to us: if you like, we’ll add it here.

Watch out for

  • Never put the key in the app or the web page. CORS is open, but a key in client code is visible to anyone.
  • Refreshing more often than every half second doesn’t help: the data doesn’t change faster.
  • Attribution in the app, where users see the aircraft: the line to use. The example server returns it with the data.
  • On a 429, keep the last copy and retry after Retry-After: users won’t notice.
  • There are no routes or photos in the data: if the app shows departure and arrival, you need another source.

Further reading

← For product builders