Help us shape the future of Go! Take the 2026 Go Developer Survey

flights

package module
v1.3.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 19, 2026 License: MIT Imports: 15 Imported by: 0

README

✈️ go-flights

A fast, strongly-typed Go client for scraping Google Flights.
Go port of fast-flights (Python) — feature-complete, TDD-built, production-ready.

go get github.com/chaurasiayush/go-flights@latest

Requires Go 1.21+


At a glance

package main

import (
    "fmt"
    flights "github.com/chaurasiayush/go-flights"
)

func main() {
    q, err := flights.CreateQuery(
        []flights.FlightQuery{
            flights.NewFlightQuery("2026-12-01", "BLR", "LKO"),
        },
        flights.Passengers{Adults: 1},
        flights.QueryOptions{
            Seat:     flights.SeatEconomy,
            Trip:     flights.TripOneWay,
            Language: "en",
            Currency: "INR",
        },
    )
    if err != nil {
        panic(err)
    }

    result, err := flights.GetFlightsDirect(q, "", nil)
    if err != nil {
        panic(err)
    }

    for _, f := range result.Items {
        price := "N/A"
        if f.Price != nil {
            price = fmt.Sprintf("₹%d", *f.Price)
        }
        fmt.Printf("%s  %s  %v\n", f.Type, price, f.Airlines)
    }
}

How it works

Google Flights encodes search parameters as a Base64-encoded Protobuf string in the ?tfs= URL parameter. This library:

  1. Serialises your query into a proto3 Info message (defined in pb/flights.proto)
  2. Fetches the page using a browser-impersonating TLS client (Chrome 131 / macOS fingerprint via bogdanfinn/tls-client) to avoid bot detection
  3. Extracts the embedded <script class="ds:1"> JavaScript blob from the HTML
  4. Parses the positional JSON payload and returns typed Go structs

Installation

go get github.com/chaurasiayush/go-flights@latest

Query builder

FlightQuery — one leg
leg := flights.NewFlightQuery("2026-12-01", "JFK", "LAX")

// All filter fields are optional pointer types — nil means "not set"
leg.MaxStops           = flights.Int32P(1)
leg.Airlines           = []string{"DL", "AA"}
leg.EarliestDepartureHour = flights.Int32P(7)
leg.LatestDepartureHour   = flights.Int32P(22)
leg.EarliestArrivalHour   = flights.Int32P(9)
leg.LatestArrivalHour     = flights.Int32P(23)
leg.MaxDurationMinutes    = flights.Int32P(600)
leg.ConnectingAirports    = []string{"ORD", "DEN"}
leg.MinLayoverMinutes     = flights.Int32P(60)
leg.MaxLayoverMinutes     = flights.Int32P(240)
leg.LessEmissionsOnly     = true

flights.NewFlightQueryFromTime(time.Now().AddDate(0,0,7), "JFK", "LAX") also accepted.

Passengers
// Direct struct literal — no constructor needed for simple cases
p := flights.Passengers{Adults: 2, Children: 1}

// Validated constructor — returns error for >9 passengers or infants > adults
p, err := flights.NewPassengers(adults, children, infantsInSeat, infantsOnLap)
QueryOptions — global filters
opts := flights.QueryOptions{
    Seat:     flights.SeatBusiness,       // SeatEconomy | SeatPremiumEconomy | SeatBusiness | SeatFirst
    Trip:     flights.TripRoundTrip,      // TripOneWay | TripRoundTrip | TripMultiCity
    Language: "en",                       // IETF BCP-47, "" = let Google decide
    Currency: "USD",                      // ISO 4217, "" = let Google decide
    MaxStops: flights.Int32P(1),          // applied to every leg
    MaxPrice: flights.Int32P(500),        // in the selected Currency
    CarryOnBags:  1,
    CheckedBags:  1,
    HideSeparateAndSelfTransfer: true,
    ExcludeBasicEconomy:         true,
}
CreateQuery
q, err := flights.CreateQuery(legs, passengers, opts)

Fetching results

Default client (browser-impersonating)
// Returns (ResultList, error)
result, err := flights.GetFlightsDirect(q, "", nil)

// With a proxy
result, err := flights.GetFlightsDirect(q, "http://user:pass@proxy:8080", nil)
Get only the raw HTML (for your own parser)
html, err := flights.FetchFlightsHTML(q, "", nil)
Parse from HTML you already have
result, err := flights.Parse(htmlString)
Parse from the raw JS blob (for testing / custom fetching)
result, err := flights.ParseJS(jsScriptContent)

Result model

type ResultList struct {
    Items    []Flights
    Metadata JsMetadata   // Airlines, Alliances
}

type Flights struct {
    Type     string       // e.g. "6E", "nonstop"
    Price    *int         // nil when Google returns an unpriced itinerary
    Airlines []string
    Flights  []SingleFlight
    Carbon   CarbonEmission
}

type SingleFlight struct {
    FromAirport Airport
    ToAirport   Airport
    Departure   SimpleDatetime   // Date [3]int{year,month,day}, Time [2]int{hour,min}
    Arrival     SimpleDatetime
    Duration    int              // minutes
    PlaneType   string
}

Integrations

Plug-in integrations replace the default fetch or parse pipeline entirely.

BrightData — protect your IP

Routes requests through the BrightData SERP API.
Credentials are read from BRIGHT_DATA_API_KEY / BRIGHT_DATA_API_URL env vars, or set explicitly.

import "github.com/chaurasiayush/go-flights/integrations"

bd, err := integrations.NewBrightData(
    integrations.WithBrightDataAPIKey("your-key"),
    integrations.WithBrightDataZone("serp_api1"),   // optional, default: serp_api1
)
if err != nil {
    panic(err)
}

result, err := flights.GetFlightsDirect(q, "", bd)
SearchApi — richer structured data

Uses the SearchApi Google Flights endpoint for more consistent results and richer fields (amenities, booking options, price insights, etc.).
API key read from SEARCHAPI_KEY or set explicitly.

import "github.com/chaurasiayush/go-flights/integrations/searchapi"

api, err := searchapi.New(
    searchapi.WithSearchAPIKey("your-key"),
)
if err != nil {
    panic(err)
}

raw, err := flights.GetFlights(q, "", api)
result := raw.(*searchapi.Result)

// Rich fields exclusive to SearchApi
result.PriceInsights.LowestPrice
result.PriceInsights.PriceLevel       // "low" | "typical" | "high"
result.Flights[0].Segments[0].Amenities.Wifi
result.Flights[0].BookingToken
result.BookingOptions
result.CheaperAlternatives
SearchApi result model (abbreviated)
type Result struct {
    PriceInsights          PriceInsights
    Flights                []Flight
    PassengerAssistanceLinks []AirlineLink
    BaggageAllowanceLinks  []AirlineLink
    BookingOptions         []BookingOption
    CheaperAlternatives    []CheaperAlternative
}

type Flight struct {
    IsBest        bool
    Airlines      string        // "United · Lufthansa"
    FlightNumbers []string
    Segments      []Segment
    Layovers      []Layover
    DurationMin   int
    Stops         int
    Carbon        *CarbonEmissions
    Price         *int
    BookingToken  *string
}

type Segment struct {
    Departure, Arrival Airport
    Airline            string
    FlightNumber       string
    DurationMin        int
    Airplane           string
    Amenities          *Amenities  // Wi-Fi, legroom, USB, video, etc.
}

Writing a custom integration

Implement FetchIntegration to replace only the HTTP step (HTML is still parsed by the built-in parser):

type MyFetcher struct{}

func (f *MyFetcher) FetchHTML(q flights.QueryOrString) (string, error) {
    // fetch and return the raw Google Flights HTML page
}

result, err := flights.GetFlightsDirect(q, "", &MyFetcher{})

Implement DataSourceIntegration to replace the entire pipeline:

type MySource struct{}

func (s *MySource) Fetch(q flights.QueryOrString) (any, error) {
    // return any type you like
}

raw, err := flights.GetFlights(q, "", &MySource{})

Building the URL (debugging)

u, err := q.URL()
// https://www.google.com/travel/flights/search?tfs=GhoSCj...&hl=en&curr=USD

params, err := q.Params()
// map[string]string{"tfs": "GhoSCj...", "hl": "en", "curr": "USD"}

Running tests

go test ./...

All 41 tests run offline (no live HTTP). The live smoke-test CLI is in cmd/run/:

go run ./cmd/run/main.go

Project structure

go-flights/
├── errors.go                      # ErrFlightsNotFound
├── types.go                       # SeatType, TripType, Language, Currency
├── model.go                       # Result structs
├── querying.go                    # FlightQuery, Query, Passengers, CreateQuery
├── parser.go                      # Parse, ParseJS
├── fetcher.go                     # GetFlights, GetFlightsDirect, FetchFlightsHTML
├── headers.go                     # Browser-impersonating request headers
├── pb/
│   ├── flights.proto              # Protobuf schema (source of truth)
│   └── flights.pb.go              # Generated bindings (do not edit)
├── integrations/
│   ├── brightdata.go              # BrightData FetchIntegration
│   └── searchapi/
│       ├── models.go              # Rich SearchApi result types
│       └── searchapi.go           # SearchApi DataSourceIntegration
├── internal/
│   └── jsonutil/                  # Type-safe helpers for untyped JSON traversal
└── cmd/run/
    └── main.go                    # Live smoke-test CLI

Regenerating protobuf bindings

If you modify pb/flights.proto:

protoc \
  --go_out=. \
  --go_opt=paths=source_relative \
  --go_opt=Mpb/flights.proto=github.com/chaurasiayush/go-flights/pb \
  pb/flights.proto

Requires protoc and protoc-gen-go:

brew install protobuf
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

Dependencies

Package Purpose
bogdanfinn/tls-client Chrome-fingerprinting TLS client (bot detection bypass)
bogdanfinn/fhttp HTTP client with ordered header support
golang.org/x/net/html HTML parser for <script class="ds:1"> extraction
google.golang.org/protobuf Protobuf serialisation for the ?tfs= query parameter

Limitations & known risks

  • Google can change the JSON payload structure at any time. The parser uses positional index access. If Google reorganises the ds:1 data blob, results will be wrong or empty. Run the test suite against a fresh live page periodically.
  • TLS fingerprint may be detected. If Google updates bot detection, switch to the BrightData integration as a drop-in.
  • Not an official API. Use responsibly and in accordance with Google's Terms of Service.

License

MIT — same as the original fast-flights Python library.

Documentation

Overview

Package flights is a strongly-typed Go client for scraping Google Flights. It encodes queries as Base64-encoded Protobuf strings (the ?tfs= param), fetches the page with a browser-impersonating HTTP client, and parses the embedded JavaScript data blob.

Index

Constants

This section is empty.

Variables

View Source
var ErrFlightsNotFound = errors.New("flights: no flights found")

ErrFlightsNotFound is returned when Google Flights returns no results or an explicit error response for the requested query.

Functions

func FetchFlightsHTML

func FetchFlightsHTML(q QueryOrString, proxy string, fetchIntegration FetchIntegration) (string, error)

FetchFlightsHTML fetches the raw Google Flights HTML page for the given query. When proxy is non-empty it is used as the HTTP proxy. When fetchIntegration is non-nil, the integration's FetchHTML method is called instead of the default TLS-impersonating client.

func GetFlights

func GetFlights(q QueryOrString, proxy string, integration Integration) (any, error)

GetFlights fetches Google Flights results for the given query.

  • If integration is nil, the built-in browser-impersonating client is used and the HTML is parsed into a ResultList.
  • If integration implements FetchIntegration, its FetchHTML is called and the result is parsed.
  • If integration implements DataSourceIntegration, its Fetch method is called directly (bypassing HTML fetch + parse entirely).

func Int32P

func Int32P(v int32) *int32

Int32P is a convenience helper that returns a pointer to v. Use it when setting optional int32 fields on FlightQuery and QueryOptions:

leg.MaxStops = flights.Int32P(1)

Types

type Airline

type Airline struct {
	Code string
	Name string
}

Airline holds basic airline metadata returned in the page's JS bundle.

type Airport

type Airport struct {
	Code string
	Name string
}

Airport is an IATA-coded airport with a human-readable name.

type Alliance

type Alliance struct {
	Code string
	Name string
}

Alliance holds basic alliance metadata returned in the page's JS bundle.

type CarbonEmission

type CarbonEmission struct {
	// TypicalOnRoute is the typical CO₂ for this route in grams.
	TypicalOnRoute int
	// Emission is the actual emission for this itinerary in grams.
	Emission int
}

CarbonEmission holds CO₂ data associated with a whole itinerary.

type Currency

type Currency = string

Currency is an ISO 4217 currency code accepted by Google Flights. An empty string tells Google to use the requester's locale.

type DataSourceIntegration

type DataSourceIntegration interface {
	Integration
	Fetch(q QueryOrString) (any, error)
}

DataSourceIntegration completely replaces the fetch + parse pipeline with an external data source (e.g. SearchApi). Its Fetch method returns an arbitrary result type.

type FetchIntegration

type FetchIntegration interface {
	Integration
	FetchHTML(q QueryOrString) (string, error)
}

FetchIntegration replaces the default HTTP fetch step. Its FetchHTML method returns the raw HTML of the Google Flights page, which is then parsed by the standard parser.

type FlightQuery

type FlightQuery struct {
	// Date is the departure date in "YYYY-MM-DD" format. A time.Time is also
	// accepted via NewFlightQueryFromTime.
	Date        string
	FromAirport string
	ToAirport   string

	MaxStops              *int32
	Airlines              []string
	EarliestDepartureHour *int32
	LatestDepartureHour   *int32
	EarliestArrivalHour   *int32
	LatestArrivalHour     *int32
	MaxDurationMinutes    *int32
	ConnectingAirports    []string
	MinLayoverMinutes     *int32
	MaxLayoverMinutes     *int32
	LessEmissionsOnly     bool
}

FlightQuery describes a single flight leg within a search.

All optional filter fields use pointer types so the zero value (nil) means "not set", which is distinct from 0 (a meaningful value for some filters).

func NewFlightQuery

func NewFlightQuery(date, from, to string) FlightQuery

NewFlightQuery constructs a FlightQuery with the given date string.

func NewFlightQueryFromTime

func NewFlightQueryFromTime(date time.Time, from, to string) FlightQuery

NewFlightQueryFromTime constructs a FlightQuery from a time.Time value.

func (FlightQuery) WithMaxStops

func (q FlightQuery) WithMaxStops(m int32) FlightQuery

WithMaxStops returns a copy of q with MaxStops set to m.

type Flights

type Flights struct {
	// Type is the label Google attaches to the itinerary (e.g. "nonstop").
	Type string
	// Price is the fare amount in the requested currency, or nil when
	// Google returns an unpriced itinerary.
	Price    *int
	Airlines []string
	Flights  []SingleFlight
	Carbon   CarbonEmission
}

Flights is a complete bookable itinerary returned by Google Flights.

type Integration

type Integration interface{}

Integration is a marker type; all plug-in types implement either FetchIntegration or DataSourceIntegration.

type JsMetadata

type JsMetadata struct {
	Airlines  []Airline
	Alliances []Alliance
}

JsMetadata carries the airline and alliance tables extracted from the page.

type Language

type Language = string

Language is an IETF BCP-47 language tag accepted by Google Flights. An empty string tells Google to use the requester's locale.

type Passengers

type Passengers struct {
	Adults        int
	Children      int
	InfantsInSeat int
	InfantsOnLap  int
}

Passengers holds a passenger headcount breakdown. Total must not exceed 9, and InfantsOnLap must not exceed Adults.

func NewPassengers

func NewPassengers(adults, children, infantsInSeat, infantsOnLap int) (Passengers, error)

NewPassengers validates and returns a Passengers value.

type Query

type Query struct {
	// contains filtered or unexported fields
}

Query is the serialisable search query. Build one with CreateQuery.

func CreateQuery

func CreateQuery(flights []FlightQuery, passengers Passengers, opts QueryOptions) (*Query, error)

CreateQuery builds a Query from a list of flight legs and options. If opts.MaxStops is set, it is applied to every leg that has no per-leg MaxStops already set.

func (*Query) Currency

func (q *Query) Currency() string

Currency returns the currency code.

func (*Query) FlightData

func (q *Query) FlightData() []*pb.FlightData

FlightData returns the raw protobuf FlightData slice (used by integrations).

func (*Query) Language

func (q *Query) Language() string

Language returns the language tag.

func (*Query) Params

func (q *Query) Params() (map[string]string, error)

Params returns the URL query parameters as a map suitable for http request building.

func (*Query) Passengers

func (q *Query) Passengers() []pb.Passenger

Passengers returns the raw passenger enum slice (used by integrations).

func (*Query) SeatType

func (q *Query) SeatType() (SeatType, error)

SeatType returns the human-readable seat type.

func (*Query) ToBytes

func (q *Query) ToBytes() ([]byte, error)

ToBytes serialises the query to protobuf wire bytes.

func (*Query) ToStr

func (q *Query) ToStr() (string, error)

ToStr serialises the query to a Base64-encoded string (the value for Google's `?tfs=` query parameter).

func (*Query) TripType

func (q *Query) TripType() (TripType, error)

TripType returns the human-readable trip type.

func (*Query) URL

func (q *Query) URL() (string, error)

URL returns the full Google Flights search URL for this query.

type QueryOptions

type QueryOptions struct {
	Seat     SeatType
	Trip     TripType
	Language Language
	Currency Currency

	// MaxStops applies to every leg that doesn't already have MaxStops set.
	MaxStops *int32
	// MaxPrice is the maximum fare in the selected Currency.
	MaxPrice                    *int32
	CarryOnBags                 int
	CheckedBags                 int
	HideSeparateAndSelfTransfer bool
	ExcludeBasicEconomy         bool
}

QueryOptions holds the global (non-per-leg) settings for a search.

type QueryOrString

type QueryOrString interface {
	// contains filtered or unexported methods
}

QueryOrString accepts either a *Query (structured) or a plain string search term, mirroring the Python API's `q: Query | str` parameter.

type ResultList

type ResultList struct {
	Items    []Flights
	Metadata JsMetadata
}

ResultList is the parsed list of itineraries, with page metadata attached.

func GetFlightsDirect

func GetFlightsDirect(q QueryOrString, proxy string, fi FetchIntegration) (ResultList, error)

GetFlightsDirect is a type-safe convenience wrapper that returns a ResultList directly, for callers that use the default fetcher or a FetchIntegration.

func Parse

func Parse(htmlContent string) (ResultList, error)

Parse extracts the embedded Google Flights data from a raw HTML page string and returns a ResultList.

func ParseJS

func ParseJS(js string) (ResultList, error)

ParseJS parses the raw text content of the <script class="ds:1"> element. It is exported so unit tests can call it directly with fixture data, mirroring the Python codebase's test strategy.

type SeatType

type SeatType string

SeatType represents a cabin class.

const (
	SeatEconomy        SeatType = "economy"
	SeatPremiumEconomy SeatType = "premium-economy"
	SeatBusiness       SeatType = "business"
	SeatFirst          SeatType = "first"
)

type SimpleDatetime

type SimpleDatetime struct {
	// Date is [year, month, day].
	Date [3]int
	// Time is [hour, minute].
	Time [2]int
}

SimpleDatetime holds a calendar date and a wall-clock time as separate integer tuples, matching Google's sparse encoding exactly.

type SingleFlight

type SingleFlight struct {
	FromAirport Airport
	ToAirport   Airport
	Departure   SimpleDatetime
	Arrival     SimpleDatetime
	// Duration is flight time in minutes.
	Duration  int
	PlaneType string
}

SingleFlight is one leg within a multi-segment itinerary.

type StringQuery

type StringQuery string

StringQuery wraps a plain-text search string.

type TripType

type TripType string

TripType describes the itinerary shape.

const (
	TripOneWay    TripType = "one-way"
	TripRoundTrip TripType = "round-trip"
	TripMultiCity TripType = "multi-city"
)

Directories

Path Synopsis
cmd
run command
Package integrations provides plug-in integrations for the flights package.
Package integrations provides plug-in integrations for the flights package.
searchapi
Package searchapi implements the SearchApi DataSourceIntegration for the flights package, providing richer structured results than the direct scraper.
Package searchapi implements the SearchApi DataSourceIntegration for the flights package, providing richer structured results than the direct scraper.
internal
jsonutil
Package jsonutil provides helpers for navigating deeply-nested JSON structures decoded into []any / map[string]any values.
Package jsonutil provides helpers for navigating deeply-nested JSON structures decoded into []any / map[string]any values.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL