VOOZH about

URL: https://pypi.org/project/letsfg/

โ‡ฑ letsfg ยท PyPI


Skip to main content

letsfg 2026.5.79

pip install letsfg

Latest release

Released:

Flight search & booking for AI agents. 200+ airline connectors run locally, free. Direct booking URLs via letsfg.co concierge or Developer API.

Navigation

Verified details

These details have been verified by PyPI
Maintainers
๐Ÿ‘ Avatar for amjaworsky from gravatar.com
amjaworsky

Unverified details

These details have not been verified by PyPI
Project links
Meta
  • License: MIT License (MIT)
  • Author: LetsFG
  • Tags agent , ai , airline , autonomous , booking , cli , easyjet , flights , low-cost , mcp , ndc , openai , ryanair , scraper , travel , wizzair
  • Requires: Python >=3.10
  • Provides-Extra: all , dev

Project description

LetsFG โ€” Your AI agent just learned to book flights.

200+ airline connectors. Real prices. One function call. Search 400+ airlines at raw airline prices โ€” $20โ€“$50 cheaper than Booking.com, Kayak, and other OTAs.

๐Ÿ‘ GitHub stars
๐Ÿ‘ PyPI

Three ways to use LetsFG

Local (this SDK) PFS (Programmatic Flight Search via letsfg.co) Developer API
Search cost Free Free (Twitter/X token, one-time setup) Prepaid credits
Booking URL 1% concierge fee (min $3) via letsfg.co 1% concierge fee (min $3) via letsfg.co Direct airline URL, no fee
Speed 20โ€“40 s (fast mode) ยท 1โ€“15 min (full) 60โ€“90 s 2โ€“5 s (discover) ยท 60โ€“90 s (full)
Setup pip install letsfg Twitter/X challenge โ†’ letsfg.co/for-agents letsfg.co/developers

Want direct airline URLs without any per-booking fee? Use the Developer API โ€” prepaid credits, results in seconds, no checkout step.

Install

pipinstallletsfg

Search flights immediately โ€” no account, no API key:

letsfgsearch-localLHRBCN2026-06-15

That fires 200+ airline connectors on your machine. Search is free. Booking links are delivered via the letsfg.co concierge flow (1% fee, min $3) โ€” see Unlocking offer results below.

# Search using only Brazilian sites
letsfgsearchBSBSDU2026-05-24--countryBR
# Or by region
letsfgsearchGRULIM2026-06-15--regionlatin-america

Short on time? Use --mode fast to search only OTAs + key airlines (~25 connectors, 20-40s instead of 6+ min):

letsfgsearch-localLHRBCN2026-06-15--modefast

Authentication

fromletsfgimport LetsFG

# Register (one-time, no auth needed)
creds = LetsFG.register("my-agent", "agent@example.com")
print(creds["api_key"]) # "trav_xxxxx..." โ€” save this

# Option A: Pass API key directly
bt = LetsFG(api_key="trav_...")

# Option B: Set LETSFG_API_KEY env var, then:
bt = LetsFG()

# Setup payment (required before unlock) โ€” two options:

# Option 1: Stripe test token (for development)
bt.setup_payment(token="tok_visa")

# Option 2: Stripe PaymentMethod ID (from Stripe.js or Elements)
bt.setup_payment(payment_method_id="pm_1234567890")

The API accepts only Stripe-generated tokens or payment_method_id values โ€” raw card numbers are not accepted.

The API key is sent as X-API-Key header on every request. The SDK handles this automatically.

Verify Your Credentials

# Check that auth + payment are working
profile = bt.me()
print(f"Agent: {profile['agent_name']}")
print(f"Payment: {profile.get('payment_status','not set up')}")
print(f"Searches: {profile.get('search_count',0)}")

Auth Failure Recovery

fromletsfgimport LetsFG, AuthenticationError

try:
 bt = LetsFG(api_key="trav_...")
 flights = bt.search("LHR", "JFK", "2026-04-15")
except AuthenticationError:
 # Key invalid or expired โ€” re-register to get a new one
 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 new key
 flights = bt.search("LHR", "JFK", "2026-04-15")

Quick Start (Python)

fromletsfgimport LetsFG

bt = LetsFG(api_key="trav_...")

# Search flights โ€” FREE
flights = bt.search("GDN", "BER", "2026-03-03")
print(f"{flights.total_results} offers, cheapest: {flights.cheapest.summary()}")

# Unlock booking link (1% fee, min $3, charged via letsfg.co)
unlock = bt.unlock(flights.cheapest.id)
print(f"Confirmed price: {unlock.confirmed_currency}{unlock.confirmed_price}")

# Book โ€” ticket price charged via Stripe (zero markup)
booking = bt.book(
 offer_id=flights.cheapest.id,
 passengers=[{
 "id": flights.passenger_ids[0],
 "given_name": "John",
 "family_name": "Doe",
 "born_on": "1990-01-15",
 "gender": "m",
 "title": "mr",
 "email": "john@example.com",
 }],
 contact_email="john@example.com"
)
print(f"PNR: {booking.booking_reference}")

Multi-Passenger Search

# 2 adults + 1 child, round-trip, premium economy
flights = bt.search(
 "LHR", "JFK", "2026-06-01",
 return_date="2026-06-15",
 adults=2,
 children=1,
 cabin_class="W", # W=premium, M=economy, C=business, F=first
 sort="price",
)

# passenger_ids will be ["pas_0", "pas_1", "pas_2"]
print(f"Passenger IDs: {flights.passenger_ids}")

# Book with details for EACH passenger
booking = bt.book(
 offer_id=unlocked.offer_id,
 passengers=[
 {"id": "pas_0", "given_name": "John", "family_name": "Doe", "born_on": "1990-01-15", "gender": "m", "title": "mr"},
 {"id": "pas_1", "given_name": "Jane", "family_name": "Doe", "born_on": "1992-03-20", "gender": "f", "title": "ms"},
 {"id": "pas_2", "given_name": "Tom", "family_name": "Doe", "born_on": "2018-05-10", "gender": "m", "title": "mr"},
 ],
 contact_email="john@example.com",
)

Resolve Locations

Always resolve city names to IATA codes before searching:

locations = bt.resolve_location("New York")
# [{"iata_code": "JFK", "name": "John F. Kennedy", "type": "airport", "city": "New York"}, ...]

# Use in search
flights = bt.search(locations[0]["iata_code"], "LAX", "2026-04-15")

Working with Search Results

flights = bt.search("LON", "BCN", "2026-04-01", return_date="2026-04-08", limit=50)

# Iterate all offers
for offer in flights.offers:
 print(f"{offer.owner_airline}: {offer.currency}{offer.price}")
 print(f" Route: {offer.outbound.route_str}")
 print(f" Duration: {offer.outbound.total_duration_seconds//3600}h")
 print(f" Stops: {offer.outbound.stopovers}")
 print(f" Refundable: {offer.conditions.get('refund_before_departure','unknown')}")
 print(f" Changeable: {offer.conditions.get('change_before_departure','unknown')}")

# Filter: direct flights only
direct = [o for o in flights.offers if o.outbound.stopovers == 0]

# Filter: specific airline
ba = [o for o in flights.offers if "British Airways" in o.airlines]

# Filter: refundable only
refundable = [o for o in flights.offers if o.conditions.get("refund_before_departure") == "allowed"]

# Sort by duration
by_duration = sorted(flights.offers, key=lambda o: o.outbound.total_duration_seconds)

# Cheapest offer
print(f"Best: {flights.cheapest.price}{flights.cheapest.currency}")

Error Handling

fromletsfgimport (
 LetsFG, LetsFGError,
 AuthenticationError, PaymentRequiredError, OfferExpiredError,
)

bt = LetsFG(api_key="trav_...")

# Handle invalid locations
try:
 flights = bt.search("INVALID", "JFK", "2026-04-15")
except LetsFGError as e:
 if e.status_code == 422:
 # Resolve the location first
 locations = bt.resolve_location("London")
 flights = bt.search(locations[0]["iata_code"], "JFK", "2026-04-15")

# Handle payment and expiry
try:
 unlocked = bt.unlock(offer_id)
except PaymentRequiredError:
 print("Run bt.setup_payment() first")
except OfferExpiredError:
 print("Offer expired โ€” search again for fresh results")

# Handle booking failures
try:
 booking = bt.book(offer_id=unlocked.offer_id, passengers=[...], contact_email="...")
except OfferExpiredError:
 print("30-minute window expired โ€” search and unlock again")
except AuthenticationError:
 print("Invalid API key")
except LetsFGError as e:
 print(f"API error ({e.status_code}): {e.message}")
Exception HTTP Code Cause
AuthenticationError 401 Missing or invalid API key
PaymentRequiredError 402 No payment method (call setup_payment())
OfferExpiredError 410 Offer no longer available
LetsFGError any Base class for all API errors

Timeout and Retry Pattern

Airline APIs can be slow (2โ€“15s for search). Use retry with backoff for production:

importtime
fromletsfgimport LetsFG, LetsFGError

bt = LetsFG()

defsearch_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 LetsFGError as e:
 if "429" in str(e) or "rate limit" in str(e).lower():
 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 LetsFGError("Max retries exceeded")

Rate Limits

Endpoint Rate Limit Typical Latency
Search No hard limit (billing is the natural governor) 60โ€“90 s
Resolve location 120 req/min < 1 s
Unlock 20 req/min 2โ€“5 s
Book 10 req/min 3โ€“10 s

Minimizing Unlock Costs

Searching is free and unlimited. Unlock via the Developer API is also free; via the local/website path the concierge fee (1% of ticket price, min $3) applies once per offer. Strategy:

# Search multiple dates (free) โ€” compare before unlocking
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):
 best = (date, result.cheapest)

# Unlock only the winner (1% fee, min $3)
if best:
 unlocked = bt.unlock(best[1].id)
 # Book within 30 minutes (ticket price only)
 booking = bt.book(offer_id=unlocked.offer_id, passengers=[...], contact_email="...")

Local Search (No API Key)

Note: Local search results return masked booking links by default. Each offer includes offer_ref and payment_token fields. To get a direct airline booking URL, you have two options: use the concierge unlock flow (1% fee, min $3 โ€” no API key needed) or sign up for the Developer API at letsfg.co/developers for fee-free direct links.

The SDK includes 200 connectors for airlines that run directly on your machine. No API key, no backend, completely free:

fromletsfg.localimport search_local

# Fires all relevant airline connectors โ€” Ryanair, Wizz Air, EasyJet, etc.
result = await search_local("GDN", "BCN", "2026-06-15")
print(f"{result['total_results']} offers from local connectors")

# Limit browser concurrency for constrained environments
result = await search_local("GDN", "BCN", "2026-06-15", max_browsers=4)

The full search (bt.search()) runs both local connectors and cloud providers simultaneously and merges results.

Unlocking offer results

Local search results include offer_ref and payment_token on each offer. Use these to get the direct airline booking URL via the concierge flow (no API key required):

importhttpx
importtime

# 1. Pick an offer from local search results
offer = result["offers"][0]
offer_id = offer["id"]
offer_ref = offer["offer_ref"]
payment_token = offer["payment_token"]
price = offer["price"] # ticket price as string, e.g. "312.50"
currency = offer["currency"] # e.g. "USD"

# 2. Initiate checkout โ€” returns a Stripe checkout URL
# Fee = max(price ร— 1%, $3.00). No API key needed.
resp = httpx.post(
 "https://letsfg.co/api/developers/checkout",
 json={
 "offer_id": offer_id,
 "offer_ref": offer_ref,
 "payment_token": payment_token,
 "currency": currency,
 "price": price,
 },
)
resp.raise_for_status()
checkout = resp.json()
checkout_url = checkout["checkout_url"]

# 3. Present the checkout URL to the user (or open it in a browser)
print(f"Pay here: {checkout_url}")

# 4. Poll until payment is confirmed
booking_url = None
while not booking_url:
 time.sleep(3)
 verify = httpx.get(
 "https://letsfg.co/api/developers/payment-verify",
 params={"token": payment_token},
 )
 data = verify.json()
 if data.get("verified"):
 booking_url = data["booking_url"]

# 5. booking_url is the direct airline link โ€” no further fees
print(f"Book here: {booking_url}")

To skip the per-booking fee entirely, use the Developer API โ€” it returns direct airline booking URLs on every search result.

Quick Start (CLI)

exportLETSFG_API_KEY=trav_...

# Search (1 adult, one-way, economy โ€” defaults)
letsfgsearchGDNBER2026-03-03--sortprice

# Multi-passenger round trip
letsfgsearchLONBCN2026-04-01--return2026-04-08--adults2--children1--cabinM

# Business class, direct flights only
letsfgsearchJFKLHR2026-05-01--adults3--cabinC--max-stops0

# Machine-readable output (for agents)
letsfgsearchLONBCN2026-04-01--json

# Unlock
letsfgunlockoff_xxx

# Book
letsfgbookoff_xxx\
--passenger'{"id":"pas_xxx","given_name":"John","family_name":"Doe","born_on":"1990-01-15","gender":"m","title":"mr","email":"john@example.com"}'\
--emailjohn@example.com

# Resolve location
letsfglocations"Berlin"

Search Flags

Flag Short Default Description
--return -r (one-way) Return date YYYY-MM-DD
--adults -a 1 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 Raw JSON output

All CLI Commands

Command Description Cost
search Search flights between any two airports FREE
locations Resolve city name to IATA codes FREE
unlock Unlock offer (confirms price, reserves 30min) FREE
book Book flight (creates real airline PNR) Ticket price
search-local Search 200 local airline connectors FREE
system-info Show system resources & concurrency tier FREE
register Register new agent, get API key FREE
setup-payment Attach payment card (payment token) FREE
me Show agent profile and usage stats FREE

Every command supports --json for machine-readable output.

Environment Variables

Variable Description
LETSFG_API_KEY Your agent API key
LETSFG_BASE_URL API URL (default: https://letsfg.co/developers)
LETSFG_MAX_BROWSERS Max concurrent browser instances (1โ€“32). Auto-detected from RAM if not set.

Performance Tuning

LetsFG auto-detects your system's available RAM and scales browser concurrency:

Available RAM Tier Max Browsers
< 2 GB Minimal 2
2โ€“4 GB Low 3
4โ€“8 GB Moderate 5
8โ€“16 GB Standard 8
16โ€“32 GB High 12
32+ GB Maximum 16
fromletsfgimport get_system_profile, configure_max_browsers

# Check system resources and recommended concurrency
profile = get_system_profile()
print(f"RAM: {profile['ram_available_gb']:.1f} GB available")
print(f"Tier: {profile['tier']} โ†’ {profile['recommended_max_browsers']} browsers")

# Override auto-detection
configure_max_browsers(4) # clamps to 1โ€“32
# Via CLI
letsfgsystem-info
letsfgsystem-info--json# machine-readable

# Override via env var
exportLETSFG_MAX_BROWSERS=4
letsfgsearch-localLHRBCN2026-04-15

# Override via CLI flag
letsfgsearch-localLHRBCN2026-04-15--max-browsers4

Priority: env var > explicit config/flag > auto-detect.

How It Works

  1. Search โ€” Free. 200+ connectors fire on your machine and return real-time offers.
  2. Unlock booking URL โ€” Pay the concierge fee (1% of ticket price, min $3) via letsfg.co to receive the direct airline booking link. Or use the Developer API (prepaid credits) for fee-free direct links.
  3. Book โ€” Open the direct airline URL and complete the booking on the airline's own site.

Prices are cheaper because we connect directly to airlines โ€” no OTA markup.


Also Available As

  • MCP Server: npx letsfg-mcp โ€” npm
  • JS/TS SDK: npm install letsfg โ€” npm
  • Try without installing: letsfg.co โ€” search instantly in your browser
  • GitHub: LetsFG/LetsFG

โญ Star the repo โ€” we appreciate the support.

License

MIT

Project details

Verified details

These details have been verified by PyPI
Maintainers
๐Ÿ‘ Avatar for amjaworsky from gravatar.com
amjaworsky

Unverified details

These details have not been verified by PyPI
Project links
Meta
  • License: MIT License (MIT)
  • Author: LetsFG
  • Tags agent , ai , airline , autonomous , booking , cli , easyjet , flights , low-cost , mcp , ndc , openai , ryanair , scraper , travel , wizzair
  • Requires: Python >=3.10
  • Provides-Extra: all , dev

Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

letsfg-2026.5.79.tar.gz (1.6 MB view details)

Uploaded Source

Built Distribution

Filter files by name, interpreter, ABI, and platform.

If you're not sure about the file name format, learn more about wheel file names.

Copy a direct link to the current filters

letsfg-2026.5.79-py3-none-any.whl (1.9 MB view details)

Uploaded Python 3

File details

Details for the file letsfg-2026.5.79.tar.gz.

File metadata

  • Download URL: letsfg-2026.5.79.tar.gz
  • Upload date:
  • Size: 1.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for letsfg-2026.5.79.tar.gz
Algorithm Hash digest
SHA256 62be0016e3af0922ec1ecc97e12fdb114d71af9f474b7edda00f0df0fd99c9ff
MD5 db866f35553993b6c5f5276b8944d552
BLAKE2b-256 3474b10bcf6f960a792559df7f1a408a9d62a5ae90b50169bd2de7cc7a79e09a

See more details on using hashes here.

File details

Details for the file letsfg-2026.5.79-py3-none-any.whl.

File metadata

  • Download URL: letsfg-2026.5.79-py3-none-any.whl
  • Upload date:
  • Size: 1.9 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for letsfg-2026.5.79-py3-none-any.whl
Algorithm Hash digest
SHA256 d5b0f5ba3b795a86c9c54238bc1e77710ffef8653d0b5d75c0f4a411d1b478e5
MD5 b657769fc766c0efd5c0eaf9f93e5cd5
BLAKE2b-256 71f5c177b95a5e1534fbcfd9552fa97d056b16a9fab9ae40dc2c5f07cfd4b4f7

See more details on using hashes here.

Supported by

๐Ÿ‘ Image
AWS Cloud computing and Security Sponsor ๐Ÿ‘ Image
Datadog Monitoring ๐Ÿ‘ Image
Depot Continuous Integration ๐Ÿ‘ Image
Fastly CDN ๐Ÿ‘ Image
Google Download Analytics ๐Ÿ‘ Image
Pingdom Monitoring ๐Ÿ‘ Image
Sentry Error logging ๐Ÿ‘ Image
StatusPage Status page