Every ConnectScore on this site, as JSON. Free, no key, no accounts, no rate limit, CORS open to every origin. Read-only. Each response carries a sources array with the data credits — please keep it attached when you re-publish.
Responses are pretty-printed and gzipped. Cache-Control: public, max-age=3600 on success (the data is refreshed once a day), 300 on errors.
| Method | Path | Returns |
|---|---|---|
| GET | /api | This index: every endpoint, the airline keys, the flight-number prefixes. |
| GET | /api/airlines | All 18 airlines, best ConnectScore first. |
| GET | /api/airlines/{key} | One airline. Unknown key → 404 JSON with the list of valid keys. |
| GET | /api/score/{flightNumber} | Per-flight odds where we have route history, otherwise the coarse airline score. Untracked prefix → 404 JSON. |
All 18 airlines, ordered by ConnectScore descending, ties broken by name — the same order as the leaderboard, from the same function.
Keys are the slugs used in the site URLs: airbaltic · aircanada · airfrance · alaska · american · britishairways · delta · emirates · hawaiian · jetblue · jsx · qatar · sas · southwest · united · virginatlantic · westjet · zipair
Abridged. Qatar Airways really is 58 — 58% of the fleet × 1.0 system quality × 1.00 free-for-you. Same number on the page →
Accepts UA212, ua 212, UA0212 — case, spaces, hyphens and leading zeros are all normalised away. Prefixes we know: AA AC AF AS B6 BA BT DL EK HA QR SK UA VS WN WS XE ZG
The method field is the whole point of this endpoint. It tells you how much to trust the number, and it is never blurred:
| method | prob | What it means |
|---|---|---|
| route-history | 0–100 | We found this exact flight number in our cached United route history. prob is the share of recent observations of that flight that were flown by a Starlink aircraft, with the observation count and confidence in evidence. |
| airline-coarse | null | We have no per-flight history for it, so all we can honestly offer is the airline’s fleet-wide ConnectScore. prob is null rather than a guess — inventing precision here would be the worst thing this API could do. |
United is the only fleet with per-flight history today; every other prefix returns airline-coarse. Rank a whole route →
connectScore = fleet.equippedShare × system.quality × free.factor, rounded
| key | q | Label |
|---|---|---|
| starlink | 1.0 | Starlink |
| leo | 1.0 | Amazon Leo |
| viasat | 0.6 | Viasat |
| 2ku | 0.6 | 2Ku |
| intelsat | 0.6 | Intelsat |
| geo | 0.3 | legacy GEO |
| panasonic | 0.3 | Panasonic |
| none | 0.0 | — |
| status | factor | Means |
|---|---|---|
| free | 1.00 | free onboard |
| loyalty-free | 1.00 | free for loyalty members |
| loyalty-tier | 0.85 | free on paid status tiers |
| partial | 0.85 | free on some cabins/routes |
| unknown | 0.85 | free status unconfirmed |
| paid | 0.70 | paid |
tail-counts — the airline publishes equipped and total aircraft, so equippedShare = equipped / total.
fleetwide-coverage — no tail counts exist (Delta, jetBlue publish only “fleetwide”), so equipped and total are null and the share comes from a stated coverage fraction.
A future deal is never scored. future is reported and contributes zero until the hardware flies.
| Status | code | When |
|---|---|---|
| 400 | unparseable_flight | The path segment is not shaped like a flight number. |
| 404 | unknown_airline | No airline with that key. |
| 404 | unknown_airline_prefix | The flight number parses, but we do not track that carrier. |
| 405 | method_not_allowed | Read-only API — GET or HEAD. |
| 503 | dataset_unavailable | The cached United dataset could not be read from this deploy. This should never happen; it means the deploy is broken, and we would rather say so than quietly hand back the coarse score as if nothing were wrong. |
There is also an MCP endpoint, so an AI assistant can look these numbers up itself instead of guessing from whatever it remembers about airline WiFi. It speaks streamable HTTP — JSON-RPC 2.0 over POST /mcp, no key, no account, CORS open, and no session required for a single call.
| Tool | Arguments | Returns |
|---|---|---|
| get_airline_score | key | One airline: ConnectScore, system, fleet share, cost onboard, and the confidence tier the number comes from. |
| list_airline_scores | — | All 18 airlines, best odds first. One call instead of eighteen. |
| score_flight | flight_number | One flight. United returns a per-flight probability with its sample size; every other carrier returns the coarse airline score with prob: null. |
Every tool result comes back twice: a text block for the model to relay and a structuredContent object for the client to parse. The data credits are in both, because the text is what usually reaches the user.
What initialize returns is not a description of the endpoints — it is the decision layer: someone asking about flight WiFi is trying to maximise hours of working WiFi, so prefer the higher ConnectScore, use United's route history when there is a flight number, name which confidence tier you are quoting, never invent a per-flight number for a fleet we have no history for, and always pass the credits through. A data endpoint with no opinion gets averaged into mush by whichever model is holding it.
Each tool is a thin wrapper around the very handler that serves /api/** — it builds a synthetic GET request and re-shapes the answer. So score_flight("UA212") and GET /api/score/UA212 cannot disagree: they are the same call. The acceptance suite asserts exactly that.
This server opens no server-initiated SSE stream, so there is nothing for a GET to subscribe to. It answers with a 405 whose body tells you the POST to make instead — a bare 405 with no bytes is the kind of unhelpful signal this project has been bitten by before.
The fleet numbers for United and Alaska are not ours — they come from unitedstarlinktracker.com and alaskastarlinktracker.com, the independent community trackers built by @martinamps. The sources array is in every response so that the credit travels with the data. Keep it, or reproduce it wherever you show the numbers.
It reads only our own cached dataset, from the same deploy that served this page. It does not proxy a tracker, an airline, or a flight-status provider — your traffic can never become their bill. That is also why per-flight odds exist for United only: it is the fleet we have history for.
There is no key and no quota. Responses are cacheable for an hour and the underlying data changes once a day, so honour the Cache-Control header rather than polling. If it ever gets abused, a limit is the thing that appears first.
Fields may be added at any time. Anything already here — connectScore, method, prob, sources — will not change meaning without the path changing to /api/v1/. Every response carries an x-connectscore-api header with the version.
ConnectScores and per-flight odds are historical estimates. Aircraft assignments change until departure, and a score is the chance of getting the good system, not of getting any WiFi at all.