Building AI Agents with BoostedTravel¶
Guidelines for building autonomous AI agents that search, evaluate, and book flights.
Architecture¶
User request → Agent parses intent → Resolve locations → Search (free)
→ Filter & rank offers → Present to user → Unlock best ($1) → Book (free)
Agent Best Practices¶
-
Always resolve locations first. City names are ambiguous — "London" could be LHR, LGW, STN, LCY, or LTN. Use
resolve_location()to get IATA codes, then let the user confirm if multiple options exist. -
Search is free — use it liberally. Search multiple dates, multiple origin/destination pairs, different cabin classes. Build a complete picture before spending $1 on unlock.
-
Understand the 30-minute expiration. After unlocking, you have 30 minutes to book. If the window expires, you must search again (free) and unlock again ($1). Plan your workflow to minimize the gap between unlock and book.
-
Handle price changes gracefully. Search prices are real-time snapshots. The unlock step confirms the actual current price with the airline. If the confirmed price differs significantly from the search price, inform the user before proceeding to book.
-
Map passenger IDs correctly. Search returns
passenger_ids(e.g.,["pas_0", "pas_1"]). When booking with multiple passengers, each passenger dict must include the correctidfrom this list. The first adult getspas_0, second getspas_1, etc. -
Use REAL passenger details. Airlines send e-tickets to the contact email. Names must match the passenger's passport or government ID. Never use placeholder data.
Handling Edge Cases¶
from boostedtravel import (
BoostedTravel, BoostedTravelError,
PaymentRequiredError, OfferExpiredError,
)
# Retry on expired offers
def resilient_book(bt, origin, dest, date, passengers, email, max_retries=2):
for attempt in range(max_retries + 1):
flights = bt.search(origin, dest, date)
if not flights.offers:
return None
try:
unlocked = bt.unlock(flights.cheapest.id)
booking = bt.book(
offer_id=unlocked.offer_id,
passengers=[{**p, "id": pid} for p, pid in zip(passengers, flights.passenger_ids)],
contact_email=email,
)
return booking
except OfferExpiredError:
if attempt < max_retries:
print(f"Offer expired, retrying ({attempt + 1}/{max_retries})...")
continue
raise
except PaymentRequiredError:
print("Payment method not set up — call bt.setup_payment()")
raise
# Compare prices across dates intelligently
def find_cheapest_date(bt, origin, dest, dates):
"""Search multiple dates (free) and return the cheapest option."""
best = None
for date in dates:
try:
result = bt.search(origin, dest, date)
if result.offers and (best is None or result.cheapest.price < best[1].price):
best = (date, result.cheapest, result.passenger_ids)
except BoostedTravelError:
continue # Skip dates with no routes
return best # (date, offer, passenger_ids) or None
Rate Limits and Timeouts¶
The API has generous rate limits. Search is unlimited and free, so you can make many requests without cost.
| Endpoint | Rate Limit | Timeout |
|---|---|---|
| Search | 60 req/min per agent | 30s (airline APIs can be slow) |
| Resolve location | 120 req/min per agent | 5s |
| Unlock | 20 req/min per agent | 15s |
| Book | 10 req/min per agent | 30s |
Handle rate limits and timeouts in production:
import time
from boostedtravel import BoostedTravel, BoostedTravelError
bt = BoostedTravel()
def search_with_retry(origin, dest, date, max_retries=3):
"""Retry with exponential backoff on rate limit or timeout."""
for attempt in range(max_retries):
try:
return bt.search(origin, dest, date)
except BoostedTravelError as e:
if "rate limit" in str(e).lower() or "429" in str(e):
wait = 2 ** attempt # 1s, 2s, 4s
print(f"Rate limited, waiting {wait}s...")
time.sleep(wait)
elif "timeout" in str(e).lower() or "504" in str(e):
print(f"Timeout, retrying ({attempt + 1}/{max_retries})...")
time.sleep(1)
else:
raise
raise BoostedTravelError("Max retries exceeded")
Advanced Preference Evaluation¶
Rather than always picking the cheapest flight, score offers by weighted criteria:
def score_offer(offer, preferences=None):
"""Score a flight offer by multiple criteria (lower = better).
preferences: dict with weights, e.g.:
{"price": 0.4, "duration": 0.3, "stops": 0.2, "airline_pref": 0.1}
"""
prefs = preferences or {"price": 0.4, "duration": 0.3, "stops": 0.2, "airline_pref": 0.1}
preferred_airlines = {"British Airways", "Delta", "United", "Lufthansa"}
# Normalize factors (0-1 scale, lower is better)
price_score = offer.price / 2000 # Normalize against $2000 baseline
duration_hours = offer.outbound.total_duration_seconds / 3600
duration_score = duration_hours / 24 # Normalize against 24h baseline
stops_score = offer.outbound.stopovers / 3 # Normalize against 3 stops
airline_score = 0 if any(a in preferred_airlines for a in offer.airlines) else 1
return (
prefs["price"] * price_score +
prefs["duration"] * duration_score +
prefs["stops"] * stops_score +
prefs["airline_pref"] * airline_score
)
# Usage: find best offer considering multiple criteria
flights = bt.search("LHR", "JFK", "2026-06-01", limit=50)
best = min(flights.offers, key=lambda o: score_offer(o, {
"price": 0.3, # Price matters, but not everything
"duration": 0.4, # Shortest travel time is priority
"stops": 0.2, # Prefer direct flights
"airline_pref": 0.1 # Slight preference for known airlines
}))
print(f"Best overall: {best.airlines[0]} ${best.price} — {best.outbound.stopovers} stops")
Data Persistence for Price Tracking¶
For agents that track prices over time or compare across sessions:
import json
from datetime import datetime
from pathlib import Path
CACHE_FILE = Path("flight_price_history.json")
def load_price_history():
if CACHE_FILE.exists():
return json.loads(CACHE_FILE.read_text())
return {}
def save_search_result(origin, dest, date, result):
"""Save search results for later comparison."""
history = load_price_history()
key = f"{origin}-{dest}-{date}"
if key not in history:
history[key] = []
history[key].append({
"searched_at": datetime.utcnow().isoformat(),
"cheapest_price": result.cheapest.price if result.offers else None,
"total_offers": result.total_results,
"airlines": list(set(a for o in result.offers[:5] for a in o.airlines)),
})
CACHE_FILE.write_text(json.dumps(history, indent=2))
def get_price_trend(origin, dest, date):
"""Check if prices are rising or falling for a route."""
history = load_price_history()
key = f"{origin}-{dest}-{date}"
entries = history.get(key, [])
if len(entries) < 2:
return "insufficient_data"
prices = [e["cheapest_price"] for e in entries if e["cheapest_price"]]
if prices[-1] < prices[0]:
return f"falling (${prices[0]} → ${prices[-1]})"
return f"rising (${prices[0]} → ${prices[-1]})"