Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Output Modes

Crosswind separates data from diagnostics and supports automatic format detection.

The contract

  • stdout = flight data (JSON, tables)
  • stderr = diagnostics (timing, hints, error messages in TTY mode)

This separation means you can safely pipe Crosswind's output into jq or other tools without diagnostic noise contaminating the data stream.

Auto-detection

When you do not specify a format, Crosswind auto-detects:

  • TTY stdout → styled table
  • Non-TTY stdout (pipe, redirect) → JSON envelope

Force JSON in a terminal

crosswind BEG JFK apr1 --json

JSON envelope

All JSON output follows a consistent envelope:

{
  "v": 1,
  "status": "ok",
  "cmd": "search",
  "data": {
    "flights": [...],
    "airlines": [...]
  },
  "timing_ms": 1423
}
FieldDescription
vEnvelope version (always 1)
status"ok" or "error"
cmdCommand name ("search")
dataSearch results (flights + airline metadata)
timing_msRequest duration in milliseconds

Flight object

Each flight in data.flights contains:

{
  "airlines": ["LOT"],
  "segments": [...],
  "price": 627,
  "stops": 1,
  "duration_minutes": 855,
  "is_best": true,
  "carbon_grams": 284000,
  "typical_carbon_grams": 310000
}

Segment object

Each segment in a flight:

{
  "from_code": "BEG",
  "from_name": "Belgrade Nikola Tesla Airport",
  "to_code": "WAW",
  "to_name": "Warsaw Chopin Airport",
  "depart_date": "2026-04-01",
  "depart_time": "06:15",
  "arrive_date": "2026-04-01",
  "arrive_time": "07:45",
  "duration_minutes": 90,
  "aircraft": "Embraer 195",
  "flight_number": "LO 572"
}

Error envelope

When an error occurs in JSON mode:

{
  "v": 1,
  "status": "error",
  "code": "invalid_airport_code",
  "message": "airport code must be exactly 3 letters, got 'XX'",
  "hint": "use a 3-letter IATA code like JFK, LAX, BEG"
}

Piping with jq

# Count flights
crosswind BEG JFK apr1 | jq '.data.flights | length'
 
# Cheapest flight
crosswind BEG JFK apr1 | jq '.data.flights[0]'
 
# All nonstop flights
crosswind BEG JFK apr1 | jq '[.data.flights[] | select(.stops == 0)]'
 
# Flight numbers only
crosswind BEG JFK apr1 | jq '[.data.flights[].segments[].flight_number]'
 
# Best flights under $800
crosswind BEG JFK apr1 | jq '[.data.flights[] | select(.is_best and .price < 800)]'