What the endpoint does

One request in. One settled record out.

A single GET takes a flight number and a date and returns the first complete answer. Internals stay ours.

What you get

# 01

One query shape

Flight number plus a date. Origin and destination are optional and only narrow the match when a number was flown more than once that day.

# 02

Sealed resolve

You send a number and a date. You get a settled record. How we produce it stays ours.

# 03

Delay math included

Arrival and departure delay in minutes, plus a disruption block that records how the number was derived and how much to trust it.

# 04

Settled records are frozen

A disruption older than three UTC days cannot change, so it is written to Postgres once and replayed from there at zero lookup latency.

# 05

Deterministic by design

Every timestamp is UTC. The same flight and date resolve the same way today and next quarter.

# 06

Auditable by default

Every call is logged against the key that made it, with latency, outcome and status code — so a disputed invoice or a runaway integration is a query, not a guess.

What settles

Only a complete delay, cancellation or diversion becomes the record. Anything else comes back as selected: null.

A flight that operated on time is not a record we freeze. Ask again and it costs another request. Coverage and limits.

The query

Two required parameters. Everything else narrows the match or changes how the answer is produced.

GET /api/v1/flights/status?flightNumber=BA2709&flightDate=2025-12-28
Query parameters accepted by the flight status endpoint
ParameterTypeNotes
flightNumberRequiredstring (2–12)IATA flight designator. Trimmed and upper-cased server-side.also flight, number
flightDateRequiredYYYY-MM-DDScheduled departure date in UTC. A full ISO timestamp is truncated to the date.also date
originIATA (3 letters)Disambiguates codeshares and repeated flight numbers.also originIata, from
destinationIATA (3 letters)Disambiguates codeshares and repeated flight numbers.also destinationIata, dest, to
refreshboolean flagSkip the persisted record and resolve the flight again from scratch.
screenshotboolean flagAttach a rendered proof image of the record as base64. Never persisted.also includeScreenshot

Boolean flags accept "1" · "0" · "true" · "false" · "yes" · "no".

Response shape

The same object comes back whether the answer was resolved live or replayed from storage.

flightNumber
Echoed back, normalised to upper case.string
flightDate
Echoed back as `YYYY-MM-DD`.string
originIata
Resolved departure airport.string | null
destinationIata
Resolved arrival airport.string | null
selected
The normalised record. Null when nothing was found.object | null
flightDelay
Delay and disruption verdict with its rationale, when timings were available.object | null
meta
Timing, cache and persistence state.object

Delay math, not just a status string

Minutes are signed the obvious way: positive is late, negative is early, zero is on time.

On selected
delayMinutes

arrival

departureDelayMinutes

departure

Arrival delay is the headline number, because it is what most compensation and audit rules reference. On a cancellation it is null on purpose — a flight that never flew has no arrival to be late for.

flightDelay.disruption
typenone | delayed | cancelled | diverted | unknown
isCancelledboolean
isDivertedboolean
neverArrivedboolean
arrivalDelayMinutesint | null
departureDelayMinutesint | null
confidencehigh | medium | low | none
sourcesstring[]
rationalestring[]

rationale is the part worth reading. It carries the working — which clocks were compared, in what order — so a disputed number can be argued from the response rather than from memory. confidence tells you how much of that working rested on actual times rather than an estimate, and sources names the comparisons that produced it.

Status classification

Derived once from cancelled, diverted and arrival delay — never copied from a free-text status.

delayed

Arrival delay greater than zero minutes.

cancelled

Never operated. delayMinutes is deliberately null.

diverted

Landed somewhere other than the filed destination.

Those three are the only outcomes that become a record, which is also why they are the only ones worth freezing. The union on selected.status also carries on_time and not_found; a lookup with nothing to settle expresses that as selected: null. The unmodified upstream status string is kept as selected.statusRaw.

Settled history is frozen

Caching here is a statement about the flight, not about traffic.

Older than 3 UTC days

A disruption that old will not change again. It is written to Postgres once and replayed on every later request with meta.cached: true and durationMs: 0.

Inside 3 UTC days

Never frozen. Status can still move while a flight is that recent, so those lookups go through a live resolve every time.

refresh=1

Bypasses the stored record and resolves from scratch. The fresh answer overwrites the record in place if it is still persistable.

On-time and not-found results are never stored, so they always cost a live resolve. meta.persisted tells you whether this particular call wrote anything.

One flight is enough to judge it.

Create an account, mint a key, and send the first request against a flight you already know the answer to.