API reference
One endpoint, one response shape.
Authenticate with a key, pass a flight number and a date, read the resolved record. Everything below is generated from the route handlers themselves.
https://flight-api.dev
All timestamps are ISO 8601 in UTC. Responses are always JSON and always carry a boolean success discriminator.
Machine-readable
Every artifact below is generated from the same source as this page, so it cannot describe a different API.
Every path, parameter, schema and status code. Feed it to a client generator or an agent toolchain.
The whole reference as one structured document, including the worked examples on this page.
A short, link-first index for agents — start here and follow the links.
No credential is needed to read any of these. Point an MCP client with fetch capability at the index and it can walk the rest itself.
{
"mcpServers": {
"fetch": {
"command": "uvx",
"args": [
"mcp-server-fetch"
]
}
}
}Then: “read https://flight-api.dev/llms.txt and use it to call the flight status endpoint”.
Authentication
Keys are hashed with SHA-256 and compared in constant time. The plaintext is shown once, at creation.
Every key is prefixed flt_. Requests without a credential get a 401; a disabled key or account gets 403; an account with no active plan gets 402. Manage keys from the dashboard.
Metering
One credit per authorised request. The rules below are the whole contract — there is nothing else to model.
At authorisation, before the resolve runs. A miss, a cache replay and a full live resolve all cost exactly one credit.
Rejected credentials (401), disabled keys and accounts (403), and GET /api/v1/account, which is deliberately unmetered so checking a balance never moves it.
A 400 still costs a credit — the key was already authorised. The usage log records it against the same key so the ledger and the meter agree.
Without overage the next request is refused with 429 and no credit is taken. With overage enabled the request is served and billed at the plan rate.
Allowances run in 30-day windows that roll from the previous window, not from the first request of the month. Unused credits do not carry over.
Overage is metered as it happens and invoiced in arrears: one invoice at the start of each month covering the month before, charged automatically. A month without overage is not charged. The plan fee bills separately on its own cycle.
/api/v1/flights/statusAPI keyResolve a historical flight
Resolves one flight on one date and returns the selected record. Settled disruptions are replayed from storage.
IATA flight designator. Trimmed and upper-cased server-side.
aliases: flight, number
Scheduled departure date in UTC. A full ISO timestamp is truncated to the date.
aliases: date
Disambiguates codeshares and repeated flight numbers.
aliases: originIata, from
Disambiguates codeshares and repeated flight numbers.
aliases: destinationIata, dest, to
Skip the persisted record and resolve the flight again from scratch.
Attach a rendered proof image of the record as base64. Never persisted.
aliases: includeScreenshot
Boolean flags accept "1" · "0" · "true" · "false" · "yes" · "no".
curl -sG "https://flight-api.dev/api/v1/flights/status" \
-H "Authorization: Bearer $FLIGHT_API_KEY" \
--data-urlencode "flightNumber=BA249" \
--data-urlencode "flightDate=2026-08-14"const params = new URLSearchParams({
flightNumber: 'BA249',
flightDate: '2026-08-14',
})
const response = await fetch(`https://flight-api.dev/api/v1/flights/status?${params}`, {
headers: { Authorization: `Bearer ${process.env.FLIGHT_API_KEY}` },
})
const { success, data, message } = await response.json()
if (!success) throw new Error(message)
// data.selected is the resolved record.
console.log(data.selected?.status, data.selected?.delayMinutes, data.meta.cached)Response
A 200 always means the resolve completed — it does not guarantee the flight was found.
- flightNumber string
- Echoed back, normalised to upper case.
- flightDate string
- Echoed back as `YYYY-MM-DD`.
- originIata string | null
- Resolved departure airport.
- destinationIata string | null
- Resolved arrival airport.
- selected object | null
- The normalised record. Null when nothing was found.
- flightDelay object | null
- Delay and disruption verdict with its rationale, when timings were available.
- meta object
- Timing, cache and persistence state.
- found boolean
- The flight was matched.
- complete boolean
- Enough timings were present to settle the record.
- status 'delayed' | 'cancelled' | 'diverted' | 'on_time' | 'not_found'
- Classified outcome.
- isDelayed boolean
- Arrival delay greater than zero.
- isCancelled boolean
- Flight never operated.
- isDiverted boolean
- Landed somewhere other than the destination.
- delayMinutes number | null
- Arrival delay in minutes.
- departureDelayMinutes number | null
- Departure delay in minutes.
- scheduledDeparture string | null
- ISO 8601, UTC.
- scheduledArrival string | null
- ISO 8601, UTC.
- estimatedDeparture string | null
- ISO 8601, UTC.
- estimatedArrival string | null
- ISO 8601, UTC.
- actualDeparture string | null
- ISO 8601, UTC.
- actualArrival string | null
- ISO 8601, UTC.
- airline object | null
- `name`, `iata`, `icao`.
- aircraft object | null
- `registration`, `iata`, `icao`, `icao24`, `label`.
- flightIata string | null
- Normalised IATA designator.
- flightIcao string | null
- Normalised ICAO designator.
- statusRaw string | null
- Raw status string, unmodified.
- durationMs number
- Wall clock for the whole resolve. `0` on a cache hit.
- startedAt string
- ISO 8601, UTC.
- finishedAt string
- ISO 8601, UTC.
- cached boolean
- Served from the persisted record instead of a live resolve.
- persisted boolean
- This response was written to the record table.
- recordId string | null
- Always null on the public endpoint.
{
"success": true,
"data": {
"flightNumber": "BA249",
"flightDate": "2026-08-14",
"originIata": "LHR",
"destinationIata": "GRU",
"selected": {
"found": true,
"complete": true,
"status": "delayed",
"isDelayed": true,
"isCancelled": false,
"isDiverted": false,
"delayMinutes": 47,
"departureDelayMinutes": 39,
"originIata": "LHR",
"destinationIata": "GRU",
"scheduledDeparture": "2026-08-14T21:40:00.000Z",
"scheduledArrival": "2026-08-15T05:05:00.000Z",
"estimatedDeparture": null,
"estimatedArrival": null,
"actualDeparture": "2026-08-14T22:19:00.000Z",
"actualArrival": "2026-08-15T05:52:00.000Z",
"airline": {
"name": "British Airways",
"iata": "BA",
"icao": "BAW"
},
"aircraft": {
"registration": "G-ZBKF",
"iata": "789",
"icao": "B789",
"icao24": null,
"label": "Boeing 787-9"
},
"flightIata": "BA249",
"flightIcao": "BAW249",
"source": null,
"statusRaw": "Landed 05:52"
},
"flightDelay": {
"delayMinutes": 47,
"delayFormatted": "47 min late",
"statusKind": "landed",
"disruption": {
"type": "delay",
"isCancelled": false,
"isDiverted": false,
"neverArrived": false,
"arrivalDelayMinutes": 47,
"departureDelayMinutes": 39,
"confidence": "high",
"sources": [
"ata_vs_sta"
],
"rationale": [
"Actual arrival 47 min after scheduled arrival"
]
}
},
"meta": {
"durationMs": 0,
"startedAt": "2026-08-24T09:12:04.881Z",
"finishedAt": "2026-08-24T09:12:04.881Z",
"source": null,
"cached": true,
"persisted": true,
"recordId": null
},
"sources": []
}
}Caching
Only outcomes that can no longer change are frozen, so a cached reply is never stale.
Delayed, cancelled or diverted, complete, and departing more than three UTC days ago. Stored unique on flight number and date, then replayed with meta.cached true and durationMs 0.
On-time and not-found results, anything inside the three-day settle window, and screenshots.
Bypasses the stored record and resolves from scratch. The fresh answer overwrites the row in place when it is still persistable.
Errors
Failures carry a human-readable message; validation errors name the field.
- 200
- Resolved. `data.selected` may still be null when the flight was not found.
- 400
- Query validation failed. `message` names the offending field.
- 401
- Missing or invalid API key.
- 402
- No active plan on the account, or the subscription is canceled or past due. No credit is taken.
- 403
- Key or account disabled.
- 429
- Request allowance for the current window is exhausted and overage is not enabled. No credit is taken.
- 500
- Resolve failed. Safe to retry.
{
"success": false,
"message": "flightDate: flightDate must be YYYY-MM-DD"
}/api/v1/accountAPI keyInspect the calling key
Returns the account and key behind the supplied credential, its lifetime request count and the credit position for the current window. Unmetered — this call costs nothing.
{
"success": true,
"data": {
"accountId": "acct_9f3c1d",
"accountSlug": "acme-9f3c1d",
"keyId": "clz8k2f9a0002qw2h",
"keyName": "production",
"keyPrefix": "flt_0Rk2Xa9c",
"unlimited": false,
"requestCount": 1284
}
}- 200
- Key is valid.
- 401
- Missing or invalid API key.
- 402
- No active plan, or the subscription is canceled or past due.
- 403
- Key or account disabled.
/api/healthPublicLiveness probe
Unauthenticated. Returns immediately.
{
"ok": true,
"service": "flight-api"
}