search-flights▌
google.com/search-flights-ts4g1f · updated May 21, 2026
MDX-style export adds YAML metadata + attribution linking explainx.ai and this canonical listing URL.
Search Google Flights for one-way or round-trip itineraries between two airports on given dates and return the cheapest options with airline, flight numbers, total duration, stops, depart/arrive times, and a booking link. Read-only — never books.
| name | search-flights |
| title | Google Flights Search |
| description | >- Search Google Flights for one-way or round-trip itineraries between two airports on given dates and return the cheapest options with airline, flight numbers, total duration, stops, depart/arrive times, and a booking link. Read-only — never books. |
| website | google.com |
| category | travel |
| tags | - travel - flights - search - google - read-only - anti-bot |
| source | 'browserbase: agent-runtime 2026-05-18' |
| updated | '2026-05-18' |
| recommended_method | browser |
| alternative_methods | - method: url-param rationale: >- The `tfs=<base64-protobuf>` deep-link pre-fills origin/destination/dates/cabin/passengers in the URL itself, so the workflow needs zero form interaction. This is a navigation shortcut layered on top of the browser path — it does not replace browser rendering (the results page is JS-driven and the booking funnel is JS-only). - method: api rationale: >- Confirmed unavailable for consumer search. `developers.google.com/travel/flights` is a Travel Partner API for airlines/OTAs to push inventory TO Google — it does not return search results. Don't waste time searching for a JSON endpoint. - method: hybrid rationale: >- Direct HTTP `curl` of `https://www.google.com/travel/flights?tfs=...` with Chrome-UA + consent cookies sometimes returns parseable HTML (cheerio selectors documented in SKILL) and an embedded `script.ds:1` JS data blob with full itinerary detail. Reliable from clean residential IPs; expect `/sorry/index` 429 from most cloud / data-center IPs. Use as opportunistic optimization with browser fallback on any non-200. |
| verified | true |
| proxies | true |
Google Flights Search
Purpose
Given an origin, a destination, one date (one-way) or two dates (round-trip), and a passenger / cabin config, return the cheapest itineraries with airline, flight numbers, total duration, stops, depart/arrive times, and a booking link. Read-only — never click Select flight, Book, or any continuation that leaves the results screen.
When to Use
- "What's the cheapest flight from SFO to JFK on 2026-06-15?"
- Comparing fares across dates / airports for a trip-planner agent.
- Bulk price monitoring on a fixed route (driven by the same
tfs=URL day-over-day). - Any flow that needs a price + itinerary snapshot. Booking is a separate skill — Google Flights routes the actual purchase to airline-direct or OTA partners, none of which this skill touches.
Workflow
Google does not publish a consumer flight search API. The official Travel Partner API (developers.google.com/travel/flights) is for airlines/OTAs to push inventory to Google — it does not return search results. The reliable path is to drive the consumer site https://www.google.com/travel/flights with a deep-link URL that pre-applies all search parameters via the tfs= (base64-protobuf) query string, so no form interaction is required.
A residential-proxy + Browserbase-Verified session is mandatory. From data-center IPs Google serves the /sorry/index 429 interstitial ("Our systems have detected unusual traffic from your computer network") within 1–2 requests on /travel/flights* — verified at runtime (HTTP 429, Content-Type: text/html, captcha widget served inline).
1. Build the tfs deep-link
Construct the URL:
https://www.google.com/travel/flights?tfs=<BASE64_PROTOBUF>&hl=en&curr=USD&gl=us&tfu=EgQIABABIgA
The tfs value is a base64-URL-encoded protobuf with this schema (verified against open-source decoders):
message Airport { string airport = 2; } // 3-letter IATA, uppercase
message FlightData { string date = 2; // YYYY-MM-DD
Airport from_flight = 13;
Airport to_flight = 14;
optional int32 max_stops = 5; // 0 = nonstop
repeated string airlines = 6; } // 2-letter IATA or alliance
enum Seat { ECONOMY=1; PREMIUM_ECONOMY=2; BUSINESS=3; FIRST=4; }
enum Trip { ROUND_TRIP=1; ONE_WAY=2; MULTI_CITY=3; }
enum Passenger { ADULT=1; CHILD=2; INFANT_IN_SEAT=3; INFANT_ON_LAP=4; }
message Info { repeated FlightData data = 3; // 1 leg for one-way, 2 for round-trip
Seat seat = 9;
repeated Passenger passengers = 8; // one enum entry per passenger
Trip trip = 19; }
Wire-format rule: passengers is repeated and unpacked — three adults + one infant-on-lap = four entries [ADULT, ADULT, ADULT, INFANT_ON_LAP], not a single scalar count. Total passengers must be ≤ 9. infants_on_lap must be ≤ adults.
The trailing constants — hl=en, curr=USD, gl=us, tfu=EgQIABABIgA — are required. Drop any of them and Google often redirects to a "popular destinations" landing page that ignores the tfs payload.
Easier fallback when you don't want to encode protobuf: use the natural-language q= deep-link. The intent-parser handles ~95% of routes cleanly:
https://www.google.com/travel/flights?q=Flights+from+SFO+to+JFK+on+2026-06-15+through+2026-06-22&hl=en&curr=USD&gl=us
Verify after navigation that the resolved URL contains tfs= (intent-parser succeeded) — if it lands on /explore instead, the parser couldn't disambiguate and you must fall back to building the tfs directly.
2. Open in a stealth + residential-proxy session
sid=$(browse cloud sessions create --keep-alive --verified --proxies | jq -r .id)
export BROWSE_SESSION="$sid"
browse open "$URL" --remote
browse wait load --remote
browse wait timeout 3500 --remote # itinerary cards stream in 2–4s after `load`
Anything short of --verified --proxies gets either the /sorry/index 429 or an empty itinerary list with a "Something went wrong" toast.
3. Dismiss the consent / cookie banner if present
On a fresh session (no SOCS cookie) Google overlays a consent dialog that swallows clicks on result rows.
browse snapshot --remote
# Look for a `button: "Reject all"` or `button: "Accept all"` ref — click it.
browse click "<ref>" --remote
browse wait timeout 1000 --remote
If you reuse a persisted Browserbase context, you can pre-seed the consent state once (CONSENT=PENDING+987; SOCS=<base64>) and skip this step on every subsequent run.
4. Switch the sort to "Cheapest" (the prompt explicitly asks for cheapest)
The default landing tab is "Best", which is Google's price-vs-duration heuristic, not strictly the cheapest. Click the "Cheapest" tab:
browse snapshot --remote
# Find: tab "Cheapest" → click its ref
browse click "<ref>" --remote
browse wait timeout 2500 --remote # the list re-sorts in place; no navigation
Equivalent URL-only path: append &sort=2 (SerpAPI-style sort codes — 1=top, 2=price, 3=depart, 4=arrive, 5=duration, 6=emissions). Google honors this param on the consumer page.
5. Read the itinerary cards
browse get html body --remote > /tmp/flights.html
The result list is a sequence of <li> items inside two parent containers (one per tab/section):
div[jsname="IWWDBc"]— "Best flights" / top sectiondiv[jsname="YdtKid"]— "Other flights" section
Within each ul.Rk10dc li row:
| Field | Selector |
|---|---|
| Airline name | div.sSHqwe.tPgKwe.ogfYpf span (first) |
| Depart / arrive times | span.mv1WYe div (nodes 0 and 1) |
Next-day arrival flag (+1, +2) | span.bOzv6.bOzv6 next to arrival time |
| Total duration | div.gvkrdb.AdWm1c.tPgKwe.ogfYpf ("5 hr 32 min") |
| Stops | div.EfT7Ae.AdWm1c.tPgKwe span.ogfYpf ("Nonstop" / "1 stop") |
| Layover detail | div.c8rWCd.tPgKwe.ogfYpf ("1 hr 45 min LAX") |
| Price | div.YMlIz.FpEdX span ("$412") |
| CO₂e | div.O7CXue.tPgKwe ("312 kg CO2e") |
| Card "details" expander | button[aria-label*="more details"] |
Flight numbers (e.g. DL 412) are not in the card-collapsed view. You have to expand each card — click button[aria-label*="more details"] — which renders an inner detail panel containing each segment's Airline + flight number + aircraft + seat pitch + on-time stats. Read span.sSHqwe span:nth-of-type(2) inside each segment block to get the AA 1234-style identifier. Budget 1.5–3 seconds per card for the detail-render to settle before the next snapshot.
6. Construct booking links
There is no clean per-itinerary "deep link to airline" available before the user picks a card — Google routes booking through /travel/flights/booking which is Disallow: in robots.txt and serves Akamai-blocked responses to non-browser HTTP. The two practical options:
- Canonical Google Flights detail URL — keep the current page URL and append the itinerary's
itinerary-keyparameter fromdata-iidattribute on the row (<li data-iid="...">). Format:https://www.google.com/travel/flights/booking?tfs=<original>&iid=<row data-iid>. Reaches the booking-redirect page when followed in-browser. Use this when you need a single URL the user can paste. - Click + capture the outbound URL — click
button[aria-label*="Select flight"]on the card, thenbrowse get urlimmediately after navigation. Google bounces to either an airline-direct URL or a Google-hosted broker page. Carries an?gclid=tracking param. Higher-fidelity but costs one navigation per card.
Default to option (1) for read-only price-comparison flows. Use option (2) only when the consumer explicitly needs the merchant-direct URL.
7. Pull deeper itinerary detail via the embedded JS blob (optional, faster than per-card expansion)
The full structured itinerary list (including flight numbers, aircraft types, layover cities, codeshares) is embedded in the initial HTML as a nested-list blob inside <script class="ds:1">…AF_initDataCallback({…, data: [ …deeply nested arrays… ], …})…</script>. Regex: /^.*?\{.*?data:(\[.*\]).*}/. Parse the captured group as JSON, then index by the positional paths below (verified against open-source decoders):
- Itinerary root list:
data[0]is "best",data[1]is "other" - Per itinerary:
flights[],layovers[],travel_time(minutes),departure_airport,arrival_airport,itinerary_summary.flights(e.g."DL 412, DL 1735"),itinerary_summary.price(dollars, integer) - Per flight segment (
itinerary.flights[i]):operator[2],departure_airport[3],arrival_airport[5],departure_time[8]([hour, minute]),arrival_time[10]([hour, minute]),travel_time[11](minutes),seat_pitch_short[14],codeshares[15],aircraft[17],departure_date[20],arrival_date[21],airline_code[22][0],airline_name[22][3],flight_number[22][1]
Reading the blob is one extraction per page vs. N expand-clicks in the DOM path, but the indices are positional and Google has rotated them at least once historically — always pin the parser against the latest version of wooldox/google-flights-ts/src/decoder.ts (or the upstream Python AWeirdDev/flights) before trusting them blindly.
8. Release the session
browse cloud sessions update "$sid" --status REQUEST_RELEASE
Fast-path fallback: direct HTTP, no browser
If you have a clean residential IP and just need a quick price snapshot, you can sometimes skip the browser entirely:
curl -sS "https://www.google.com/travel/flights?tfs=<b64>&hl=en&curr=USD&tfu=EgQIABABIgA" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ..." \
-H "Cookie: CONSENT=PENDING+987; SOCS=CAESHAgBEhJnd3NfMjAyMzA4MTAtMF9SQzIaAmRlIAEaBgiAo_CmBg" \
-H "Accept-Language: en-US,en;q=0.5"
The 200-OK HTML body contains both the cheerio-parsable cards (selectors above) and the script.ds:1 JS blob. Expect this path to fail most of the time from cloud IPs — Google returns /sorry/index 429 or a redirect to a generic landing page. Use only as an opportunistic optimization with a graceful fallback to the browser path on any non-200 response or any response containing <title>Sorry...</title>.
Site-Specific Gotchas
- READ-ONLY. Never click
Select flight,Book, or any time-slot ref on the result card. The skill terminates at the results screen. --verified --proxiesis mandatory. Bare and--proxies-only sessions get the/sorry/index429 captcha interstitial within 1–2 requests on/travel/flights*. Verified at iteration time via directbrowse cloud fetchof/sorry/indexreturning HTTP 429 with the canonical "Our systems have detected unusual traffic" body.- The page is >1 MB of HTML — confirmed:
browse cloud fetch https://www.google.com/travel/flightsreturns502 The response body exceeded the maximum allowed size of 1MB. Use a browser session to handle large responses.Don't try to use Browserbase's Fetch API for the search page; you must drive a session. robots.txtexplicitly disallows the booking funnel —Disallow: /travel/flights/booking,/travel/flights/search,/travel/flights/s/,/travel/search,/travel/clk. The main/travel/flightslanding is the only allowed entry point. Skill stays on/travel/flights?tfs=...and never navigates into aDisallow-ed subpath.- No public consumer search API.
developers.google.com/travel/flightsis a partner API for airlines/OTAs to push inventory to Google. It does not return search results. Don't waste time looking for a JSON endpoint — the consumer site is the only surface. tfsis base64-URL-encoded protobuf, not JSON. Decoders that try to JSON-parse it fail silently and produce blank pages. Verified protobuf schema in step 1; reference encoders:wooldox/google-flights-ts(TS, hand-rolled),AWeirdDev/flights(Python, upstream).passengersis repeated-unpacked, not a count. Two adults =[ADULT, ADULT], not2. Mis-encoding shows up as "1 traveler" on the rendered page regardless of intent.- Default tab is "Best", not "Cheapest". The prompt asks for cheapest — explicitly click the "Cheapest" tab (or append
&sort=2to the URL) before reading rows, otherwise the top results are Google's "best value" heuristic, which can be $50–$200 above true cheapest on a US transcon. - Flight numbers are not in the collapsed card. Either expand each card (1.5–3s wait per expand) or parse the
script.ds:1JS blob (one parse per page). The blob path is ~10× faster but positionally indexed — see step 7 caveat. - Next-day-arrival marker is easy to miss. The arrival cell shows
11:50 PMwith a tiny+1superscript when the flight lands the next day. Selector:span.bOzv6.bOzv6. Drop this and you'll mis-computetotal_duration_minutesby 1440. - Consent dialog is a click-trap. Fresh sessions get the EU-style "Before you continue" overlay (even on US IPs). It captures clicks on row buttons silently. Always check for and dismiss it before clicking anything else.
q=intent-parser can rewrite the URL. Useful (you can skip protobuf encoding) but unpredictable — ambiguous airport names or odd date phrasings can land on/exploreinstead of running a search. Always inspect the post-navigation URL fortfs=; fall back to manually building the protobuf when it's missing.- Prices are cached, not live. A given
tfspayload can return a stale price for hours. For high-fidelity pricing, append&curr=USD&hl=en(forces currency / locale) and treat the result as "Google's last-seen price", not "current airline price". Surface the page's<title>"Updated …" timestamp when emitting output. - HTML selectors are obfuscated and rotate quarterly.
jsname="IWWDBc"andul.Rk10dcare stable across at least 18 months of open-source community usage but have changed in the past. When a parse drops to zero rows, refresh selectors fromwooldox/google-flights-ts/src/core.ts(parseResponse) or the upstreamAWeirdDev/flightslibrary — both track Google's churn. - Booking page is Akamai-blocked from cookieless HTTP. Don't try to follow
/travel/flights/bookingURLs out-of-band — they only render in-session after aSelect flightclick. For consumer-facing booking URLs, prefer attaching the canonical Google Flights URL with theiid(see step 6 option 1). - Trace capture from Vercel Sandbox is unavailable. The sandbox's DNS resolver returns
REFUSEDforconnect.usw2.browserbase.comand all otherconnect.*.browserbase.comhosts (verified across us-west-2, us-east-1, eu-central-1, ap-southeast-1). The Browserbase Fetch API atapi.browserbase.comworks, but CDP/Playwright sessions cannot be driven from the sandbox itself. The skill is designed to run from a Browserbase-connected client (laptop, CI runner, or a host whose egress is unfiltered) — this caveat is environment-specific, not a property of Google Flights.
Expected Output
Two outcome shapes:
// Success
{
"success": true,
"origin": "SFO",
"destination": "JFK",
"depart_date": "2026-06-15",
"return_date": "2026-06-22",
"trip_type": "round-trip",
"passengers": { "adults": 1, "children": 0, "infants_in_seat": 0, "infants_on_lap": 0 },
"cabin": "economy",
"sort": "cheapest",
"price_currency": "USD",
"results": [
{
"rank": 1,
"price": 412,
"airlines": ["Delta"],
"flight_numbers": ["DL 412", "DL 1735"],
"stops": 0,
"total_duration_minutes": 332,
"outbound": {
"depart_time_local": "2026-06-15T07:30-07:00",
"arrive_time_local": "2026-06-15T15:50-04:00",
"depart_airport": "SFO",
"arrive_airport": "JFK",
"duration_minutes": 320,
"segments": [
{ "flight_number": "DL 412", "depart": "07:30", "arrive": "15:50",
"depart_airport": "SFO", "arrive_airport": "JFK",
"aircraft": "Boeing 757", "operator": "Delta Air Lines", "duration_minutes": 320, "layover_minutes": 0 }
]
},
"return": {
"depart_time_local": "2026-06-22T18:10-04:00",
"arrive_time_local": "2026-06-22T21:35-07:00",
"depart_airport": "JFK",
"arrive_airport": "SFO",
"duration_minutes": 385,
"segments": [ /* same shape */ ]
},
"co2e_kg": 312,
"co2e_vs_typical_pct": -8,
"booking_url": "https://www.google.com/travel/flights/booking?tfs=<original>&iid=<row data-iid>",
"last_updated": "2026-05-18T18:42:00Z"
}
]
}
// Anti-bot wall (Sorry page) — emit when the post-navigation URL contains /sorry/ or the page title starts with "Sorry"
{
"success": false,
"error_reasoning": "Google served the /sorry/index 429 anti-bot interstitial. Retry with a fresh --verified --proxies session, or rotate the proxy pool. The IP currently in use is on Google's traffic-anomaly list."
}
If the route has no flights on the requested date — valid empty result, not a failure: { "success": true, ..., "results": [] } plus the page's empty-state text in an info field ("No flights available for these dates. Try nearby dates.").
How to use search-flights on Cursor
AI-first code editor with Composer
Prerequisites
Before installing skills in Cursor, ensure your development environment meets these requirements:
- ›Cursor installed and configured on your development machine
- ›Node.js version 16.0+ with npm package manager (verify with
node --version) - ›Active project directory or workspace where you want to add search-flights
Execute installation command
Execute the skills CLI command in your project's root directory to begin installation:
The skills CLI fetches search-flights from GitHub repository google.com/search-flights-ts4g1f and configures it for Cursor.
Select Cursor when prompted
The CLI will show a list of available agents. Use arrow keys to navigate and space to select Cursor:
Verify installation
Confirm successful installation by checking the skill directory location:
Reload or restart Cursor to activate search-flights. Access the skill through slash commands (e.g., /search-flights) or your agent's skill management interface.
Security & Verification Notice
We perform automated surface-level scans (Gen AI Scanner, Socket, Snyk) during installation. These checks detect common vulnerabilities but do not guarantee complete security. Always review skill source code and verify the publisher's reputation before production use.
Skills execute code in your development environment. Always verify the publisher's identity, review recent commits, and test in isolated environments before production deployment.
List & Monetize Your Skill
Submit your Claude Code skill and start earning
Use Cases▌
Task Automation & Efficiency
Automate repetitive workflows and reduce manual effort
Example
Generate reports, summarize documents, draft communications
Save 3-5 hours per week on routine tasks
Knowledge Enhancement
Learn new skills, understand complex topics, get expert guidance
Example
Explain concepts, provide examples, suggest learning resources
Accelerate learning and skill development by 2x
Quality Improvement
Enhance output quality through reviews, suggestions, and refinements
Example
Review drafts, suggest improvements, catch errors
Improve work quality by 30-40% with less effort
Implementation Guide▌
Prerequisites
- ›Claude Desktop or compatible AI client with skill support
- ›Clear understanding of task or problem to solve
- ›Willingness to iterate and refine outputs
Time Estimate
15-45 minutes depending on use case complexity
Installation Steps
- 1.Install skill using provided installation command
- 2.Test with simple use case relevant to your work
- 3.Evaluate output quality and relevance
- 4.Iterate on prompts to improve results
- 5.Integrate into regular workflow if valuable
Common Pitfalls
- ⚠Expecting perfect results without iteration
- ⚠Not providing enough context in prompts
- ⚠Using skill for tasks outside its intended scope
- ⚠Accepting outputs without review and validation
Best Practices▌
✓ Do
- +Start with clear, specific prompts
- +Provide relevant context and constraints
- +Review and refine all outputs before using
- +Iterate to improve output quality
- +Document successful prompt patterns
✗ Don't
- −Don't use without understanding skill limitations
- −Don't skip validation of outputs
- −Don't share sensitive information in prompts
- −Don't expect skill to replace human judgment
💡 Pro Tips
- ★Be specific about desired format and style
- ★Ask for multiple options to choose from
- ★Request explanations to understand reasoning
- ★Combine AI efficiency with human expertise
When to Use This▌
✓ Use When
Use when skill capabilities match your task, clear ROI on time saved, and you can validate outputs. Best for repetitive tasks, learning, and quality improvement.
✗ Avoid When
Avoid when task requires deep expertise you can't validate, involves sensitive decisions, or when learning process is more valuable than speed of completion.
Learning Path▌
- 1Familiarize yourself with skill capabilities and limitations
- 2Start with low-risk, non-critical tasks
- 3Progress to more complex and valuable use cases
- 4Build expertise through regular use and experimentation
Discussion
Product Hunt–style comments (not star reviews)- No comments yet — start the thread.
Ratings
4.7★★★★★56 reviews- ★★★★★Hiroshi Okafor· Dec 28, 2024
Solid pick for teams standardizing on skills: search-flights is focused, and the summary matches what you get after install.
- ★★★★★Diego Perez· Dec 28, 2024
We added search-flights from the explainx registry; install was straightforward and the SKILL.md answered most questions upfront.
- ★★★★★Ganesh Mohane· Dec 24, 2024
Registry listing for search-flights matched our evaluation — installs cleanly and behaves as described in the markdown.
- ★★★★★Omar Jain· Dec 24, 2024
search-flights has been reliable in day-to-day use. Documentation quality is above average for community skills.
- ★★★★★Omar Kapoor· Dec 8, 2024
Keeps context tight: search-flights is the kind of skill you can hand to a new teammate without a long onboarding doc.
- ★★★★★Sakura Johnson· Nov 23, 2024
Registry listing for search-flights matched our evaluation — installs cleanly and behaves as described in the markdown.
- ★★★★★Omar Bansal· Nov 19, 2024
search-flights is among the better-maintained entries we tried; worth keeping pinned for repeat workflows.
- ★★★★★Maya Anderson· Nov 19, 2024
search-flights fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.
- ★★★★★Hiroshi Yang· Nov 15, 2024
search-flights reduced setup friction for our internal harness; good balance of opinion and flexibility.
- ★★★★★Omar Shah· Nov 3, 2024
We added search-flights from the explainx registry; install was straightforward and the SKILL.md answered most questions upfront.
showing 1-10 of 56