Heading · flights.sahirm.com
The alert is real, but it isn't LLM tokens and it isn't 75%. It's the AeroDataBox flight-data plan on RapidAPI, and it's at 87.5% with nine days left in the cycle. Measured live from RapidAPI's rate-limit headers on 29 Aug 2026, 18:34 UTC.
AeroDataBox BASIC, the $5 tier. Two independent caps: API units and request count. Only one of them is in trouble.
Requests used: 497 of 2,400 (20.7%). Units used: 525 of 600 (87.5%). You are nowhere near the request cap and almost out of units — so "just make fewer calls" is only half the story, and the request counter you'd naturally watch is the one that looks healthy.
At ~1.06 units per request, essentially every call costs a unit. The unit cap is 4× tighter than the request cap, so the unit cap is the only one that matters. Budget against 600, not 2,400.
This is also not the first time. RapidAPI sent an 85% warning on 31 Jul and a 100%-exhausted notice on 2 Aug — the previous cycle was fully burned through with a week to spare. Today's 85% email is the same pattern repeating.
Single measure: units consumed this cycle. The first five bars are modelled from the code paths and the flight/subscription rows in the production database; the last is the residual. Only the total, the request count and the flight data are measured — see the evidence table below.
The biggest finding is that bar. Roughly 43% of the spend cannot be attributed to anything, because the app has no API metering. There is no usage table in the schema, completion.usage is never read, and the only accounting is a console.log that tags just three of the call sites — poll, schedule-diff and subscribe.
Everything else spends invisibly: the entire second AeroDataBox client in src/ingest/validate.ts (which bypasses adbFetch and runs on every parsed email, every confirm, every manual add and every paste import), plus all the balance, refill, delete and orphan-sweep calls. Fix the metering first — otherwise the next fix is a guess.
| What | Value | Source |
|---|---|---|
| Unit cap / remaining | 600 / 75 | x-ratelimit-api-units-*, live probe |
| Request cap / remaining | 2,400 / 1,903 | x-ratelimit-requests-*, live probe |
| Cycle reset | 801,486 s ≈ 8 Sep | x-ratelimit-…-reset |
| Flights in the whole database | 19 | flights table, prod |
| Legs that flew this cycle | 6 | flights, dep ≥ 8 Aug |
| Active subscriptions | 2 | alert_subscriptions |
| Dead subscription rows | 35 | swept one-by-one, daily |
| Status events written, 21 days | 31 poll · 41 webhook | flight_status_events |
| Reconcile runs | 21 (one per day) | ops_log |
| Subscription retry exhaustions | 3 × 6 attempts | adb.subscription_retries_exhausted |
| Webhook credits left today | 15 of 50 | kv_state → adb.credits |
| Prior cycle outcome | 100% exhausted 2 Aug | RapidAPI email |
Sanity check on the scale of the problem: 19 flights ever, 6 flown this cycle, 2 live subscriptions — and 525 units. That is ~87 units per flown leg. The plan allows about 8/day. Nothing here is user load; it's the machinery around the flights.
src/ingest/validate.ts through adbFetch, tag every call site, and persist a per-call row. Right now 43% of spend is invisible and every other fix is unmeasurable.src/alerts/register.ts only skips a poll if a webhook landed in the last 75 minutes. A healthy subscription on an on-time flight sends nothing, so the skip never fires and the flight pays a unit an hour for 8–14 hours — for data the webhook would have pushed free.LH1960 / AI4174.dateLocal from the subscription body.webhook creditsingest.parse has retryLimit: 5 and never checks the already-persisted email.llmOutput, so one email can pay for extraction — and its AeroDataBox validation — up to six times.Separately: the [positions] sampler is returning HTTP 403 on every call against airplanes.live. That one is free and keyless so it costs nothing, but the live-map feature it powers is silently dead — worth a look on its own merits.