npm package: @mattywhite/skyscanner-api
Description: A Node.js library for querying the reverse-engineered SkyScanner Android app API for flights, airports, locations, and car rentals, with built-in retries and error handling
Author: @irrisolto
Latest version: 1.0.0

# SkyScanner API Client (Node.js)

A Node.js library for querying the reverse-engineered SkyScanner Android app API for flights, airports, locations, and car rentals, with built-in retries and error handling.

> **Note**: This is a Node.js port of the original Python library. Original credit goes to @irrisolto.

## Table of Contents

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Class Reference](#class-reference)
- [Methods](#methods)
- [Types and Enums](#types-and-enums)
- [Error Handling](#error-handling)
- [Examples](#examples)

## Installation

```bash
npm install skyscanner-api
```

Or if you're installing from source:

```bash
npm install
```

## Quick Start

```javascript
const { SkyScanner, CabinClass, SpecialTypes } = require('skyscanner-api');

async function main() {
    // Initialize the client
    const scanner = new SkyScanner({
        locale: "en-US",
        currency: "USD",
        market: "US"
    });

    // Search for airports
    const airports = await scanner.searchAirports("London");
    const origin = airports[0];

    const destinationAirports = await scanner.searchAirports("New York");
    const destination = destinationAirports[0];

    // Search for flights
    const departDate = new Date('2025-08-15');
    const returnDate = new Date('2025-08-22');

    const response = await scanner.getFlightPrices({
        origin: origin,
        destination: destination,
        departDate: departDate,
        returnDate: returnDate,
        cabinClass: CabinClass.ECONOMY,
        adults: 2
    });

    console.log(response.json);
}

main().catch(console.error);
```

## Class Reference

### SkyScanner

The main client class for interacting with Skyscanner APIs.

#### Constructor

```javascript
const scanner = new SkyScanner({
    locale: "en-US",       // Locale code for results
    currency: "USD",       // Currency code for pricing
    market: "US",          // Market region code
    retryDelay: 2,         // Seconds to wait between polling retries
    maxRetries: 15,        // Maximum number of polling retries
    proxy: "",             // Proxy URL for HTTP requests
    pxAuthorization: null, // Optional pre-generated PX authorization token
    verify: true           // Whether to verify SSL certificates
});
```

**Parameters:**
- `locale` (string): Locale code for results (e.g., "en-US", "fr-FR")
- `currency` (string): Currency code for pricing (e.g., "USD", "EUR", "GBP")
- `market` (string): Market region code (e.g., "US", "UK", "DE")
- `retryDelay` (number): Seconds to wait between polling retries
- `maxRetries` (number): Maximum number of polling retries before giving up
- `proxy` (string): Proxy URL configuration for HTTP requests
- `pxAuthorization` (string | null): Optional pre-generated PX authorization token
- `verify` (boolean): Whether to verify SSL certificates

## Methods

### getFlightPrices()

Search for flight prices between two locations.

```javascript
await scanner.getFlightPrices({
    origin,              // Airport object
    destination,         // Airport | SpecialTypes
    departDate,          // Date | SpecialTypes | null
    returnDate,          // Date | SpecialTypes | null
    cabinClass,          // CabinClass (default: ECONOMY)
    adults,              // number (default: 1)
    childAges            // number[] (default: [])
});
```

**Parameters:**
- `origin` (Airport): Origin airport object
- `destination` (Airport | SpecialTypes): Destination airport or special search type (e.g., SpecialTypes.EVERYWHERE)
- `departDate` (Date | SpecialTypes | null): Departure date or SpecialTypes.ANYTIME
- `returnDate` (Date | SpecialTypes | null): Return date (optional for one-way trips)
- `cabinClass` (string): Cabin class preference (from CabinClass enum)
- `adults` (number): Number of adult passengers (1-8)
- `childAges` (number[]): Ages of child passengers (0-17 years, max 8 children)

**Returns:** `SkyscannerResponse` object containing flight options and pricing data

**Throws:**
- `Error`: Invalid dates, passenger counts, or search parameters
- `BannedWithCaptcha`: When blocked by Skyscanner's anti-bot measures
- `AttemptsExhaustedIncompleteResponse`: When max retries exceeded

### searchAirports()

Auto-suggest airports based on a search query.

```javascript
await scanner.searchAirports(query, departDate, returnDate);
```

**Parameters:**
- `query` (string): Search text (airport name, city, or IATA code)
- `departDate` (Date | null): Optional departure date for context
- `returnDate` (Date | null): Optional return date for context

**Returns:** Array of `Airport` objects matching the query

### searchLocations()

Auto-suggest locations for car rentals and other services.

```javascript
await scanner.searchLocations(query);
```

**Parameters:**
- `query` (string): Location search text

**Returns:** Array of `Location` objects

### getAirportByCode()

Retrieve a specific airport by its IATA code.

```javascript
await scanner.getAirportByCode(airportCode);
```

**Parameters:**
- `airportCode` (string): Three-letter IATA airport code (e.g., "JFK", "LHR")

**Returns:** `Airport` object for the specified code

**Throws:**
- `GenericError`: If airport code not found

### getItineraryDetails()

Get detailed information for a specific flight itinerary.

```javascript
await scanner.getItineraryDetails(itineraryId, response);
```

**Parameters:**
- `itineraryId` (string): Unique itinerary identifier from search results
- `response` (SkyscannerResponse): Original search response containing session data

**Returns:** Object with detailed itinerary information including flight legs and preferences

### getCarRental()

Search for car rental options between locations and times.

```javascript
await scanner.getCarRental({
    origin,           // Location | Coordinates | Airport
    departTime,       // Date
    returnTime,       // Date
    destination,      // Location | Coordinates | Airport | null
    isDriverOver25    // boolean (default: true)
});
```

**Parameters:**
- `origin` (Location | Coordinates | Airport): Pickup location
- `departTime` (Date): Pickup date and time
- `returnTime` (Date): Drop-off date and time  
- `destination` (Location | Coordinates | Airport | null): Drop-off location (defaults to origin)
- `isDriverOver25` (boolean): Driver age flag affecting pricing

**Returns:** Object containing car rental options and pricing

### getCarRentalFromUrl()

Parse a Skyscanner car rental URL and fetch rental options.

```javascript
await scanner.getCarRentalFromUrl(url);
```

**Parameters:**
- `url` (string): Skyscanner car hire URL

**Returns:** Car rental search results

**Example URL format:**
```
https://www.skyscanner.net/g/carhire-quotes/GB/en-GB/GBP/30/27544008/27544008/2025-07-01T10:00/2025-08-01T10:00/
```

## Types and Enums

### Airport
```javascript
const airport = new Airport(
    "London Heathrow",  // title
    "27544008",         // entityId
    "LHR"               // skyId (IATA code)
);
```

### Location
```javascript
const location = new Location(
    "London",           // entityName
    "27544008",         // entityId
    "51.5074,-0.1278"   // coordinates (lat,lng)
);
```

### CabinClass
- `CabinClass.ECONOMY`
- `CabinClass.PREMIUM_ECONOMY`  
- `CabinClass.BUSINESS`
- `CabinClass.FIRST`

### SpecialTypes
- `SpecialTypes.ANYTIME` - Flexible date search
- `SpecialTypes.EVERYWHERE` - Open destination search

## Error Handling

The library defines several custom exceptions:

### BannedWithCaptcha
Raised when Skyscanner blocks requests with CAPTCHA challenges.

```javascript
try {
    const response = await scanner.getFlightPrices({...});
} catch (error) {
    if (error instanceof BannedWithCaptcha) {
        console.log(`Blocked by anti-bot measures: ${error.captchaUrl}`);
        // Consider using proxies or reducing request frequency
    }
}
```

### AttemptsExhaustedIncompleteResponse
Raised when polling retries are exhausted without getting complete results.

```javascript
try {
    const response = await scanner.getFlightPrices({...});
} catch (error) {
    if (error instanceof AttemptsExhaustedIncompleteResponse) {
        console.log("Search timed out - try again later");
    }
}
```

### GenericError
General API errors with status codes and response details.

## Examples

### Basic Flight Search

```javascript
const { SkyScanner } = require('skyscanner-api');

async function searchFlights() {
    const scanner = new SkyScanner();

    // Find airports
    const londonAirports = await scanner.searchAirports("London");
    const heathrow = londonAirports[0];

    const nycAirports = await scanner.searchAirports("New York");
    const jfk = nycAirports[0];

    // Search flights
    const response = await scanner.getFlightPrices({
        origin: heathrow,
        destination: jfk,
        departDate: new Date('2025-09-01'),
        returnDate: new Date('2025-09-08'),
        adults: 1
    });

    console.log(response.json);
}

searchFlights().catch(console.error);
```

### Flexible Search (Anywhere, Anytime)

```javascript
const { SkyScanner, SpecialTypes } = require('skyscanner-api');

async function flexibleSearch() {
    const scanner = new SkyScanner();
    const londonAirports = await scanner.searchAirports("London");
    const heathrow = londonAirports[0];

    // Search from London to anywhere
    const response = await scanner.getFlightPrices({
        origin: heathrow,
        destination: SpecialTypes.EVERYWHERE,
        departDate: SpecialTypes.ANYTIME
    });

    console.log(response.json);
}

flexibleSearch().catch(console.error);
```

### Car Rental Search

```javascript
const { SkyScanner } = require('skyscanner-api');

async function searchCarRentals() {
    const scanner = new SkyScanner();

    // Search locations
    const locations = await scanner.searchLocations("London");
    const pickupLocation = locations[0];

    // Set dates
    const pickupDate = new Date('2025-07-01');
    pickupDate.setHours(10, 0);
    
    const returnDate = new Date('2025-07-08');
    returnDate.setHours(10, 0);

    // Search car rentals
    const rentals = await scanner.getCarRental({
        origin: pickupLocation,
        departTime: pickupDate,
        returnTime: returnDate,
        isDriverOver25: true
    });

    console.log(rentals);
}

searchCarRentals().catch(console.error);
```

## Rate Limiting and Best Practices

1. **Use Proxies**: Consider proxy rotation for high-volume usage  
2. **Cache Results**: Store airport/location searches to reduce API calls
3. **Validate Inputs**: Check dates and passenger counts before API calls
4. **Reuse PX Authorization**: X-Px-Authorization header isn't single use. Once you've made one you can use it for multiple requests, but once you get captcha you need to switch IP and authorization

## Dependencies

- `axios`: HTTP client for making API requests
- `https-proxy-agent`: Proxy support for HTTPS requests

## License

MIT

## Credits

Original Python library by @irrisolto on Discord.
Node.js port created for broader accessibility.

## Disclaimer

This library uses reverse-engineered Skyscanner Android app endpoints. Use at your own risk. The library is not affiliated with, endorsed by, or sponsored by Skyscanner.