In the Weeds: Building a Flight Price Tracker with Kiwi MCP
A technical guide to building an automated flight price monitoring system using Kiwi Flights MCP — track prices across flexible dates, get alerts on drops, and find deals that manual searching would miss.
The Problem with Manual Flight Search
Searching for flights manually is a losing game. Prices change multiple times per day. The cheapest option on Tuesday might be fifty percent more expensive by Thursday. Flexible date searches help, but who has time to check every day across multiple date ranges and routes?
The answer is automation. KKiwi Flights MCP provides programmatic access to flight search data — and combined with nn8n for scheduling and alerting, you can build a system that monitors prices continuously and alerts you only when conditions are favorable.
Architecture
The system runs every six hours, queries current prices for your tracked routes, stores historical data, analyzes trends, and alerts when prices drop below threshold or match favorable patterns.
Setting Up Kiwi MCP
KKiwi Flights MCP exposes the Kiwi.com search API through the Model Context Protocol. The key endpoints:
Search flights:
json{
"tool": "search_flights",
"parameters": {
"fly_from": "LAX",
"fly_to": "CDG",
"date_from": "01/06/2026",
"date_to": "15/06/2026",
"return_from": "08/06/2026",
"return_to": "22/06/2026",
"adults": 2,
"curr": "USD",
"sort": "price",
"limit": 20
}
}
The flexible date ranges are key — date_from/date_to define a window for departure, and return_from/return_to define a window for return. Kiwi searches all combinations within those windows.
Multi-city search:
json{
"tool": "search_flights",
"parameters": {
"fly_from": "LAX",
"fly_to": "CDG,LHR,AMS,BCN",
"date_from": "01/06/2026",
"date_to": "30/06/2026",
"nights_in_dst_from": 5,
"nights_in_dst_to": 10,
"curr": "USD"
}
}
This searches multiple destinations simultaneously with flexible trip duration. Perfect for "I want to go to Europe for a week in June — where is cheapest?"
The n8n Workflow
Here is the complete workflow in n8n:
Node 1: Cron Trigger — Runs every 6 hours
Node 2: Load Tracked Routes — Fetches your route configurations from a Postgres table:
sqlSELECT FROM tracked_routes WHERE active = true;
Node 3: Loop Over Routes — For each tracked route, query Kiwi MCP
Node 4: Kiwi Query — HTTP Request to Kiwi MCP with route parameters
Node 5: Store Results — Insert price data into historical table:
sqlINSERT INTO price_history (route_id, price, currency, departure, return_date, airline, queried_at)
VALUES ($1, $2, $3, $4, $5, $6, NOW());
Node 6: Analyze — Compare current price to historical data:
sqlSELECT
current_price,
avg_price_30d,
min_price_30d,
CASE
WHEN current_price < min_price_30d THEN 'ALL_TIME_LOW'
WHEN current_price < avg_price_30d 0.8 THEN 'SIGNIFICANT_DROP'
WHEN current_price < avg_price_30d 0.9 THEN 'MINOR_DROP'
ELSE 'NORMAL'
END AS alert_level
FROM price_analysis
WHERE route_id = $1;
Node 7: Alert — If alert_level is significant, notify via Slack or email
The Database Schema
sqlCREATE TABLE tracked_routes (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL, -- "LA to Paris summer 2026"
origin TEXT NOT NULL,
destination TEXT NOT NULL, -- Can be comma-separated for multi-city
date_from DATE,
date_to DATE,
return_from DATE,
return_to DATE,
passengers INT DEFAULT 1,
max_price DECIMAL, -- Alert threshold
active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE price_history (
id SERIAL PRIMARY KEY,
route_id INT REFERENCES tracked_routes(id),
price DECIMAL NOT NULL,
currency TEXT DEFAULT 'USD',
departure DATE,
return_date DATE,
airline TEXT,
stops INT,
duration_hours DECIMAL,
booking_url TEXT,
queried_at TIMESTAMPTZ DEFAULT NOW()
);
-- Materialized view for analysis
CREATE MATERIALIZED VIEW price_analysis AS
SELECT
route_id,
(SELECT price FROM price_history WHERE route_id = ph.route_id ORDER BY queried_at DESC LIMIT 1) AS current_price,
AVG(price) AS avg_price_30d,
MIN(price) AS min_price_30d,
MAX(price) AS max_price_30d,
STDDEV(price) AS price_volatility
FROM price_history ph
WHERE queried_at > NOW() - INTERVAL '30 days'
GROUP BY route_id;
Refresh the materialized view after each data collection run:
sqlREFRESH MATERIALIZED VIEW price_analysis;
Advanced: Price Prediction
With enough historical data, you can add a prediction layer. Flight prices follow patterns:
- Prices generally increase as departure approaches
- Tuesday/Wednesday bookings are often cheaper
- Prices spike around holidays
- There is often a "sweet spot" window where prices are lowest (typically 6-8 weeks before departure for domestic, 2-3 months for international)
Using ttxtai or a simple regression model, you can predict whether prices are likely to go lower or if now is the time to buy:
pythonimport numpy as np
from sklearn.linear_model import LinearRegression
def predict_price_direction(route_id, days_until_departure):
# Get historical prices at similar days-until-departure
prices = get_historical_prices(route_id, days_until_departure, window=7)
if len(prices) < 10:
return "INSUFFICIENT_DATA"
# Simple trend analysis
x = np.arange(len(prices)).reshape(-1, 1)
y = np.array(prices)
model = LinearRegression().fit(x, y)
trend = model.coef_[0]
if trend > 5:
return "RISING_BUY_NOW"
elif trend < -5:
return "FALLING_WAIT"
else:
return "STABLE"
The Alert Message
When the system detects a favorable price, the alert should be actionable:
🛫 PRICE DROP: LA → Paris
Current price: $487 per person
30-day average: $623
This is 22% below average.
Best option found:
Depart: June 8 (Mon), Air France direct
Return: June 16 (Tue), Air France 1 stop
Duration: 11h out, 14h return
Total for 2 passengers: $974
Trend: Prices have been falling for 3 days.
Prediction: Likely to hold for 24-48h.
Book: [direct link to booking]
This integrates with SSlack MCP for team or family channels where travel planning happens collaboratively.
Multi-Trip Optimization
For complex travel (multiple trips per year, family visiting different destinations), the tracker becomes a portfolio manager:
sql-- Annual travel budget tracker
SELECT
tr.name,
pa.current_price tr.passengers AS current_total,
pa.min_price_30d tr.passengers AS best_case,
CASE
WHEN pa.current_price <= tr.max_price THEN 'WITHIN_BUDGET'
ELSE 'OVER_BUDGET'
END AS budget_status
FROM tracked_routes tr
JOIN price_analysis pa ON tr.id = pa.route_id
WHERE tr.active = true
ORDER BY pa.current_price tr.passengers DESC;
Scaling with Neon
For personal use, any Postgres instance handles this. For a service (monitoring flights for multiple users), NNeon MCP provides serverless Postgres that scales to zero when idle — you only pay for the compute during the 4x daily price checks, not the 20 hours between them.
When to Book
The system should not just alert on drops — it should advise on timing. Combine current price data with:
- Days until departure (urgency factor)
- Price volatility (high volatility = wait, low volatility = buy now)
- Historical seasonal patterns
- Day-of-week pricing patterns
The best alert is: "This price is good AND likely to go up soon AND your departure is approaching. Book now." That requires all three factors aligning.
Build the tracker. Let it watch. Book when the data says go.
Your future self, sitting on that flight knowing they paid thirty percent less than the person next to them, will thank you.
Ratings & Reviews
0.0
out of 5
0 ratings
No reviews yet. Be the first to share your experience.
Tools in this post