
Flight Ticket Search
Compare round-trip flight prices from the terminal using a cached Python venv and Google Flights public data—no API key or booking automation.
Overview
flight-ticket-search is an agent skill for the Build phase that runs a cached Python CLI to search Google Flights-style fares without API keys or automated booking.
Install
npx skills add https://github.com/nomadamas/k-skill --skill flight-ticket-searchWhat is this skill?
- Bootstraps pinned fast-flights==2.2 in an isolated ~/.cache/k-skill/flight-ticket-search venv and re-execs safely
- No API key, login, CAPTCHA bypass, purchase, or booking automation—search and analysis only
- Python helper with argparse/JSON output for agent-driven fare comparison workflows
- Uses Google Flights public surface via fast-flights inside a private user cache directory
- Pins fast-flights==2.2 in a private cache venv
Adoption & trust: 655 installs on skills.sh; 5.4k GitHub stars; 1/3 security scanners passed (skills.sh audits).
What problem does it solve?
You want quick flight price reads from the terminal without API contracts, logins, or wiring a full travel product.
Who is it for?
Indie builders and nomads who script travel research locally and want agents to compare fares via a maintained scraper wrapper.
Skip if: Production OTAs, automated ticketing, CAPTCHA bypass, or teams that need licensed GDS or airline API compliance.
When should I use this skill?
When you need repeatable terminal flight fare searches for travel planning without API keys or booking automation.
What do I get? / Deliverables
You get isolated-venv flight search output (JSON/statistics) your agent can use for itinerary decisions, with no purchase path enabled.
- JSON or structured fare comparison output from CLI runs
- Isolated ~/.cache/k-skill/flight-ticket-search venv
Recommended Skills
Journey fit
Canonical shelf is Build because the skill is an installable CLI integration that wires an external data source (fast-flights) into your agent workflow. Integrations fits a third-party scraper/runtime bootstrap pattern rather than product frontend, ship gates, or growth work.
How it compares
Use for no-key CLI fare snapshots instead of building a custom scraper or signing a paid flights API.
Common Questions / FAQ
Who is flight-ticket-search for?
Solo builders and frequent travelers who use Claude Code, Cursor, or similar agents and want terminal-based flight lookups without API keys.
When should I use flight-ticket-search?
Use during Build when wiring personal automation or travel ops CLI workflows, or anytime you need fare comparison data before committing to dates—not as part of Ship/Launch product features unless scope stays search-only.
Is flight-ticket-search safe to install?
It installs a pinned dependency in a user cache venv and avoids checkout automation; review the Security Audits panel on this page and treat scraped public surfaces as brittle, ToS-sensitive integrations.
SKILL.md
READMESKILL.md - Flight Ticket Search
#!/usr/bin/env python3 """Free flight ticket search helper for the k-skill flight-ticket-search skill. Uses fast-flights (Google Flights public surface scraper) in an isolated user cache venv. No API key, login, CAPTCHA bypass, purchase, or booking automation. """ from __future__ import annotations import argparse import json import os import re import statistics import shutil import subprocess import sys import time from dataclasses import asdict from datetime import date, datetime, timedelta from pathlib import Path from typing import Any, Iterable from urllib.parse import urlencode PINNED_FAST_FLIGHTS = "fast-flights==2.2" CACHE_ROOT = Path.home() / ".cache" / "k-skill" / "flight-ticket-search" VENV_DIR = CACHE_ROOT / "venv" def ensure_runtime() -> None: """Install fast-flights into a private cache venv, then re-exec there.""" if os.environ.get("FLIGHT_TICKET_SEARCH_BOOTSTRAPPED") == "1": return py = VENV_DIR / "bin" / "python" def candidate_python_executables() -> list[str]: candidates = [sys.executable] candidates.extend( found for name in ("python3.13", "python3.12", "python3.11", "python3") if (found := shutil.which(name)) ) seen: set[str] = set() unique: list[str] = [] for candidate in candidates: resolved = str(Path(candidate).resolve()) if resolved not in seen: seen.add(resolved) unique.append(candidate) return unique def create_venv() -> None: CACHE_ROOT.mkdir(parents=True, exist_ok=True) errors: list[str] = [] for python in candidate_python_executables(): shutil.rmtree(VENV_DIR, ignore_errors=True) try: subprocess.check_call([python, "-m", "venv", str(VENV_DIR)]) return except (OSError, subprocess.CalledProcessError) as exc: errors.append(f"{python}: {exc}") raise RuntimeError( "Unable to create flight-ticket-search venv with available Python interpreters: " + "; ".join(errors) ) def venv_has_fast_flights() -> bool: if not py.exists(): return False return subprocess.run( [str(py), "-c", "import fast_flights"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False, ).returncode == 0 if not py.exists(): create_venv() if not venv_has_fast_flights(): try: subprocess.check_call([str(py), "-m", "ensurepip", "--upgrade"]) subprocess.check_call([ str(py), "-m", "pip", "install", "--disable-pip-version-check", "-q", PINNED_FAST_FLIGHTS, ]) except (OSError, subprocess.CalledProcessError): # Recover from interrupted or pip-less cache venvs before surfacing a hard failure. shutil.rmtree(VENV_DIR, ignore_errors=True) create_venv() subprocess.check_call([str(py), "-m", "ensurepip", "--upgrade"]) subprocess.check_call([ str(py), "-m", "pip", "install", "--disable-pip-version-check", "-q", PINNED_FAST_FLIGHTS, ]) env = os.environ.copy() env["FLIGHT_TICKET_SEARCH_BOOTSTRAPPED"] = "1" os.execve(str(py), [str(py), __file__, *sys.argv[1:]], env) def parse_date(s: str) -> date: return datetime.strptime(s, "%Y-%m-%d").date() def positive_int(value: str) -> int: parsed = int(value) if parsed < 1: raise argparse.ArgumentTypeError("must be a positive integer") return parsed def nonnegative_int(value: str) -> int: parsed = int(value) if parsed < 0: raise argparse.ArgumentTypeError("must be zero or a positive i