Live flight price search + route watching from the command line, using live Google Flights data. Four small verbs, plain text or JSON out — no chat code, no account, no API key.
Ships as a pi coding agent skill but is a pure CLI: any agent, cron job, or script can drive it.
- (a)
flypi search FROM TO DATE [--return] [filters]— live fares, cheapest first - (b)
flypi watch add|list|rm|history ...— remember a trip and track its price - (c)
flypi check— re-search every watch, print/notify on drops - (d)
flypi notify off|cmd|agent— where alerts go: printed, piped to a command (ntfy/Telegram/anything), or delivered by your agent over chat
Search engine: fli (pip install flights),
the same reverse-engineered Google Flights API used by flightclaw.
- Python ≥ 3.10
No accounts, API keys, or services needed. For push alerts, the only extra is the free ntfy phone app.
flypi is a pi package:
pi install git:github.com/sarguru/flypi # or pin a release: ...@v0.5.0
cd ~/.pi/agent/git/github.com/sarguru/flypi
./scripts/setup.sh # one-time: venv + flight engineThe skill is then available to pi automatically (install with pi install;
update with pi update; remove with pi remove git:github.com/sarguru/flypi).
If a pi update replaces the checkout, re-run ./scripts/setup.sh once.
git clone https://github.com/sarguru/flypi.git
cd flypi
./scripts/setup.sh # creates .venv, installs the flight engine
./scripts/flypi --versionAs a pi skill, clone or symlink the repo into a skill directory such as
~/.pi/agent/skills/flypi, then run ./scripts/setup.sh.
./scripts/flypi search LHR CTA 2026-12-21 # one-way, cheapest first
./scripts/flypi search JFK LHR 2026-12-01 --return 2026-12-08 --stops nonstop
./scripts/flypi search LGW "CTA,PMO" 2026-12-21 --max-price 300
./scripts/flypi search LHR JFK 2026-12-01 --seat business --airlines BA,AA --jsonOutput: one line per option —
airline flight(s) | dep -> arr | price | stops | duration.
| flag | meaning |
|---|---|
--return YYYY-MM-DD |
round trip |
--until YYYY-MM-DD |
cheapest fare per day across a date range |
--stops nonstop |
direct only (any default) |
--seat business |
cabin: economy / premium_economy / business / first |
--airlines BA,AA / --exclude-airlines FR |
include / exclude carriers |
--max-price 300 |
only fares under the cap |
--adults 2 |
passenger count |
--currency EUR |
currency (default USD) |
--top N |
how many results (default 8) |
--json |
structured output |
Airports: IATA codes, comma-separated for several at once (LHR,LGW,
JFK,EWR,LGA). Dates are always YYYY-MM-DD.
./scripts/flypi watch add LHR,LGW "CTA,PMO" 2026-12-21 --return 2027-01-03 \
--target 400 --label "sicily xmas"
./scripts/flypi watch list
./scripts/flypi watch rm 3watch add immediately fetches today's cheapest fare and stores it as the
baseline. --target is an optional price goal.
./scripts/flypi watch history 1 # full price history of watch #1./scripts/flypi check # re-search everything
./scripts/flypi check --quiet # only print changes/errors (cron-friendly)check stores each result in history and prints a line per watch:
watch #2 (LHR,LGW -> CTA,PMO 2026-12-21 -> 2027-01-03): 367 (unchanged)
PRICE DROP watch #1 (LHR -> CTA 2026-12-21): 448 -> 367 (-81)
TARGET HIT watch #1: 367 <= target 400
Exit code is 1 when anything changed (drop or target hit) so cron can act
on it. Watches are polled sequentially with a pause between queries
(FLYPI_DELAY, default 3s — keep it; Google rate-limits). On HTTP 429 flypi
waits FLYPI_RETRY_SEC (default 90s) and retries once before failing cleanly.
flypi never sends anything on its own. When check sees a drop or target
hit, the event digest is printed — and handed to whatever notifier you
configured (default: printed only):
./scripts/flypi notify status # current config
./scripts/flypi notify cmd 'curl -d @- https://ntfy.sh/your-topic' # pipe to anything
./scripts/flypi notify agent [--jid <address>] # your agent alerts you
./scripts/flypi notify offOnly price drops and target hits are ever sent — "unchanged" checks stay silent.
- Install the ntfy app on your phone
- Pick a topic name and point flypi at it:
./scripts/flypi notify cmd 'curl -d @- https://ntfy.sh/your-topic'That's it — no account, no server, no pairing. A price drop on any watch pings your phone. (Your topic is public on the default server unless you protect it; anything secret goes in the message, not the topic.)
notify cmd pipes each event digest to the command's stdin, so the same hook
works for any destination you prefer — Telegram, a Slack webhook, an email
API, or your own script:
./scripts/flypi notify cmd 'curl -s -F chat_id=YOUR_ID -F text=@- https://api.telegram.org/botTOKEN/sendMessage'When flypi runs inside an agent with a chat channel (e.g. whatsapp-pi), you don't need any pairing, topic, or push app — alerts ride the same channel you already talk to the agent on:
./scripts/flypi notify agent --jid 130476962508942@s.whatsapp.netThe jid is stored as an opaque recipient address; flypi itself contains no
chat code. On a drop/target hit the operator agent sends you one concise
WhatsApp message (see the "Agent behavior" section in SKILL.md).
flypi check runs when something invokes it — you, a cron job, or pi
itself. If you keep a pi session running (e.g. the launchd + tmux setup
used by whatsapp-pi), this package ships an extension that turns that
long-lived session into the scheduler:
- Set the interval in the always-on pi's environment:
export FLYPI_CHECK_HOURS=6(orFLYPI_CHECK_MINUTES=360) - Restart that pi session.
From then on, the extension runs flypi check --quiet every 6h while pi is
alive; results are appended to ~/.flypi/checks.log, and any price
drop/target hit flows to your notifier — through your notify cmd
destination, or, when you subscribed with flypi notify agent --jid <addr>
and whatsapp-pi is online, by waking the agent to push you a WhatsApp
message (set FLYPI_CHECK_WAKE=0 to disable the wake-up). A /flypi-check
command in pi runs a check on demand. No timer starts unless the env var is
set, and a lock file prevents two pi sessions from checking doubly. (Classic
cron — flypi check --quiet every few hours, exit code 1 on changes —
remains a fine alternative when pi is not always on.)
State lives in ~/.flypi/ (flypi.db = watches + history, config.json =
notifier). Override the location with FLYPI_HOME.
./scripts/setup.sh # once
.venv/bin/python -m unittest discover -s tests -vAll offline — nothing in the test suite queries Google Flights.
- Uses an unofficial Google Flights access path via
fli; it can break upstream. Keep theflightspip package updated (./scripts/setup.sh). - Be gentle: personal checks every few hours are fine. On sustained HTTP 429
back off ~10–15 minutes; flypi already waits
FLYPI_RETRY_SEConce. - Prices are indicative fare engine quotes, not bookings.
MIT — see LICENSE. Not affiliated with Google or any airline.