route-explorer's /api/token sits behind invisible Cloudflare Turnstile
that requires Apple's Private Access Token attestation. Third-party
iOS apps don't qualify for PAT issuance, and Linux Docker containers
can't pass it either (cross-OS fingerprint, even with patchright /
Camoufox). Migrates direct-flight search to FlightAware; multi-stop
and where-can-I-go remain via embedded SFSafariViewController.
- FlightAwareScheduleClient — scrapes route.rvt + trackpoll JSON for
real schedules without auth. T+0..2 day window. Tests against
captured HTML fixtures.
- BlobRouteClient — pulls the public Vercel blob route catalog
route-explorer's frontend reads (no auth, no Turnstile).
- DiagnosticLogger + LoggingURLSessionDelegate + DiagnosticsView —
device-shareable forensic trace. Boot header captures device, OS,
locale, UA; share-sheet export of session logs.
- TurnstileDebugView — live WKWebView gate inspector. Used to prove
the PAT-entitlement gap on a real device.
- RouteExplorerBrowserView — SFSafariViewController wrapper. Real
Safari clears Turnstile naturally; the in-app browser opens at
pre-filled search URLs. Surfaced from Search ("Open in
route-explorer") and Settings → Tools.
- RouteExplorerTokenStore + RouteExplorerSetupView — bookmarklet
capture flow (token round-tripped via flights://routeexplorer-token
URL scheme). Kept dormant for future use.
backend/ — Docker proxy attempts (Playwright, patchright, Camoufox).
All fail on Linux because Cloudflare auto-denies before the Turnstile
widget renders. Documented; kept as scaffolding for a future paid-
solver integration.
scripts/probe_flightaware.py — reference algorithm for the FA path.
scripts/probe_nodriver.py — local-Mac sanity check confirming the
gate clears with real macOS Chrome (proves the blocker is
fingerprint-level, not network-level).
7.2 KiB
TSA Wait-Times Data Feasibility Research
Date: 2026-05-31 Project: Flights iOS app Question: Is there a real, free, key-less, server-less source of TSA wait-time data we can ship? Short answer: No. Recommend Option B — keep the bundled baseline and honestly relabel the UI.
URLs investigated
| URL | Status | Notes |
|---|---|---|
https://www.tsa.gov/travel/security-screening/whatcanibring |
403 to WebFetch | Page is the "What Can I Bring" tool; not a wait-time source. |
https://www.tsa.gov/mobile |
200 | Marketing page for MyTSA app. No public API surface. |
https://apps.tsa.dhs.gov/MyTSAWebService/GetTSOWaitTimes.ashx |
302 → tsa.gov | The historically documented endpoint (used by GitHub taitcha/tsa-mashup and others) no longer returns data. Confirmed via curl -sI -L: BigIP load balancer issues a 302 Moved Temporarily to http://www.tsa.gov with Content-Length: 0. The MyTSAWebService is effectively retired. |
https://www.tsa.gov/data/apcp.xml |
404 Not Found | Companion airport-checkpoint metadata file the legacy API depended on. Confirmed dead via curl: HTTP/2 404 Not found. |
https://www.dhs.gov/mytsa-api-documentation |
403 to WebFetch | Documentation page still indexed but the underlying service is gone. |
https://catalog.data.gov/dataset/tsa-wait-times-january-2006-to-december-2015 |
200 | Archive only — Jan 2006 through Dec 2015. No real-time component. Useful for historical research, not for live wait estimates. |
https://catalog.data.gov/dataset/tsa-foia-reading-room-weekly-passenger-throughput-data |
200 | Weekly throughput (passenger counts), not wait minutes. Lagged. Not appropriate for "wait at checkpoint right now." |
https://www.tsawaittimes.com/api/ |
200 | Third-party paid API run by TayTech LLC (Wisconsin). $49.95/mo, $479.52/yr. Requires API key + paid sub. Self-disclaimer: "this website is not owned or affiliated with the TSA" and "wait times are estimates and may not be reflective of the actual experience." Data source is a mix of "government data, traveler contributions, and internal data." Violates the project's no-paid-API constraint. |
https://apps.apple.com/us/app/mytsa/id380200364 |
200 | MyTSA v4.5.0, last updated 2024-12-09. App itself still available but consumes the same dead internal feed. |
https://www.dhs.gov/check-wait-times |
200 | Marketing landing page. Points to MyTSA app only. |
https://github.com/taitcha/tsa-mashup |
200 | Open-source Python 2.7 demo. Hardcodes the now-dead GetTSOWaitTimes.ashx endpoint. Repo is unmaintained. |
https://www.ksat.com/news/local/2026/03/25/where-to-find-airport-security-wait-times-while-tsa-app-is-down/ |
200 | News article (March 2026): TSA website and MyTSA app currently show "no longer being updated" warnings due to government shutdown / staffing. Even the official channel is unreliable right now. |
https://developer.apple.com/wallet/ |
200 | PassKit boarding-pass integration with iOS 26 Maps gives walking-time-to-gate, not security wait time. No PassKit/Maps API exposes TSA queue data to third-party apps. |
What does and does not exist
Does not exist:
- A free, key-less, public, real-time TSA wait-times API for general developer use.
- A
data.govreal-time feed (only historical archives through 2015 + weekly throughput counts). - An Apple system framework that exposes TSA wait times to third-party apps (PassKit/Maps surface gate-walk timings only).
- A working successor to
GetTSOWaitTimes.ashx.
Does exist (but unusable for this project):
tsawaittimes.com— paid, third-party, partially crowdsourced, not affiliated with TSA. Violates the no-paid-API rule and would mislead users with non-official data branded as TSA.- Historical data.gov archive (2006–2015) — possibly useful for refining the bundled baseline once, but not for live use.
- MyTSA consumer app — only useful when a human reads the screen, not as a programmatic source. Also currently warning users that its own data is stale.
Recommendation: Option B — keep the bundled baseline, relabel honestly
Reasons to keep rather than drop:
- The current
tsa_wait_baseline.jsonalready gives a reasonable order-of-magnitude estimate for ~25 hubs by hour-of-day and weekday/weekend. For an airline employee planning a standby trip, "Tuesday 6am at ATL averages ~22 min" is genuinely useful context even if it isn't live. - A nonrev traveller looking at a flight detail benefits from any signal about checkpoint pressure, provided it is clearly labelled as a historical typical-value rather than a live measurement.
- Dropping the feature would lose information that we do have honestly. The fix is in the wording, not in the data.
What must change in the UI (Phase 3 fix, not this phase):
The basis string surfaced by TSAWaitTimesClient.waitEstimate(...) is what LiveFlightDetailSheet displays. Today it says "baseline (weekday)", "baseline (weekend)", or "estimated". These are not clear enough about provenance. Replace with exact wording:
| Current basis | Replace with (verbatim) |
|---|---|
"baseline (weekday)" |
"Typical wait — weekday avg, not live" |
"baseline (weekend)" |
"Typical wait — weekend avg, not live" |
"estimated" |
"Rough estimate — no live TSA feed" |
And in LiveFlightDetailSheet, the section header or footnote near the TSA row should read:
Wait times are historical typicals. TSA does not publish a public real-time feed; values shown are hour-of-day averages, not live measurements.
That sentence is the honest disclaimer to surface in the sheet. It can be a .footnote under the TSA row or part of an info Label.
Assumptions the reviewer should verify
- The
tsa_wait_baseline.jsonfile shipped inFlights/Resources/is hand-curated per theTSAWaitTimesClientdocstring. I did not verify the actual numbers in that JSON against historical TSA reports. If you want defensibility, the next pass should re-source the buckets from the data.gov 2006–2015 archive (or the weekly throughput dataset's recent values, used as a busyness proxy rather than literal wait minutes) and add asource:field to the JSON. - The
GetTSOWaitTimes.ashxendpoint redirects to the TSA homepage as observed today (2026-05-31) from this network. There is a small chance this is a transient outage tied to the ongoing shutdown rather than permanent retirement — but the airport-metadata XML being 404, plus public reporting that TSA's own app is showing "not being updated" warnings, makes me confident this is structural, not transient. If you want to be doubly safe, code the rewrite to try the endpoint with a short timeout and fall back to baseline silently — but I would not recommend spending the effort given how unreliable the upstream has proven. - The
tsawaittimes.compricing was scraped from their public marketing page. If you ever reconsider, re-verify current pricing before paying.
File summary
/Users/m4mini/Desktop/code/Flights/notes/tsa_research.md— this report (new)/Users/m4mini/Desktop/code/Flights/Flights/Services/TSAWaitTimesClient.swift— read only, not modified (per task instructions; Phase 3 will apply the relabel based on this recommendation)