Find and Compare Filtered Kayak Flights

Site kayak.comTask find-filtered-kayak-flightsVersion v3Updated Sep 30, 2026Category travel

Search Kayak flight itineraries using its direct route URL and encoded sort, cabin, and airline filters, then extract fare and itinerary details. This skill was captured from a live agent session on kayak.com and is published here as a reusable recipe for agents.

NoteSelectors and URL schemes drift as sites change. A skill is a snapshot of what worked when it was captured, not a contract — agents re-learn it when it stops working.

Find flight results on Kayak for a supplied route, dates, passenger mix, cabin, sort order, and optional airline filter, returning comparable fares and itinerary details.

Use Cases

Use when the caller wants Kayak flight options with a price sort and/or airline selection, especially when comparing specific carriers (for example Scoot, Starlux, or China Airlines). The caller should provide airport codes, flight dates, passenger counts or child ages, and any desired cabin or airline restrictions.

Automation Flow

  1. Construct Kayak's direct flight-results URL without visiting the homepage: https://{kayak-domain}/flights/{origin}-{destination}/{outbound-date}/{return-date}/{adults}adults[/children-{child-age-1}-{child-age-2}-...]?sort={sort}&fs=cfc=1;cabin={cabin}[;airlines={airline-code-1},{airline-code-2}]

    {kayak-domain} is the Kayak edition host, for example www.kayak.sg or www.kayak.com; the displayed currency follows the domain, so pick the edition whose currency the caller wants and report it. Use ISO dates (YYYY-MM-DD), price_a for ascending price, e for Economy, p for Premium Economy, b for Business, and f for First. Omit fs when no cabin or other filter is requested. Child ages are joined with hyphens after children-.

  2. Encode requested airline names using Kayak carrier codes. Known mappings include TR = Scoot, JX = Starlux, and CI = China Airlines. For the direct filter form observed on Kayak, use airlines=TR, airlines=JX,CI, or another comma-separated code list. Kayak may also accept the expanded form airlines=-multiple_airlines,{code-1},{code-2}; preserve the leading -multiple_airlines when that form is required by the rendered filter state. Airline filtering is optional and defaults to unfiltered results.

  3. For Premium Economy searches use cabin=p; retain the cabin label returned by each itinerary because Kayak can identify an itinerary as Mixed when its legs do not share the same cabin.

  4. In one browser-agent operation, navigate to the constructed URL, wait until dynamic results or a blocking page appears, then run this evaluate() extractor on the current page:

(() => {
  const origin='{origin}', destination='{destination}', airlineNames=[/* caller's requested airline names, e.g. 'Scoot','Starlux' */];
  const clean = s => (s || '').replace(/\s+/g, ' ').trim();
  const body = document.body?.innerText || '';
  const lines = body.split(/\n/).map(clean).filter(Boolean);
  const pageText = location.href + '\n' + body;
  const captcha = /sitecaptcha|verify you are human|captcha/i.test(pageText);
  const status = lines.filter(s => /of \d+ flights|No matching|Loading|complete/i.test(s)).slice(0, 50);
  if (captcha) return { url: location.href, title: document.title, captcha: true, status, flights: [] };

  const blocks = [];
  let current = [];
  for (const line of lines) {
    if (/^Select$/i.test(line) && current.length) {
      blocks.push(current);
      current = [];
    } else {
      current.push(line);
    }
  }
  if (current.length) blocks.push(current);

  const flights = blocks.map(block => {
    const joined = block.join(' | ');
    const priceRe = /(?:[\p{Sc}]|[A-Z]{0,2}\$|\b(?:USD|EUR|GBP|SGD|AUD|JPY|INR|CAD|HKD)\b)\s*[\d,]+(?:\.\d{2})?/giu;
    const prices = [...joined.matchAll(priceRe)].map(m => clean(m[0]));
    const total = joined.match(/(?:[\p{Sc}]|[A-Z]{0,2}\$|\b(?:USD|EUR|GBP|SGD|AUD|JPY|INR|CAD|HKD)\b)\s*[\d,]+(?:\.\d{2})?\s*total/iu);
    const wantAirlines = airlineNames.length ? new RegExp(airlineNames.map(a=>a.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join('|'),'i') : null;
    const carriers = wantAirlines ? block.filter(s => wantAirlines.test(s)).slice(0, 6) : [];
    const times = block.filter(s => /\b\d{1,2}:\d{2}\s*(?:AM|PM)?\s*[–-]/i.test(s)).slice(0, 6);
    const routeCodes = new RegExp('stop|Direct|self-transfer|layover|\\b'+[origin,destination].filter(Boolean).join('\\b|\\b')+'\\b','i');
    const routeOrStops = block.filter(s => routeCodes.test(s)).slice(0, 10);
    const cabin = [...new Set(block.filter(s => /Economy|Premium Economy|Business|First|Mixed/i.test(s)))].slice(0, 4);
    return {
      pricePerPerson: prices[0] || null,
      totalPrice: total ? clean(total[0]) : null,
      carriers,
      times,
      routeOrStops,
      cabin,
      text: block.filter(s => s.length > 2 && !/^Go to/i.test(s)).slice(0, 30).join(' || ')
    };
  }).filter(f => f.pricePerPerson || f.totalPrice || f.carriers.length || f.times.length || f.routeOrStops.length);

  return { url: location.href, title: document.title, captcha: false, status, flights };
})()
  1. If Kayak redirects to /sitecaptcha.html?out=... or the extractor reports captcha: true, report the anti-bot block and preserve the requested results URL from the out parameter; never invent flight results. Otherwise return the extracted flights in the page's displayed currency and wording.

Possible Friction Points

  • Kayak exposes route, dates, passenger counts, and child ages in the path rather than requiring form submission; child ages use a hyphen-separated children-{age} segment.
  • The cheapest-first modifier is sort=price_a; cabin filtering uses the semicolon-separated fs=cfc=1;cabin={code} parameter.
  • On an unfiltered search (no requested airlines), carriers is returned empty by design; read the operating carrier from each card's visible text instead of expecting a filled carriers array.
  • Airline filters are nested inside the same fs parameter. Kayak has shown both the compact form airlines=TR or airlines=JX,CI and the expanded form airlines=-multiple_airlines,{codes}. Verify carrier labels in returned cards rather than assuming the filter syntax was honored.
  • Known carrier codes are TR for Scoot, JX for Starlux, and CI for China Airlines.
  • Results populate asynchronously after navigation. Wait for flight cards, price text, a flight-count/status line, or a captcha page before evaluating; a roughly 14-second wait may be needed for dynamic results.
  • Result cards do not guarantee a stable public data attribute. The extractor segments rendered text around Select, so missing fields must be treated as unavailable rather than inferred.
  • Kayak may display both per-person and total prices. Preserve both when available and compare fares using the displayed total when present.
  • A Premium Economy filter may still produce a Mixed cabin label for itineraries whose outbound and return legs differ; report the displayed label instead of normalizing it to Premium Economy.

Call it

GET https://production-sfo.browserless.io/skills?token=TOKEN-HERE&domain=kayak.com&task=find-filtered-kayak-flights