Flight Price Tracking API
This minimal Python tracker searches one route, stores its cheapest fare in SQLite, alerts on a target price or 10% drop, and fetches a booking link only when an alert fires.
A complete Python fare tracker
Install requests, save this as tracker.py, and set IGNAV_API_KEY in the process environment. Pass a fixed origin, destination, and departure date on each run.
import os
import sqlite3
import sys
from datetime import UTC, datetime
import requests
API_KEY = os.environ["IGNAV_API_KEY"]
API = "https://ignav.com/api"
if len(sys.argv) != 4:
raise SystemExit("Usage: python tracker.py ORIGIN DESTINATION YYYY-MM-DD")
ORIGIN, DESTINATION, DEPARTURE_DATE = sys.argv[1:4]
TARGET_PRICE = 300.0
DROP_PERCENT = 10
ROUTE = {
"origin": ORIGIN,
"destination": DESTINATION,
"departure_date": DEPARTURE_DATE,
}
response = requests.post(
f"{API}/fares/one-way",
headers={"X-Api-Key": API_KEY},
json=ROUTE,
timeout=60,
)
response.raise_for_status()
itineraries = [
item
for item in response.json()["itineraries"]
if item["price"]["status"] == "verified"
]
if not itineraries:
raise SystemExit("No verified fares")
best = min(itineraries, key=lambda item: float(item["price"]["amount"]))
price = float(best["price"]["amount"])
currency = best["price"]["currency"]
checked_at = datetime.now(UTC).isoformat()
with sqlite3.connect("fares.db") as db:
db.execute("""CREATE TABLE IF NOT EXISTS fare_checks (
id INTEGER PRIMARY KEY,
origin TEXT NOT NULL,
destination TEXT NOT NULL,
departure_date TEXT NOT NULL,
checked_at TEXT NOT NULL,
price REAL NOT NULL,
currency TEXT NOT NULL,
ignav_id TEXT NOT NULL
)""")
previous = db.execute(
"""SELECT price FROM fare_checks
WHERE origin = ? AND destination = ? AND departure_date = ?
ORDER BY checked_at DESC LIMIT 1""",
(ORIGIN, DESTINATION, DEPARTURE_DATE),
).fetchone()
db.execute(
"""INSERT INTO fare_checks
(origin, destination, departure_date, checked_at, price, currency, ignav_id)
VALUES (?, ?, ?, ?, ?, ?, ?)""",
(
ORIGIN,
DESTINATION,
DEPARTURE_DATE,
checked_at,
price,
currency,
best["ignav_id"],
),
)
dropped = (
previous is not None
and price <= previous[0] * (1 - DROP_PERCENT / 100)
)
if price <= TARGET_PRICE or dropped:
booking_response = requests.post(
f"{API}/fares/booking-links",
headers={"X-Api-Key": API_KEY},
json={"ignav_id": best["ignav_id"]},
timeout=60,
)
booking_response.raise_for_status()
links = [
link["url"]
for option in booking_response.json()["booking_options"]
for link in option["links"]
]
print(f"Alert: {price:.2f} {currency} for {ORIGIN}-{DESTINATION}")
if links:
print(f"Book: {links[0]}")Run it with, for example, python tracker.py SFO HND 2026-11-15. Change TARGET_PRICE and DROP_PERCENT to match your alert rules. Replace the final print calls with your email, push, or webhook sender.
Schedule the poll
Run each saved route every six hours. A cron entry can call the same command at 0 */6 * * *; keep the API key in the scheduler's secret environment, not in the script. Polling more than once per hour for the same route and date is rarely useful.
Request cost
One route checked every six hours uses about 120 successful fare-search requests in 30 days. Ten routes use about 1,200: $2.40 at the paid rate, or $0.40 if the full one-time 1,000 free-request balance is still unused. The booking-links request runs only when an alert fires.
Production notes
Only successful requests (HTTP 200) are billed. Empty itinerary arrays are valid successful responses — they mean no flights matched, not that the request failed.
Start monitoring one to three months before departure and stop a few days before. The example skips unverified price hints so they cannot trigger alerts. Add retries for transient failures before deploying; see the production checklist for the supported retry behavior.
Ready to get started?
Create a free account to get your API key, or try the playground — no signup required.