flight-search — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited flight-search (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
MPP: a card-free enrolment lane for agents holding a Tempo wallet is offered only when the402fromPOST /api/agent-access/requestcarries anmppobject. It costs $0.01 once, as verification only. Search stays free and booking costs the price on the offer. Earlier revisions of this page described an MPP charge at unlock time; that never shipped. See <https://letsfg.co/for-agents>.
### ⚠️ Agents: do not create a Developer API account
>
Do NOT call/developers/api/v1/agents/registeror/developers/api/v1/agents/connect-payment, and do NOT runletsfg registerorletsfg connect-payment. Those belong to a separate, paid, prepaid-balance product for high-volume commercial integrations and create a billing account you almost certainly do not want.
>
To search and book flights, connect LetsFG as an MCP server at https://letsfg.co/mcp and approve it — the consent at <https://letsfg.co/connect> is one tap and needs no card. Then search and book. See <https://letsfg.co/for-agents>.Agent-native flight search and booking via the LetsFG cloud engine. Hundreds of airlines plus the major booking sites, Compares airlines and the major booking sites in one pass, with per-flight reliability history.
Agent flow (PFS): Search (free) → Book (POST /api/agent-book, the price shown on the offer) → Poll (POST /api/agent-book/status). Booking works exactly like the website checkout: the fare plus LetsFG's markup is held on the card connected at <https://letsfg.co/connect> (not taken), a LetsFG booking agent buys the ticket from the seller, and the hold is captured only once a real airline PNR exists. A failed booking releases the hold — nothing charged. It takes 4–11 minutes; over the MCP the two steps are book_flight and get_flight_booking. There is no unlock step on either lane.
Claude (claude.ai / Claude Desktop): add LetsFG from Claude's connector directory, <https://claude.ai/directory/letsfg> - no config.
Remote (no install, always latest):
{
"mcpServers": {
"letsfg": {
"url": "https://letsfg.co/mcp",
"headers": {
"X-API-Key": "trav_your_api_key"
}
}
}
}Local (stdio):
{
"mcpServers": {
"letsfg": {
"command": "npx",
"args": ["-y", "letsfg-mcp"],
"env": {
"LETSFG_API_KEY": "trav_your_api_key"
}
}
}
}pip install letsfg
letsfg search LHR BCN 2026-06-15from letsfg import LetsFG
bt = LetsFG(api_key="trav_...")
flights = bt.search("LHR", "JFK", "2026-04-15")letsfg register --name my-agent --email [email protected]Then connect a payment method (nothing is charged; there is no unlock step):
letsfg connect-payment # prints a link to open in a browserCity names are ambiguous — "London" = LHR, LGW, STN, LCY, LTN. Always resolve first:
letsfg locations "London"
# LON London (all airports)
# LHR Heathrow
# LGW Gatwicklocations = bt.resolve_location("London")
# Use city code "LON" for all airports, or specific airport "LHR"flights = bt.search("LON", "BCN", "2026-04-01")
# Round trip:
flights = bt.search("LON", "BCN", "2026-04-01", return_date="2026-04-08")
# Multi-passenger, business class:
flights = bt.search("LHR", "SIN", "2026-06-01", adults=2, children=1, cabin_class="C")letsfg search LON BCN 2026-04-01 --return 2026-04-08 --sort price --jsonSearch returns structured offers:
{
"passenger_ids": ["pas_0"],
"total_results": 47,
"offers": [{
"id": "off_xxx",
"price": 89.50,
"currency": "EUR",
"airlines": ["Ryanair"],
"route": "STN → BCN",
"duration_seconds": 7800,
"stopovers": 0,
"conditions": {
"refund_before_departure": "not_allowed",
"change_before_departure": "allowed_with_fee"
}
}]
}There is no unlock step on either lane any more, and the route answers 410 Gone. Unlock existed to confirm a live price before charging; booking now HOLDS the fare and captures only against a real airline PNR, so a moved price surfaces as a question to accept or decline rather than a surprise charge. Call book_flight (PFS) or POST /flights/book (Developer API) directly.
The fare is held on the connected card, a LetsFG booking agent buys the ticket, and the hold is captured only against a real airline PNR; a failed booking releases it. book() starts the booking (4–11 minutes); book_and_wait() blocks until it settles.
booking = bt.book(
offer_id=flights.cheapest.id,
search_id=flights.search_id, # an offer is bookable only inside its own search
passengers=[{
"id": flights.passenger_ids[0],
"given_name": "John",
"family_name": "Doe",
"born_on": "1990-01-15",
"gender": "m",
"title": "mr",
"email": "[email protected]"
}],
contact_email="[email protected]",
idempotency_key="unique-booking-key-123"
)
print(booking) # the started booking — poll it until completed | failed | needs_attentionresolve_location() first.passenger_ids. Each booking passenger must include the correct id.# Compare multiple dates (all FREE)
dates = ["2026-04-01", "2026-04-02", "2026-04-03"]
best = None
for date in dates:
result = bt.search("LON", "BCN", date)
if result.offers and (best is None or result.cheapest.price < best[1].price):
# keep the search_id too: an offer is bookable only inside the search that produced it
best = (date, result.cheapest, result.search_id)
# Book only the winner — there is no unlock step
booking = bt.book(best[1].id, passengers=[{...}], contact_email="[email protected]",
search_id=best[2])flights = bt.search("LHR", "JFK", "2026-06-01", limit=50)
candidates = [
o for o in flights.offers
if o.outbound.stopovers == 0
and o.outbound.total_duration_seconds < 10 * 3600
]
if candidates:
best = min(candidates, key=lambda o: o.price)
booking = bt.book(best.id, passengers=[{...}], contact_email="[email protected]",
search_id=flights.search_id)# Search with stops allowed (default max_stopovers=2)
flights = bt.search("GDN", "BKK", "2026-06-15", max_stopovers=2)
# Filter by connection quality
good_connections = [
o for o in flights.offers
if o.outbound.stopovers <= 1
and o.outbound.total_duration_seconds < 18 * 3600
]
# Virtual interlining finds cross-airline combos automatically
# e.g., Wizz Air GDN→VIE + Thai Airways VIE→BKK| Error | Category | Action |
|---|---|---|
SUPPLIER_TIMEOUT (504) | Transient | Retry after 1-5s |
RATE_LIMITED (429) | Transient | Wait and retry |
INVALID_IATA (422) | Validation | Use resolve_location() to fix |
OFFER_EXPIRED (410) | Business | Search again for fresh offers |
PAYMENT_REQUIRED (402) | Business | PFS: connect a card at the add_card_url (https://letsfg.co/connect). Developer API: letsfg connect-payment |
FARE_CHANGED (409) | Business | The fare moved at checkout — answer the price_change question to accept or decline it |
from letsfg import LetsFG, OfferExpiredError, PaymentRequiredError
try:
booking = bt.book(offer_id, passengers=[{...}], contact_email="[email protected]",
search_id=search_id)
except OfferExpiredError:
# Airline sold the seats — search again and book from the fresh results
flights = bt.search(origin, dest, date)
except PaymentRequiredError:
# No card on file — Developer API: letsfg connect-payment; PFS: https://letsfg.co/connect
print("Connect a card first")| Flag | Default | Description |
|---|---|---|
--return / -r | _(one-way)_ | Return date (YYYY-MM-DD) |
--adults / -a | 1 | Number of adults (1–9) |
--children | 0 | Children (2–11 years) |
--cabin / -c | _(any)_ | M economy, W premium, C business, F first |
--max-stops / -s | 2 | Max stopovers (0–4) |
--currency | EUR | Currency code |
--limit / -l | 20 | Max results (1–100) |
--sort | price | price or duration |
--json / -j | JSON output |
| Operation | Cost | Safe to Retry | Idempotent |
|---|---|---|---|
search | Free | Yes | Yes |
resolve_location | Free | Yes | Yes |
unlock | RETIRED 2026-09-08 — answers 410 Gone | — | — |
book | Price shown on the offer | Developer API: only with idempotency_key. PFS: no — a second call places a second hold; poll /api/agent-book/status instead | With key: yes |
Load only when needed:
| File | Load When |
|---|---|
| api-reference.md | Need full API endpoint details, request/response schemas |
| mcp-setup.md | Setting up MCP server for specific clients |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.