SkyscannerAnswer

Is the Skyscanner API free? Fares without a partner key

No. Skyscanner gives API keys only to approved partners. Our API returns cached fares for any route at 1 credit a call, and 7 routes made 105 quotes.

5 min read

Short answer

  • Skyscanner has no self-serve API key. Access is by application to its Partnerships team, and approved partners earn commission on the travellers they send.
  • ToolzerHub's Skyscanner route returns the cheapest fares for a pair of places, with date, airports, airline and quote time, plus the cheapest price for each of the next 12 months.
  • A script logged 105 quotes on 7 routes in 38 seconds, 48 of them quoted within the last day. A call costs 1 credit.
On this page

There is no self-serve Skyscanner API key. Access is by application, Skyscanner approves partners one by one, and the docs name no price. To get fares for a few routes without that, you read what Skyscanner shows on its route pages. Our API does that, and the script below logs the fares to a CSV.

What Skyscanner's API is for

Skyscanner's travel API is for established travel businesses with a large audience. You apply to the Partnerships team, and approved partners get an account manager and earn commission. Flights come in two kinds: Live Prices run a real search across suppliers, and Indicative Prices are cached fares that the docs say can be "up to 4 days old". Partners get both. If you run a travel site and want bookable prices on your dates and cabin, apply there.

Cheapest fares by route with ToolzerHub

We make ToolzerHub. Our Skyscanner API reads Skyscanner's public route pages. /v1/skyscanner/route takes two Skyscanner place codes, a city (lond, nyca) or an airport (lhr, jfk), and costs 1 credit a call with the x-api-key header.

bash
export TOOLZERHUB_API_KEY=your_key
curl -H "x-api-key: $TOOLZERHUB_API_KEY" \
  "https://api.toolzerhub.com/v1/skyscanner/route?origin=lond&destination=nyca"

The cheapest of the 16 quotes for London to New York, inside data.deals, with images, ids and tracking parameters cut:

json
{
  "price": 210,
  "formatted_price": "£210",
  "direct": false,
  "legs": [
    {
      "date": "2026-11-23",
      "quoted_at": "2026-10-21T20:14Z",
      "origin": { "sky_code": "LGW", "name": "London Gatwick" },
      "destination": { "sky_code": "JFK", "name": "New York John F. Kennedy" },
      "carrier_name": "Ryanair"
    }
  ],
  "deep_link_path": "/transport/flights/LGW/JFK/20261123?locale=en-GB&market=UK&currency=GBP"
}

Each quote is one-way, for one date, with the time Skyscanner saw the price. Next to the quotes, the response has cheapest_by_month for 12 months, nearby_airports with a price each (Newark £220, LaGuardia £288 here) and the airlines flying the route direct.

Prices are in GBP from Skyscanner's UK site, and there are no date, cabin or passenger parameters: you get the quotes the page shows. Place names don't work, only codes; every response lists sky_code for its airports and nearby airports, which you can pass back in.

Log fares for the routes you watch

The script takes a list of routes, appends every quote to a CSV with its age in hours and a link, and prints the cheapest price by month. Run it from cron once a day to build a fare history, and drop repeats on origin, destination, date and quoted_at. Save it as skyscanner_fares.py and install requests (pip install requests).

python
import csv
import os
import sys
import time
from datetime import datetime, timezone

import requests

URL = "https://api.toolzerhub.com/v1/skyscanner/route"
HEADERS = {"x-api-key": os.environ.get("TOOLZERHUB_API_KEY", "")}
FIELDS = ["fetched_at", "origin", "destination", "date", "price", "currency", "direct",
          "carrier", "quoted_at", "age_hours", "link"]


def route(origin, destination, tries=3):
    # 429, 5xx and dropped connections are retried; failed calls cost no credits.
    params = {"origin": origin, "destination": destination}
    for attempt in range(tries):
        last = attempt == tries - 1
        try:
            r = requests.get(URL, headers=HEADERS, params=params, timeout=90)
        except requests.RequestException:
            if last:
                raise
            time.sleep(10 * (attempt + 1))
            continue
        if r.status_code in (429, 500, 502, 503, 504) and not last:
            time.sleep(10 * (attempt + 1))
            continue
        r.raise_for_status()
        return r.json()["data"]


def age_hours(quoted_at, now):
    quoted = datetime.strptime(quoted_at, "%Y-%m-%dT%H:%MZ").replace(tzinfo=timezone.utc)
    return round((now - quoted).total_seconds() / 3600, 1)


def main(routes, out="fares.csv"):
    now = datetime.now(timezone.utc)
    new_file = not os.path.exists(out)
    with open(out, "a", newline="", encoding="utf-8-sig" if new_file else "utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=FIELDS)
        if new_file:
            writer.writeheader()
        for pair in filter(None, (p.strip() for p in routes.split(","))):
            origin, _, destination = pair.partition("-")
            try:
                data = route(origin, destination)
            except requests.RequestException as e:  # e.g. a malformed code; go on to the next
                print(f"{pair}: {e}")
                continue
            if data is None:  # an unknown code, or no Skyscanner page for the pair
                print(f"{pair}: no fares")
                continue
            for deal in data["deals"]:
                leg = deal["legs"][0]  # one leg on route pages: one-way quotes
                writer.writerow({
                    "fetched_at": now.strftime("%Y-%m-%dT%H:%MZ"),
                    "origin": leg["origin"]["sky_code"],
                    "destination": leg["destination"]["sky_code"],
                    "date": leg["date"], "price": deal["price"], "currency": data["currency"],
                    "direct": deal["direct"], "carrier": leg.get("carrier_name"),
                    "quoted_at": leg["quoted_at"], "age_hours": age_hours(leg["quoted_at"], now),
                    "link": "https://www.skyscanner.net" + deal["deep_link_path"],
                })
            by_month = data.get("cheapest_by_month") or []
            months = ", ".join(f"{m['month']} {m['price']}" for m in by_month)
            print(f"{pair}: {len(data['deals'])} quotes, cheapest {data['cheapest_price']} "
                  f"{data['currency']}; by month: {months}")


if __name__ == "__main__":
    if len(sys.argv) < 2:
        sys.exit('usage: python skyscanner_fares.py "lond-nyca,ber-bcn" [file.csv]')
    if not HEADERS["x-api-key"]:
        sys.exit("set TOOLZERHUB_API_KEY first")
    main(*sys.argv[1:])

python skyscanner_fares.py "lond-nyca,lhr-jfk,ber-bcn,syd-mel,del-dxb,mad-lis,sfo-tyoa" made 7 calls in 38 seconds and wrote 105 quotes, 13 to 16 per route. The two city codes widened the search: lond-nyca returned quotes from Gatwick, Heathrow, Luton and Stansted to both JFK and Newark, and sfo-tyoa covered Narita and Haneda.

How fresh the fares are

Treat the quotes as a guide, not a booking price. Of the 105, 48 had been quoted in the last 24 hours and the median age was 31.9 hours, but 28 were older than 4 days. The oldest, a £25 Madrid to Lisbon fare, was 12 days old. The script's age_hours column lets you drop stale ones.

Freshness varied by route. Quotes into JFK from the two London searches had a median age of 10 hours; Delhi to Dubai, 120 hours.

What it costs

Each route is one call and 1 credit, so seven routes once a day is about 210 credits a month. The 100 free credits a month cover the first two weeks; $19 buys 10,000 credits that never expire, and plans for higher volume cover more routes. Set your key and the first fare log is a CSV in under a minute.

For other prices pulled from public pages, see our Ozon price parser in Python and Zillow listings to CSV. For Airbnb listings and calendars, see does Airbnb have a public API.

We are not affiliated with Skyscanner.

Get this data from your own code

The Skyscanner API returns it as JSON with one x-api-key header. 100 free credits monthly.