Southwest logo

Southwest

Community
borski
southwest

Search Southwest Airlines fares and points pricing via Patchright browser automation. SW is not in any GDS or API. Covers all fare classes, Companion Pass value, and fare drop monitoring.

Overview

Publisherborski
Repositorytravel-hacking-toolkit
Skill namesouthwest
Stars
690
Forks
75
Bundled files
5
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 5 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by borski on GitHub. Read the source before you install it.

Installation

Install the Southwest AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/borski/travel-hacking-toolkit/tree/main/plugins/travel-hacking-toolkit/skills/southwest
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/borski/travel-hacking-toolkit.git /tmp/travel-hacking-toolkit
mkdir -p .claude/skills
cp -r /tmp/travel-hacking-toolkit/plugins/travel-hacking-toolkit/skills/southwest .claude/skills/southwest
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Southwest in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Southwest on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Southwest is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Southwest Airlines Search

Search southwest.com via Patchright (undetected Playwright fork). Southwest is the only major US airline not in any GDS, API, or flight search tool. This skill is the only way to get SW-specific data: fare classes, points pricing, and Companion Pass qualification.

Requires Patchright (undetected Playwright fork). Standard Playwright and agent-browser are detected and blocked by SW. Patchright patches the browser fingerprint to bypass detection.

Must run headed (headless=False). SW detects headless browsers even with Patchright. On macOS, a Chrome window briefly appears and closes. For background operation with no popup, use Docker:

bash
# Search new flights (default, no login needed)
docker run --rm ghcr.io/borski/sw-fares --origin SJC --dest DEN --depart 2026-05-15
docker run --rm ghcr.io/borski/sw-fares --origin SJC --dest DEN --depart 2026-05-15 --return 2026-05-18 --points --json

# Check existing reservation for price drops (requires SW login)
docker run --rm -e SW_USERNAME -e SW_PASSWORD \
  ghcr.io/borski/sw-fares change --conf ABC123 --first Jane --last Doe --json

# List all upcoming trips
docker run --rm -e SW_USERNAME -e SW_PASSWORD \
  ghcr.io/borski/sw-fares change --list --json

# Build locally
docker build -t sw-fares skills/southwest/

The Docker image uses xvfb to create a virtual display inside the container. Patchright runs "headed" against xvfb, bypassing SW's headless detection without opening any windows on your machine.

bash
# Install
pip install patchright && patchright install chromium

# Search cash fares
python3 scripts/search_fares.py --origin SJC --dest DEN --depart 2026-05-15

# Search cash + points
python3 scripts/search_fares.py --origin SJC --dest DEN --depart 2026-05-15 --return 2026-05-18 --points

# JSON output
python3 scripts/search_fares.py --origin SJC --dest DEN --depart 2026-05-15 --points --json

Key implementation details learned the hard way:

  • Navigate to southwest.com homepage FIRST, then to the results URL. Direct URL without homepage warmup gets blocked.
  • Use fareType=POINTS URL parameter for points pricing. Don't try to click the Points toggle on the page.
  • Use a fresh persistent browser context (temp dir) each run to avoid fingerprint accumulation.
  • Extract text from [role='main'] NOT <main>. The <main> element contains the booking form. [role='main'] contains the flight results.
  • Flight blocks can be parsed by splitting on # \d+ patterns and ending at View seats.
  • Always dismiss the cookie banner first. It blocks interactions.

Prerequisites

bash
pip install patchright && patchright install chromium

Or use Docker (no local install needed):

bash
# Pre-built image (fastest)
docker pull ghcr.io/borski/sw-fares:latest

# Or build locally
docker build -t sw-fares skills/southwest/

When to Use

  • User asks about Southwest flights or fares
  • Need points pricing (not available anywhere else)
  • Need fare class breakdown (Basic, Choice, Choice Preferred, Choice Extra)
  • Calculating Companion Pass value
  • Comparing SW points vs cash vs other airlines

When NOT to Use

  • Just need SW cash prices: Use google-flights skill instead (faster)
  • Completing purchases: Find flights only. Do not book.
  • Non-Southwest airlines: Use duffel, ignav, google-flights, or other skills

Southwest Fare Classes

FarePointsCashCompanion PassChange/CancelBags
Basic (Wanna Get Away)LowestLowestNOCredit only2 free
Choice (Wanna Get Away Plus)LowLowYESTransferable credit2 free
Choice Preferred (Anytime)HighHighYESRefundable2 free
Choice Extra (Business Select)HighestHighestYESRefundable + priority2 free

Critical for Companion Pass: Only Choice and above qualify. Basic does NOT. This changes the CPP math significantly.

Companion Pass CPP Formula

When CP is in play, one ticket buys travel for two:

Total cash value = qualifying_fare_cash x 2 passengers
CPP = total_cash_value / total_points x 100

Always frame as "points bought $X of travel for 2 people."

Search: Use the Script

Do NOT try to automate the SW booking form manually. It's fragile and the form's React state doesn't cooperate with browser automation. Use the script instead, which navigates directly to the results URL.

bash
# Local (opens a Chrome window briefly)
python3 skills/southwest/scripts/search_fares.py --origin SJC --dest DEN --depart 2026-05-15

# With return date and points
python3 skills/southwest/scripts/search_fares.py --origin SJC --dest DEN --depart 2026-05-15 --return 2026-05-18 --points

# JSON output for programmatic use
python3 skills/southwest/scripts/search_fares.py --origin SJC --dest DEN --depart 2026-05-15 --points --json

# Docker (no Chrome window, no display needed)
docker run --rm ghcr.io/borski/sw-fares --origin SJC --dest DEN --depart 2026-05-15 --return 2026-05-18 --points

The script handles everything: homepage warmup, cookie dismissal, result extraction, points toggle.

How the Script Works

  1. Launches Patchright (undetected Playwright) in headed mode with a fresh browser profile
  2. Visits southwest.com homepage first to establish a normal browsing session
  3. Navigates to the results URL with fareType=USD for cash or fareType=POINTS for points
  4. Waits 15 seconds for SW's React app to render flight data
  5. Extracts flight text from [role='main'] (NOT <main> which is the booking form)
  6. Parses flight blocks by splitting on # \d+ patterns
  7. Cleans up the browser and temp profile

Why fareType in the URL instead of clicking the Points toggle: The Points label on the results page is in a different frame context than the flight cards. Clicking it via Playwright is unreliable. Using the URL parameter loads points results directly. Two page loads (cash + points) is more reliable than one load + a toggle click.

Why [role='main'] not main: SW has two content areas. The <main> HTML element contains the booking form. The [role='main'] ARIA attribute marks the flight results. Using the wrong one returns zero flights.

Output Format

Always use markdown tables. Show both cash and points in separate tables.

Cash Prices

FlightDepartArriveStopsDurationBasicChoiceCh PrefCh Extra
#1046:00AM9:35AMNonstop2h 35m$277$317$387$432
#16817:40PM11:05PMNonstop2h 25m$209$249$319$364
#481/37806:35AM12:05PM1 stop via PHX4h 30m$219$259$329$374

Points Prices

FlightDepartArriveStopsDurationBasicChoiceCh PrefCh Extra
#1046:00AM9:35AMNonstop2h 35m23,500 +$5.6027,000 +$5.6033,000 +$5.6036,500 +$5.60

After Tables

  • Calculate CPP for each fare class: cash_price / points x 100
  • Flag which fares qualify for Companion Pass (Choice and above)
  • If CP is in play, show the 2-pax value
  • Compare against other airline options if available

Troubleshooting

  • "No flights found": SW may have flagged the IP from too many automated requests. Wait a few minutes and try again, or try Docker (different network stack).
  • Chrome window pops up: Expected in local mode. Use Docker for no popup.
  • Script hangs: SW's page can take up to 20 seconds to render. The 15-second wait is usually enough but slow connections may need more.
  • "Oops" error page: Retry after a short wait. SW's site can be flaky.

Limitations

  • Headed mode required. SW detects and blocks headless browsers, even with Patchright's stealth patches. Docker+xvfb is the workaround for background operation.
  • No codeshare data. SW doesn't codeshare with anyone.
  • ~20 seconds per search. Homepage warmup + page render time. Slower than API sources.
  • Prices change frequently. SW uses dynamic pricing. Results are point-in-time.
  • One-way searches only return departing flights. For round-trip, the script fetches departure fares. Return flight fare search would require a second URL load (future enhancement).

Change Flight Price Monitor (check_change.py)

READ-ONLY script. NEVER modifies, changes, or cancels any flight.

Logs into southwest.com, looks up an existing reservation, and reads the Change Flight page to check if prices have dropped below what was paid.

Docker Usage (No Popup Window)

bash
# Set your SW credentials however you like (env vars, 1Password, etc.)
export SW_USERNAME="your_username"
export SW_PASSWORD="your_password"

# List all upcoming trips (auto-discover confirmation numbers)
docker run --rm -e SW_USERNAME -e SW_PASSWORD \
  ghcr.io/borski/sw-fares change --list --json

# Check a specific reservation for price drops
docker run --rm -e SW_USERNAME -e SW_PASSWORD \
  ghcr.io/borski/sw-fares change --conf ABC123 --first Jane --last Doe --json

# Local (opens Chrome window, for debugging only)
SW_USERNAME=your_user SW_PASSWORD=your_pass \
  python3 scripts/check_change.py --conf ABC123 --first Jane --last Doe --debug

Important: Use Legal Names

SW bookings use the legal name on the reservation, not nicknames. Use the exact first and last name that appears on the booking.

How It Works

  1. Logs into southwest.com via header login flyout
  2. For --list: clicks My Account, expands accordion for each trip, extracts confirmation numbers
  3. For --conf: navigates to Change Flight page, fills lookup form, selects both legs, clicks "Explore options"
  4. Reads the change results page which shows fare differences for every available flight:
    • CURRENT FLIGHT = your booked flight (Basic cell shows "CURRENT", no change)
    • +2,000 = this flight costs 2,000 points MORE than what you paid
    • -2,000 = this flight costs 2,000 points LESS (savings, rebook opportunity)
    • Unavail = fare class sold out
  5. Flags any flight with a negative diff as a savings opportunity

Safety Guardrails

  1. DANGEROUS_LABELS blocklist: "change this flight", "confirm", "cancel", "book", "purchase", etc.
  2. is_dangerous_click() check on every button click after the login and lookup phases
  3. extract_results() is read-only. Zero clicks after the leg selection + "Explore options" step
  4. The script selects legs and views alternatives but NEVER selects a replacement flight or confirms any change

Verified Working Selectors (Apr 5, 2026)

Login flyout:

  • Trigger: button:has-text('Log in')
  • Username: input[aria-label*='Account']
  • Password: input[name='password']
  • Submit: button[type='submit']:has-text('Log in')

Change flight lookup form:

  • Confirmation: input[id*='confirmationNumber']
  • First name: input#passengerFirstName
  • Last name: input#passengerLastName
  • Submit: button#form-mixin--submit-button (text: "Continue")

Leg selection:

  • Checkboxes: label:has(input[type='checkbox']) (visible labels, hidden inputs)
  • Continue: button#form-mixin--submit-button (text: "Explore options")

Important Limitation: Companion Pass Linked Reservations

The change command DOES NOT WORK on Companion Pass-linked reservations. SW shows a banner: "Companion itinerary linked. To change it, cancel the companion's flight first."

For CP-linked reservations (or any monitoring scenario where you don't want to log in), use the monitor subcommand instead. It uses the public fare-search flow and matches your booked flight number against current results. No login needed.

Fare Drop Monitor (monitor.py)

Compare current SW fares against what you paid for booked flights. Reports any specific booked flight where the current price has dropped below the baseline (= reclaimable travel credit when you rebook).

No login needed. Uses the public search-fares flow and matches by exact flight number. Works on CP-linked reservations where the change-flight flow is blocked.

Config File

Provide a JSON array of trips:

json
[
  {
    "name": "Trip name (display only)",
    "confirmation": "ABC123",
    "passenger": "Jane (free-text label, display only)",
    "outbound": {
      "origin": "SJC",
      "dest": "SAN",
      "date": "2026-04-30",
      "flight_number": "4107",
      "baseline_pts": 13000
    },
    "return": {
      "origin": "SAN",
      "dest": "SJC",
      "date": "2026-05-03",
      "flight_number": "4119",
      "baseline_pts": 17500
    }
  }
]

The flight_number MUST match exactly how SW displays it in search results. For multi-segment flights it includes spaces, e.g. "3657 / 4630". The return leg can be omitted for one-way trips.

Docker Usage

bash
# Check all trips in the config
docker run --rm -v /path/to/trips.json:/app/trips.json:ro \
  ghcr.io/borski/sw-fares monitor --config /app/trips.json

# Check just one trip by confirmation number
docker run --rm -v /path/to/trips.json:/app/trips.json:ro \
  ghcr.io/borski/sw-fares monitor --config /app/trips.json --only ABC123

# JSON output
docker run --rm -v /path/to/trips.json:/app/trips.json:ro \
  ghcr.io/borski/sw-fares monitor --config /app/trips.json --json

Local Usage (opens Chrome window briefly)

bash
python3 scripts/monitor.py --config /path/to/trips.json

How It Works

  1. For each leg of each trip in the config:
    • Navigate to SW homepage (resets SPA state between legs)
    • Search the route + date in points fareType
    • Validate the results URL contains the requested origin and dest (rejects stale renders)
    • Find the SPECIFIC booked flight number in the results
    • Read its current Basic fare in points
    • Compare against baseline_pts from config
  2. Report savings (positive) or upcharge (negative) per leg, plus a trip-level total

After a Rebook

If you rebook a leg to capture savings, update the baseline_pts for that leg in the config to the new (lower) paid amount. Otherwise the next monitor run will keep flagging the same opportunity falsely.

Why It's Better Than the Change Flow

check_changemonitor
Login requiredYesNo
Works on CP-linked reservationsNoYes
Shows alternatives at all timesYesNo (only the booked flight)
Brittle SW DOM dependenciesHigh (form clicks)Low (URL params + text parse)
Output detailFull fare-class diffs for every alternativeBasic-fare diff for your exact booked flight

monitor is the right tool for routine baseline tracking. check_change is useful when you want to see ALL alternatives across all fare classes, but only on non-CP reservations.

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Southwest AI skill do?

Search Southwest Airlines fares and points pricing via Patchright browser automation. SW is not in any GDS or API. Covers all fare classes, Companion Pass value, and fare drop monitoring.

Why use Southwest on TypingMind?

Because you install it once and use it with any model. Southwest is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Southwest in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/borski/travel-hacking-toolkit/tree/main/plugins/travel-hacking-toolkit/skills/southwest. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Southwest?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Southwest?

As many as you like. As long as a model supports skills, you can use Southwest with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Southwest AI skill free?

Yes. It is published on GitHub by borski under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇