by LetsFG
Agent-native flight & hotel search and booking — MCP server, CLI, and Python/JS SDKs. Hundreds of airlines plus the major booking sites, with per-flight reliability history. Bookable hotel rates with free cancellation, pay-later terms, and a 10% reservation fee.
# Add to your Claude Code skills
git clone https://github.com/LetsFG/LetsFGLast scanned: 5/4/2026
{
"issues": [],
"status": "PASSED",
"scannedAt": "2026-05-04T06:38:23.048Z",
"semgrepRan": false,
"npmAuditRan": true,
"pipAuditRan": true
}LetsFG is an open-source ai agents skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by LetsFG. Agent-native flight & hotel search and booking — MCP server, CLI, and Python/JS SDKs. Hundreds of airlines plus the major booking sites, with per-flight reliability history. Bookable hotel rates with free cancellation, pay-later terms, and a 10% reservation fee. It has 1,713 GitHub stars.
Yes. LetsFG passed SkillsLLM's automated security scan — a dependency vulnerability audit plus prompt-injection heuristics — with no high-severity issues. You can read the full report in the Security Report section on this page.
Clone the repository with "git clone https://github.com/LetsFG/LetsFG" and add it to your Claude Code skills directory (see the Installation section above). LetsFG ships a SKILL.md manifest, so compatible agents can discover and load it automatically.
LetsFG is primarily written in Python. It is open-source under LetsFG on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other AI Agents skills you can browse and compare side by side. Open the AI Agents category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh LetsFG against similar tools.
No comments yet. Be the first to share your thoughts!
MPP (updated 2026-07-30): the MPP (Machine Payments Protocol)
402challenge is live, as a card-free enrolment lane for agents holding a Tempo wallet. It costs $0.01 once, as verification only — MPP has no zero-amount intent, so a card-free rail has to settle something. Search and booking stay free. 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/setup-payment, and do NOT runletsfg registerorletsfg setup-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, run
letsfg auth— a zero-amount card setup (nothing charged), then search and book. See https://letsfg.co/for-agents.
Machine-readable skill manifest for AI agents and documentation indexers.
https://letsfg.co/developers/api/v1https://letsfg.co/developers/api/mcp (Streamable HTTP)letsfg · npm letsfg · npm letsfg-mcp| Mode | Best for | Speed | Cost |
|---|---|---|---|
| CLI / SDK / MCP (PFS Bearer token) | Almost every agent. Search + booking | 60–90 s | Free auth, free search |
Developer API (https://letsfg.co/developers) |
Business / commercial / high-volume | 2–5 s (discover) · 60–90 s (full search) | Prepaid credits; direct booking URLs, no per-booking fee |
Search hundreds of airlines AND the major booking sites (Google Flights, Skyscanner, Kiwi, Kayak, Momondo) in one call. Returns real-time prices plus per-flight reliability history.
book_flight directly — no unlock step. On the Developer API, offers must be unlocked before booking.Resolve a place name to the supplier city id that hotel search needs.
POST /api/v1/hotels/destinationsId as city_id and Name as city_name.Search real, bookable hotel inventory.
X-API-Key) only. The PFS Bearer token used for flights is rejected.POST /api/v1/hotels/searchprice (what the guest pays),
reservation_fee_now (the 10%), balance_to_supplier, balance_due_by,
free_cancellation_until, combination_id_v2session_id and the chosen
offer's combination_id_v2 — booking needs both.Search ground transfers — private cars, taxis, shared shuttles, airport express.
Search activities — tours, museum tickets, day trips via direct APIs and aggregators.
Resolve city names to IATA airport/city codes.
Confirm live price with airline and reveal the direct booking URL. Reserves the offer for 30 minutes.
/api/agent-book instead.POST /api/v1/bookings/unlocksetup_payment) for the Stripe path, or an MPP-capable client for the crypto path.WWW-Authenticate: Payment challenge — pay via Tempo USDC.e and retry. Otherwise add a card via setup_payment.unlocked = bt.unlock(offer_id) → returns UnlockResultletsfg unlock off_xxxconst unlocked = await bt.unlock(offerId)Book an offer.
POST /api/agent-book. No unlock step — search, then book directly. One
passenger per call. Returns either {"booked": true, "order_id": ...} or, when the booking
genuinely could not complete, {"booked": false, "booking_url": ...} — nothing charged either
way beyond the ticket price. Not a transient error; hand the user booking_url, don't retry.
Requires search_id (from the search result) alongside offer_id.
letsfg book off_xxx --search-id srch_xxx --passenger '{...}' --email you@example.combt.book(offer_id=..., passengers=[{...}], contact_email=..., search_id=...)unlock first. Creates a real airline reservation with PNR code, and
charges ticket price via Stripe before booking.
setup_payment first.Start a hotel booking. Returns a job, NOT a booking.
pay_link.POST /api/v1/hotels/bookget_hotel_booking until status is succeeded or failed.
This is what makes it impossible to charge a card and then lose the confirmation to a timeout.expected_price and expected_balance back exactly as search returned them,
or the booking is refused as a price mismatch — a guest is never charged a price they did not
agree to.Collect the result of a booking started with book_hotel.
GET /api/v1/hotels/booking/{booking_job_id}balance_due_by is the supplier's own auto-cancellation date, not advisory. Miss it and
the room is released.Release a hotel reservation.
balance_due_by; after that the hotel's own ladder applies and can reach
100%. The 10% reservation fee is NOT refunded.POST /api/v1/hotels/cancelRegister a new AI agent.
Attach a payment card for booking. Required before booking flights.
Get current agent's profile, usage stats, and payment status.
PFS (recommended — almost every agent): a Bearer token from letsfg auth — a
zero-amount card setup, nothing charged. Search and flight booking both work
with it; hotels do not (see below).
Authorization: Bearer eyJ...
Get a token: POST /api/agent-access/request → present a card at the returned
setup_url (or headlessly via a Stripe token) → POST /api/agent-access/verify
→ 90-day token. Full flow: https://letsfg.co/for-agents.
Developer API (business / high-volume): every endpoint except register
requires an X-API-Key header.
X-API-Key: trav_...
Get your key by calling POST /api/v1/agents/register with agent_name and email. The key is permanent — save it once.
Before your first unlock, attach a payment method via POST /api/v1/agents/setup-payment (or use the MPP crypto path on the 402 challenge).
1. POST /api/search → Search flights (FREE), returns search_id
2. POST /api/agent-book → Book directly — no unlock step, no LetsFG fee
1. POST /api/v1/agents/register → Get API key (once)
2. POST /api/v1/agents/setup-payment → Attach payment card (once)
3. POST /api/v1/flights/search → Search flights (FREE)
4. POST /api/v1/bookings/unlock → Unlock offer (1% fee, min $3) → returns booking_url
5. POST /api/v1/bookings/book → Book flight (ticket price charged via Stripe)
1. POST /api/v1/agents/setup-payment → Card on file (required for SEARCH too)
2. POST /api/v1/hotels/destinations → Place name → city_id
3. POST /api/v1/hotels/search → Bookable rates (free, card still required)
4. POST /api/v1/hotels/book → Returns booking_job_id — NOT a booking
5. GET /api/v1/hotels/booking/{job_id} → Poll ~20s until succeeded/failed
→ confirmation + pay_link + balance_due_by
6. POST /api/v1/hotels/cancel → Optional; free until balance_due_by
10% is charged to the card at step 4 as a non-refundable reservation fee; the balance is paid
directly to the supplier through pay_link by balance_due_by, which is the supplier's own
auto-cancellation date. Never repeat step 4 for the same rate — that books the room twice.
pip install letsfg
letsfg auth # zero-amount card setup, nothing charged — 90-day Bearer token
# Search flights — prints search_id, needed for book
letsfg search LHR JFK 2026-04-15
letsfg search LON BCN 2026-04-01 --return 2026-04-08 --cabin C --sort price
letsfg search GDN BER 2026-05-10 --adults 2 --children 1
# Resolve locations
letsfg locations "New York"
# Book — free, ticket price only, no LetsFG fee, no unlock step
letsfg book off_xxx --search-id srch_xxx \
--passenger '{"given_name":"John","family_name":"Doe","born_on":"1990-01-15","gender":"m"}' \
--email john.doe@example.com
# Machine-readable output
letsfg search GDN BER 2026-03-03 --json
Developer API instead? letsfg register + letsfg setup-payment once, then
letsfg search ... --api-key trav_..., letsfg unlock off_xxx --api-key trav_...,
letsfg book off_xxx --api-key trav_... --passenger '{"id":"pas_0",...}' --email ....
from letsfg import LetsFG
bt = LetsFG() # reads the Bearer token from `letsfg auth`
# Search
results = bt.search("LHR", "JFK", "2026-04-15")
for offer in results.offers:
print(f"{offer.price} {offer.currency} — {', '.join(offer.airlines)}")
# Book — free, ticket price only, no LetsFG fee, no unlock step
result = bt.book(
offer_id=results.offers[0].id,
passengers=[{
"given_name": "John",
"family_name": "Doe",
"born_on": "1990-01-15",
"gender": "m",
"phone_number": "+447123456789",
}],
contact_email="john@example.com",
search_id=results.search_id,
)
if result["booked"]:
print(f"Order: {result['order_id']}")
else:
print(f"Booking link (nothing charged): {result['booking_url']}")
Developer API instead? LetsFG(api_key="trav_..."), then bt.unlock(offer_id)
before bt.book(...) — returns a BookingResult with booking_reference (PNR).
Remote (Streamable HTTP) — Developer API only:
{
"mcpServers": {
"letsfg": {
"url": "https://letsfg.co/developers/api/mcp",
"headers": {
"X-API-Key": "trav_..."
}
}
}
}
Local (stdio) — free PFS path:
npm install -g letsfg-mcp
LETSFG_BEARER_TOKEN=eyJ... letsfg-mcp # token from `letsfg auth`
The local server also accepts LETSFG_API_KEY instead, for the Developer API.
book_flight dispatches automatically based on which one is set.
| Tool | Description | Cost |
|---|---|---|
search_flights |
Search hundreds of airlines via server-side engine | FREE |
resolve_location |
City name → IATA code | FREE |
book_flight |
Book an offer. PFS: direct, no unlock step. Developer API: requires unlock_flight_offer first |
Ticket price only, no LetsFG fee (PFS) |
unlock_flight_offer |
[Developer API only] Confirm price, reveal booking URL, reserve 30min | 1% of ticket (min $3) |
setup_payment |
[Developer API only] Attach payment card | FREE |
get_agent_profile |
View usage stats | FREE |
| Flag | API Field | Values | Default |
|---|---|---|---|
--adults |
adults |
1–9 | 1 |
--children |
children |
0–9 | 0 |
--infants |
infants |
0–9 | 0 |
--cabin |
cabin_class |
M (economy), W (premium), C (business), F (first) | (any) |
--return |
return_from |
YYYY-MM-DD | — |
--max-stops |
max_stopovers |
0–4 | 2 |
--sort |
sort |
price, duration | price |
--limit |
limit |
1–100 | 20 |
--currency |
currency |
EUR, USD, GBP, etc. | EUR |
| Code | Class | Description | Typical Use Case |
|---|---|---|---|
M |
Economy | Standard seating | Budget travel, most bookings |
W |
Premium Economy | Extra legroom, priority boarding | Long-haul comfort without business price |
C |
Business | Lie-flat on long-haul, lounge access | Corporate travel, 6+ hour flights |
F |
First | Private suites, premium dining | Ultra-premium routes (limited airlines) |
--json |
— | Output as JSON | — |
| Exception | HTTP Code | When |
|---|---|---|
AuthenticationError |
401 | Invalid or missing API key |
PaymentRequiredError |
402 | No payment method (legacy flow) |
OfferExpiredError |
410 | Offer no longer available |
LetsFGError |
422 | Invalid request parameters |
LetsFGError |
429 | Too many requests (retry with backoff) |
LetsFGError |
502 | Upstream airline/hotel API error |
from letsfg import LetsFG
from letsfg.connectors.auth import BearerTokenError
try:
bt = LetsFG() # reads the Bearer token from `letsfg auth`
flights = bt.search("LHR", "JFK", "2026-04-15")
except BearerTokenError:
print("Token expired or missing — run `letsfg auth` again")
Developer API:
from letsfg import LetsFG, AuthenticationError
try:
bt = LetsFG(api_key="trav_...")
flights = bt.search("LHR", "JFK", "2026-04-15")
except AuthenticationError:
# API key invalid or expired — re-register
creds = LetsFG.register("my-agent", "agent@example.com")
bt = LetsFG(api_key=creds["api_key"])
bt.setup_payment(token="tok_visa") # re-attach payment on the new key
import time
from letsfg import LetsFG, LetsFGError
def search_with_retry(bt, origin, dest, date, max_retries=3):
for attempt in range(max_retries):
try:
return bt.search(origin, dest, date)
except LetsFGError as e:
if "429" in str(e) or "rate limit" in str(e).lower():
time.sleep(2 ** attempt) # exponential backoff
elif "timeout" in str(e).lower() or "504" in str(e):
time.sleep(1)
else:
raise
raise LetsFGError("Max retries exceeded")
| Endpoint | Rate Limit | Typical Latency |
|---|---|---|
| Search flights | No hard limit (billing is the natural governor) | 2–5 s (discover) · 60–90 s (full search) |
| Resolve location | 120 req/min | <1s |
| Unlock | 20 req/min | 2-5s |
| Book | 10 req/min | 3-10s |
| Search hotels | 30 req/min | 3-10s |
| Register | 5 req/min | <1s |
| Action | Cost |
|---|---|
| Search (flights, hotels, transfers, activities) | Free |
| Resolve locations | Free |
| Register agent | Free |
| Setup payment | Free |
| View profile | Free |
| Book flight (PFS, no unlock needed) | The price shown on the offer — no LetsFG fee |
| Unlock offer (Developer API only) | 1% of ticket (min $3) — Stripe card or MPP crypto |
| Book flight (Developer API, after unlock) | The price shown on the offer |
| Hotel booking | Room price only |
| Hotel cancellation | Per cancellation policy |
MPP (updated 2026-07-30): the MPP (Machine Payments Protocol)
402challenge is live, as a card-free enrolment lane for agents holding a Tempo wallet. It costs $0.01 once, as verification only — MPP has no zero-amount intent, so a card-free rail has to settle something. Search and booking stay free. 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/setup-payment, and do NOT runletsfg registerorletsfg setup-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, run
letsfg auth— a zero-amount card setup (nothing charged), then search and book. See https://letsfg.co/for-agents.
Finding a flight or hotel shouldn't mean checking 47 tabs. Or 3 hours of searching. Or having that feeling you could've got a better deal if you'd just waited a little longer.
So we built something about it. No markup. No tracking. No price that goes up because you looked twice.
Hundreds of airlines. Real prices. One function call.
LetsFG gives your AI agent flight and hotel search and booking superpowers. Our server-side engine scans the entire world for the cheapest price. Search is free. Real airline tickets, booked through us or handed to you as a direct link.
The same flight costs $20–$50 less because you skip OTA inflation, cookie tracking, and surge pricing.
CLI or scripts: Run letsfg auth once — it puts a payment method on file through a zero-amount Stripe setup (nothing is charged) and returns a 90-day token. Then letsfg search and letsfg book hit our cloud engine. Developer API: a separate paid product for high-volume commercial use; most agents do not need it. → Get started
Your agent can now book hotels, not just flights. Same API key, same card on file.
from letsfg import LetsFG
lfg = LetsFG()
city = lfg.hotel_destinations("Warsaw")[0]
stays = lfg.search_hotels(
city_id=city["Id"], city_name=city["Name"],
check_in="2026-11-10", check_out="2026-11-12", adults=2,
)
hotel = stays["hotels"][0]
offer = hotel["offers"][0]
print(hotel["name"], offer["price"], stays["currency"])
# Hotel Gromada Warszawa Centrum 669.86 PLN
booking = lfg.book_hotel_and_wait(
session_id=stays["session_id"],
hotel_code=hotel["hotel_code"],
combination_id_v2=offer["combination_id_v2"],
expected_price=offer["price"],
expected_balance=offer["balance_to_supplier"],
city_id=city["Id"], city_name=city["Name"],
check_in="2026-11-10", check_out="2026-11-12",
guests=[{"title": "Mr", "first_name": "Jan", "last_name": "Kowalski"}],
email="guest@example.com", phone="512345678",
)
print(booking["confirmation"], booking["pay_link"])
10% now, the rest to the hotel later. At booking we charge 10% of the price
to your card as a reservation fee. The remaining balance is paid directly to
the supplier through a pay_link we return — we never hold it.
balance_due_by is the supplier's own auto-cancellation date, not a date we
invent. Miss it and the room is released.
The 10% is non-refundable. Cancelling before balance_due_by costs nothing
else; after it, the hotel's own cancellation ladder applies and can reach 100%.
That ladder ships in the booking's terms, so you can always see the cost before
you cancel.
book_hotel returns a booking_job_id, not a
booking — the real thing takes minutes. Poll hotel_booking(job_id) until
status is succeeded or failed, or call book_hotel_and_wait and let the
SDK do it. This is not ceremony: it is what makes it impossible to charge a
card and then lose the confirmation to a timeout.book_hotel twice for the same
rate books the room twice and charges two reservation fees.price is what the guest pays. There is no wholesale figure in the response to
quote by mistake.import { LetsFG } from 'letsfg';
const lfg = new LetsFG({ apiKey: process.env.LETSFG_API_KEY });
const [city] = await lfg.hotelDestinations('Warsaw');
const stays = await lfg.searchHotels({
cityId: city.Id, cityName: city.Name,
checkIn: '2026-11-10', checkOut: '2026-11-12', adults: 2,
});
const booking = await lfg.bookHotelAndWait({ /* ...offer + guest details... */ });
console.log(booking.confirmation, booking.pay_link);
Five new tools, in the order you call them: resolve_hotel_city →
search_hotels → book_hotel → get_hotel_booking → cancel_hotel_booking.
| Path 1 — CLI / SDK | Path 2 — PFS (Programmatic Flight Search via letsfg.co) | Path 3 — Developer API | |
|---|---|---|---|
| Best for | Developers, personal use, AI agents — e |