The price trail airlines don't show you.
Track flight prices over time. Self-hosted. Open source. Bring your own LLM.
curl -fsSL https://fairtrail.org/install.sh | bashIf you have Claude Code or Codex installed, the setup script detects it automatically — no API key needed. Otherwise, it asks you to paste one.
Once it finishes:
- Open localhost:3003 — or run
fairtrail search "NYC to Tokyo in July under $800" - Fairtrail starts tracking prices immediately — come back anytime to see the trend
Airlines change flight prices hundreds of times a day. They know exactly when you searched, how many times you came back, and how desperate you are. They use this to maximize what you pay — not minimize it.
No one shows you the price trend because the companies with the data profit from hiding it:
- Aggregators want you inside their app. Google Flights and Hopper track price history internally but lock it behind your account — or don't show it at all.
- "Buy or Wait" is more profitable than transparency. A black-box prediction keeps you dependent on their platform.
- Airlines don't want price transparency. If you can see that a route dips 3 weeks before departure, that undermines dynamic pricing.
Fairtrail exists because the data is useful to you — just not to the companies that have it.
- Natural language search —
"NYC to Paris around June 15 ± 3 days" - Price evolution charts — see how fares move over days and weeks
- Shareable links — send
/q/abc123to anyone, no login required - Direct booking links — click any data point to go straight to the airline
- Airline comparison — see which carriers are cheapening vs. getting expensive
- Self-hosted — your searches stay private, your data stays on your machine
- Agent-friendly API — hook Claude Code, Codex, or any agent into your instance
- It can't work any other way. A centralized service scraping Google Flights gets IP-banned within days. Thousands of self-hosted instances, each making a few quiet requests from different IPs, is the only architecture that survives. Decentralization isn't a philosophy — it's the only design that works.
- Your searches stay private. No one sees what routes you're watching or when you're planning to travel. Airlines can't use your search history against you.
- You control the scrape frequency. Default is every 3 hours. Want every hour? Change one setting.
- Free with Claude Code, Codex, or a local model. Use your existing CLI subscription, or run Ollama / llama.cpp / vLLM locally - zero API cost.
- Your data, your database. Price history lives in your own Postgres. Export it, analyze it, keep it forever.
- Docker Desktop (runs everything behind the scenes — you won't interact with it directly)
- One of:
- Claude Code (free with Claude Pro/Max) — auto-detected
- Codex (free with ChatGPT Pro) — auto-detected
- An API key from Anthropic, OpenAI, or Google
- Ollama, llama.cpp, or vLLM - first-class local model support, configurable from the admin UI
Fairtrail needs an LLM for two things: parsing natural language queries and extracting price data from Google Flights pages. Pick any one of these:
| Provider | Auth | Cost | Notes |
|---|---|---|---|
| Claude Code | Auto-detected (host ~/.claude) |
Free (Pro/Max plan) | Subscription CLI — mounts auth read-only |
| Codex CLI | Auto-detected (host ~/.codex) |
Free (ChatGPT Pro) | Subscription CLI — mounts auth read-only |
| Anthropic | ANTHROPIC_API_KEY |
Pay-per-token | Claude Haiku 4.5 (default) |
| OpenAI | OPENAI_API_KEY |
Pay-per-token | GPT-4.1 Mini |
GOOGLE_AI_API_KEY |
Pay-per-token | Gemini 2.5 Flash | |
| Ollama | None (local) | Free | First-class provider - select in admin UI, type your model ID |
| llama.cpp | None (local) | Free | First-class provider - select in admin UI, type your model ID |
| vLLM | None (local) | Free | First-class provider - GPU-accelerated inference server (port 8000) |
| OpenAI + custom URL | OPENAI_BASE_URL |
Varies | OpenRouter or any OpenAI-compatible endpoint |
Three ways to use Fairtrail:
- Subscription users (Claude Pro/Max, ChatGPT Pro) — the installer detects
claudeorcodexon your machine and mounts their config dirs read-only into the container. Your auth tokens are never copied; the container cannot modify them. - API key users — paste an Anthropic, OpenAI, or Google key. Passed via env var, never written to disk.
- Local model users — select Ollama, llama.cpp, or vLLM in the admin UI, type your model ID, and optionally set a custom base URL. No API key needed.
All settings are in ~/.fairtrail/.env (generated by the installer). Key options:
| Variable | Default | Description |
|---|---|---|
ANTHROPIC_API_KEY |
— | Anthropic API key (one LLM provider required) |
OPENAI_API_KEY |
— | OpenAI API key |
OPENAI_BASE_URL |
— | Custom endpoint for OpenAI provider (vLLM, OpenRouter) |
GOOGLE_AI_API_KEY |
— | Google AI API key |
OLLAMA_HOST |
http://localhost:11434 |
Ollama server address (base URL derived as $OLLAMA_HOST/v1) |
POSTGRES_PASSWORD |
postgres |
Database password |
ADMIN_PASSWORD |
Auto-generated | Admin panel password |
CRON_ENABLED |
true |
Enable built-in scrape scheduler |
CRON_INTERVAL_HOURS |
3 |
Hours between scrape runs |
HOST_PORT |
3003 |
Host port to access Fairtrail (container always listens on 3003 internally) |
Secrets left empty are auto-generated on first run and printed in Docker logs.
Fairtrail is fully decentralized. You run everything — scraping, LLM calls, storage — on your own machine. There is no central server doing work for you.
What fairtrail.org does: aggregates anonymized price data that self-hosted instances opt in to share. Think of it as a community price database that grows as more people run Fairtrail.
What gets shared (opt-in only):
- Route (origin/destination airports)
- Travel date, price, currency, airline, stops, cabin class
- When the data was scraped
What is never shared:
- Your queries, search history, or preferences
- Your LLM API keys
- Your IP address or identity
Enable community sharing during the setup wizard or later in /admin → Config. Explore community data at fairtrail.org/explore.
Your local Fairtrail instance exposes a REST API that any agent, script, or CLI tool can use. No SDK needed — just HTTP calls to localhost:3003.
See AGENTS.md for the full API reference.
# 1. Parse a natural language query
curl -s -X POST http://localhost:3003/api/parse \
-H "Content-Type: application/json" \
-d '{"query": "NYC to Paris around June 15 ± 3 days"}' | jq .
# 2. Create a tracked query (use the parsed response)
curl -s -X POST http://localhost:3003/api/queries \
-H "Content-Type: application/json" \
-d '{
"rawInput": "NYC to Paris around June 15 ± 3 days",
"origin": "JFK", "originName": "New York JFK",
"destination": "CDG", "destinationName": "Paris CDG",
"dateFrom": "2026-06-12", "dateTo": "2026-06-18",
"flexibility": 3, "cabinClass": "economy",
"tripType": "round_trip", "routes": [...]
}' | jq .
# 3. Trigger an immediate scrape (CRON_SECRET is auto-generated at startup, check docker logs)
curl -s http://localhost:3003/api/cron/scrape \
-H "Authorization: Bearer $(docker compose -f docker-compose.prod.yml logs web 2>&1 | grep -oP 'CRON_SECRET=\K\S+')" | jq .
# 4. Get price data for a query
curl -s http://localhost:3003/api/queries/{id}/prices | jq .You type: "SFO to Tokyo sometime in July ± 5 days"
│
▼
┌─────────────────┐
│ LLM Parser │ Extracts origin, destination,
│ (Claude/GPT) │ date range, flexibility
└────────┬────────┘
│
▼
┌─────────────────┐
│ Playwright │ Navigates Google Flights
│ (headless) │ with your exact query
└────────┬────────┘
│
▼
┌─────────────────┐
│ LLM Extractor │ Reads the page, extracts
│ (configurable) │ structured price data
└────────┬────────┘
│
▼
┌─────────────────┐
│ PostgreSQL │ Stores price snapshots
│ + Prisma │ with timestamps
└────────┬────────┘
│
▼
┌─────────────────┐
│ Plotly.js │ Interactive chart at
│ /q/[id] │ a shareable public URL
└─────────────────┘
The built-in cron runs on a configurable interval (default: every 3h). Each run captures prices across all active queries and the chart pages update automatically.
Run Fairtrail entirely in the terminal — no browser needed.
fairtrail --headless # Interactive search wizard
fairtrail --headless --backend claude-code # Use Claude Code as AI backend
fairtrail --headless --backend codex # Use Codex as AI backend
fairtrail --headless --list # Show all tracked queries
fairtrail --headless --view <id> # Live price chart (auto-refreshes every 30s)
fairtrail --headless --view <id> --tmux # Split grouped routes into tmux panesWithout --headless, --view opens the chart in your browser and --list opens the admin dashboard.
- Natural language search — same as the web, powered by your chosen AI backend
- Braille chart — Unicode price evolution chart with per-airline colored trend lines
- Live refresh — countdown bar, auto-refreshes from DB, flashes when new data arrives
- Multi-destination — "Frankfurt to Bogota or Medellin" searches both routes
- tmux integration —
--tmuxsplits each grouped route into its own pane - Backend selection —
--backend claude-code|codex|anthropic|openai|google|ollama|llamacpp|vllm
Usage: fairtrail [command]
Commands:
(none) Start Fairtrail (Ctrl+C to stop)
search ".." Search and track a flight from the terminal
start Start in background
stop Stop — pauses all price tracking until you start again
logs View live logs
status Check if running
update Pull latest version and restart
version Show version and commit
uninstall Remove Fairtrail and all data
help Show this help
Stopping Fairtrail pauses all scraping — no queries run while it's off. Your data and queries are saved. Just run fairtrail again to resume tracking.
# Install dependencies
npm install
# Start database + cache
docker compose up -d db redis
# Apply schema
npm run db:push
# Generate Prisma client
npm run db:generate
# Start dev server (set env vars or use `doppler run --`)
npm run dev| Layer | Technology |
|---|---|
| Frontend | Next.js 15 (App Router), TypeScript, CSS Modules |
| Database | PostgreSQL 16 + Prisma ORM |
| Cache | Redis 7 (optional) |
| Browser | Playwright (headless Chromium) |
| LLM | Anthropic, OpenAI, Google, Claude Code, Codex, Ollama, llama.cpp, or vLLM |
| Charts | Plotly.js (interactive) |
| Cron | Built-in (node-cron) or external trigger |
Access at /admin (no login required on self-hosted instances):
- Manage queries — pause, resume, delete, adjust scrape frequency
- Configure LLM — choose extraction provider and model
- Monitor costs — see LLM API usage per scrape run
- View fetch history — success/failure status, errors, snapshot counts
Pull requests welcome! See CONTRIBUTING.md for guidelines.
Fairtrail is an informational tool only. Flight prices shown are scraped from third-party sources and may be inaccurate, outdated, or incomplete. Airlines change prices based on demand, search history, seat availability, and other factors — the price you see in Fairtrail may differ from the price you're offered at checkout. Do not make purchasing decisions based solely on Fairtrail data. Always verify prices directly with the airline or booking platform before buying. The authors are not responsible for any financial loss, missed deals, or incorrect pricing information.
Fairtrail is a personal tool that scrapes publicly available flight pricing data. In the US, scraping publicly accessible websites does not violate the Computer Fraud and Abuse Act (hiQ Labs v. LinkedIn, 9th Cir. 2022). Fairtrail does not circumvent any login, paywall, or technical access control.
That said, automated access to third-party websites may conflict with their terms of service. Users are solely responsible for complying with the terms of service of any website they interact with through Fairtrail. This project is not affiliated with, endorsed by, or associated with Google, any airline, or any travel booking platform.
This software is provided as-is for personal and educational use.
Google Flights has an undocumented internal API (GetShoppingResults, GetCalendarGraph) that returns structured JSON without a browser. The fli project reverse-engineers it. We investigated this approach and decided against it. Here's why.
What the direct API gives you: sub-second searches, no browser dependency, no Chromium in Docker, no LLM cost per extraction.
What it costs you:
| Fairtrail | fli | |
|---|---|---|
| Approach | Playwright + LLM extraction | Reverse-engineered internal API |
| Language | TypeScript (Next.js) | Python |
| Speed | 3-10s per search (browser + LLM) | Sub-second (single HTTP POST) |
| LLM cost | None (local/subscription) or pay-per-token | None |
| Docker image | ~400MB (Chromium) | ~20MB |
| Booking links | Yes -- captures redirect URLs | No |
| Currency control | Yes (&curr=, &gl= params) |
No -- determined by server IP geolocation |
| Fare class / cabin details | Yes -- visible in page | No |
| Seats remaining | Yes -- "3 seats left" badges | No |
| Baggage info | Yes -- in expanded details | No |
| Departure/arrival times | Yes | Yes |
| Multi-city | Yes | Partial (open PR) |
| Proxy support | Native (Playwright launch args) | None built-in |
| Price tracking | Built-in (cron + Postgres) | Manual re-querying |
| Shareable charts | Yes (/q/[id]) |
No |
The deeper reason: Fairtrail is a price tracker, not a price search engine. It runs a cron every 3 hours across a handful of active queries. Speed doesn't matter -- data completeness does. Booking links, seat counts, and fare classes are what make the price chart useful for deciding when to buy.
Both approaches share the same fundamental risk: Google can break either one at any time. DOM changes break Playwright selectors; internal response structure changes break array-index parsing. Neither has a contract. We'd rather depend on the stable, public-facing UI than on undocumented internal array positions where airline data lives at fl[22][0] and prices at data[1][0][-1].
Use Fairtrail if you want to track prices over time, see trends, get booking links, and share charts. You care about data completeness more than speed.
Use fli if you want instant, programmatic flight lookups from scripts or agents. You don't need booking URLs, price history, or a UI -- just fast structured results.
| Project | Description |
|---|---|
| fli | Google Flights API reverse-engineering (Python) -- see comparison above |
| PriceToken | Real-time LLM pricing API, npm/PyPI packages, and live dashboard |
| gitpane | Multi-repo Git workspace dashboard for the terminal |
| kin3o | AI-powered Lottie animation generator CLI |
MIT


