Every flight integration starts the same way. There is a status endpoint, there is setInterval, and by lunchtime there is a loop:
setInterval(async () => {
for (const booking of await bookingsWithFlightsToday()) {
const flight = await fetchStatus(booking.carrier, booking.number, booking.date);
if (flight.status !== booking.lastStatus || flight.gate !== booking.lastGate) {
await notify(booking, flight);
await save(booking, { lastStatus: flight.status, lastGate: flight.gate });
}
}
}, 5 * 60 * 1000);It works in the demo. This article is about what it costs once it is real, with numbers, and about the cases where it is still the right thing to write.
Cost one: the requests
A request-priced API charges for every time you ask, and a poller asks whether or not anything changed. Almost nothing changes, so almost every request buys nothing.
How many requests a flight costs depends on two choices: how often you ask, and for how long.
| Interval | Window | Requests per flight | A gate change can be this late |
|---|---|---|---|
| 15 min | last 6 h | 24 | 15 min |
| 5 min | last 6 h | 72 | 5 min |
| 2 min | last 6 h | 180 | 2 min |
| 1 min | last 3 h | 180 | 1 min |
| 3 min | full day | 480 | 3 min |
Take the middle row, which is what most teams settle on: every five minutes for the six hours before arrival, 72 requests per flight. At 500 flights a month that is 36,000 requests. On the request-priced APIs, checked in September 2026:
| Provider | What 36,000 requests need | Monthly |
|---|---|---|
| aviationstack | Basic is 10,000 requests; Professional is 50,000 | $149.99 |
| AeroDataBox | A status call is two units, so 72,000 units; Starter stops at 40,000 | $99 (Growth) |
| FlightAware AeroAPI | $0.005 per result set, on a tier with a $100 minimum | ≈ $180 |
None of those is outrageous. The point is the shape: you are paying for 36,000 questions to get perhaps 2,000 answers that mattered, and the bill scales with your nervousness rather than with your bookings.
Cost two: the staleness you agree to
The table above has a column people skip. Polling every five minutes means a gate change is, on average, two and a half minutes old when you see it and up to five minutes old at worst. For a driver in the cell-phone lot that is fine. For a "your gate has changed" push to a passenger walking the wrong way, it is not.
Shortening the interval fixes the lateness and multiplies the bill: one minute instead of five is five times the requests. The cost curve of a fixed-interval poller is a hyperbola, cost ≈ 1 / staleness, and the two ends are both bad.
The way out is not a better interval, it is a schedule that follows the flight: nothing worth asking a day out, every few minutes at the gate. That is what a flight-tracking service does internally, and it is why FlightByte's own cadence looks like this:
| Situation | Poll interval |
|---|---|
| More than 48 hours out | Every 12 hours |
| 24 to 48 hours | Every 6 hours |
| 6 to 24 hours | Hourly |
| 2 to 6 hours | Every 20 minutes |
| 45 minutes to 2 hours | Every 10 minutes |
| Under 45 minutes, boarding, departed | Every 3 minutes |
| In the air | Every 10 minutes, every 3 on approach |
| Landed, waiting for the gate | Every 2 minutes |
| At the gate | Every 5 minutes for 45 minutes, for the baggage belt, then done |
That spends roughly 40 to 120 upstream polls per flight, tighter than a five-minute loop where it matters and far looser where it does not. You can write this schedule yourself; the next guide prints the function. It is the third cost.
Cost three: the state
The demo loop compares two fields. A real one compares a snapshot, and a snapshot has edges:
- Absent is not null. An estimate that disappears because the airline withdrew it is a different event from an estimate of
null. Your diff has to know which fields can vanish. - Times come in pairs. Scheduled, estimated and actual, for gate and for runway, in UTC and local. "The departure time changed" is six comparisons and a decision about which one the customer cares about.
- Delay is a derived number. Estimated minus scheduled, gate not runway, clamped at zero on some feeds and negative on others.
- Flight numbers are not unique.
TK1is SAW → AMS in the morning and IST → JFK in the afternoon. A poller keyed on carrier, number and date will faithfully track the wrong aircraft for a booking that never told it the origin. - Cancelled flights come back. A reinstated flight is not a new flight, and a poller that deleted its row on
cancelledwill never notice.
Each of these is a bug you find in production, once, with a customer attached. FlightByte's diff produces 23 typed changes with from, to, a deltaMinutes and a sentence, because the diff is the product. That number is the size of the state machine you would otherwise carry.
Cost four: the operational tail
The loop runs on a machine that restarts, against a provider that has bad afternoons, feeding a notifier that fails sometimes.
- Retries. When your SMS provider returns a 500 at 03:10, the loop either loses the message or grows a queue.
- Rate limits. aviationstack meters per minute; AeroAPI per second on the Standard tier. A loop that wakes every five minutes and fires 500 requests at once meets the limit on the first morning it matters.
- Provider outages. Upstream returns 503 for twenty minutes. Does the loop back off, alert, or hammer?
- Deduplication. The loop ran twice because the deploy overlapped. Two "your flight has landed" messages.
- The log. A driver says the message never came. What was sent, when, and what did the phone provider answer?
None of this is hard. All of it is time, and it is time spent on the part of the product nobody sees.
Cost five: the flights you are not polling yet
A poller only knows about flights that are already in its table. Bookings are made a week out, and airlines publish the operational record a few days before departure. Somebody has to decide when a booking becomes a flight to poll, and what to do when the lookup returns 404 because the flight does not exist yet.
A subscription created at booking time waits as pending, checks on a slow schedule, and activates itself when the flight appears. The booking flow does not need to know the difference.
When polling is the right call
Honesty cuts both ways, and there are three cases where the loop above is exactly what you should write.
You need "now", on a page, for a handful of flights. A departures board or a support tool that shows the current state when someone opens it does not need a schedule at all. Call the lookup when the page loads; on FlightByte it is cached and included in the plan.
You need the data, not the change. Positions, tracks, history, airport statistics. A request-priced catalogue is the right product for that, and AeroDataBox or AeroAPI are the right vendors.
You already run the pipeline. If you have a scheduler, a diff, a retry queue and a delivery log for other reasons, adding flights to it is cheap. Most teams do not, and build them for this one job.
The arithmetic, once more
The same 500 flights a month, priced by what you actually count:
| Requests | Plan | Monthly | Left to build | |
|---|---|---|---|---|
| aviationstack | 36,000 | Professional | $149.99 | scheduler, diff, retries, log |
| AeroDataBox | 72,000 units | Growth | $99 | scheduler, diff, retries, log |
| AeroAPI | 36,000 result sets | Standard | ≈ $180 | scheduler, diff, retries, log |
| FlightByte | 500 watched flights | Starter | $19 | a receiver |
Or Growth at $49 for 2,000 flights of headroom. The polls, the diff, the retries and the log are inside that number, and the bill moves with your bookings rather than with your interval.
What a watched flight is
One flight on one date, delivered to one URL, with your metadata attached. It is priced that way because that is what costs FlightByte upstream polls and what a customer counts: a booking is a flight. Pricing has the four plans; Flight landed notification in 20 minutes has the receiver.