Skyscanner Flight Scraper avatar

Skyscanner Flight Scraper

Pricing

Pay per usage

Go to Apify Store
Skyscanner Flight Scraper

Skyscanner Flight Scraper

Scrapes Skyscanner flights via the internal web-unified-search JSON API — no browser needed (pure HTTP over residential proxy). Mirrors the jupri/skyscanner-flight technique.

Pricing

Pay per usage

Rating

0.0

(0)

Developer

deep sea

deep sea

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

2

Monthly active users

13 days ago

Last modified

Categories

Share

Scrapes flight itineraries from Skyscanner — prices, times, carriers, stops, and the full list of agents (sellers) with their ratings and booking links — into an Apify dataset. Search many dates in one run for a price-calendar view.

What you get

One record per itinerary, each carrying every agent that sells it:

{
"id": "13554-2612081040--32753-0-12712-2612081335|12712-2612151810--32753-0-13554-2612160620",
"price": 429,
"currency": "USD",
"carriers": "British Airways",
"stops": 0,
"origin": "LHR",
"destination": "JFK",
"departure": "2026-12-08T10:40",
"arrival": "2026-12-08T13:35",
"durationMinutes": 475,
"search_depart": "2026-12-08",
"search_return": "2026-12-15",
"search_complete": true,
"legs": [],
"agents": [
{
"name": "skyp",
"agent_name": "Kiwi.com",
"rating": 3.96,
"feedback_count": 8136,
"is_carrier": false,
"price": 429,
"url": "https://www.skyscanner.co.il/transport_deeplink/…"
}
]
}

Agent names and ratings come from the search feed itself, so every itinerary carries them — no extra per-itinerary requests, no partial coverage.

Input

Minimum: origin.0, target.0, depart.0.

FieldMeaning
origin.0..5 / target.0..5 / depart.0..5Per-leg origin, destination and YYYY-MM-DD date. Round trip: leg 0 outbound, leg 1 the return.
tripsMulti-date search: [{depart, return?, origin?, target?}]. One browser session serves every date, so N dates cost far less than N runs.
market / currencyISO country + currency, e.g. IL / USD.
sortscore | cheapest | fastest | departure
cabin_classeconomy | premiumeconomy | business | first
adults / children / infantsPassenger counts.
non_stop / one_stop / two_stopStop filters, any combination.
limitKeep only the first N itineraries per date, after sorting.
partial_enrich_topSee "Partial searches" below. Default 4.
fetch_details / details_limitAlso attach jupri-shaped pricing_options (fare brands) for the top N. Optional — agent names and ratings already arrive without it.

Single round trip:

{
"origin.0": "LHR", "target.0": "JFK", "depart.0": "2026-12-08",
"origin.1": "JFK", "target.1": "LHR", "depart.1": "2026-12-15",
"market": "IL", "currency": "USD",
"adults": 1, "non_stop": true, "sort": "cheapest"
}

Price calendar over several dates:

{
"origin.0": "LHR", "target.0": "JFK", "depart.0": "2026-12-08",
"trips": [
{ "depart": "2026-08-16", "return": "2026-08-20" },
{ "depart": "2026-08-23", "return": "2026-08-27" }
],
"market": "IL", "currency": "USD", "sort": "cheapest"
}

Partial searches — read this

Skyscanner's search is create-then-poll: the first response holds ~10 itineraries and the rest arrive as the search matures server-side. Sometimes it never matures. The search stops growing at ~10 itineraries and never reports complete. This is a server-side condition — the same thing happens to Skyscanner's own website — and it comes and goes in windows lasting hours.

The actor does not hide this:

  • Every record carries search_complete. When it is false, the date is a partial picture, not a genuinely thin route. Surface it in your UI rather than reporting "no availability".
  • A partial search still returns the right flights — correct cheapest-first ordering, prices and carriers — but its inline agents[] is starved to one seller per itinerary. So the actor automatically enriches the cheapest partial_enrich_top itineraries (default 4) through Skyscanner's itinerary-details endpoint, which does not depend on the search having completed. Those rows come back with their full seller list — typically 15–19 agents with names, ratings and booking links — in pricing_options.
  • Set partial_enrich_top: 0 to switch that off. Dates that complete normally never trigger it and cost nothing extra.

Re-running a partial date often returns a complete picture.

Performance and cost

Measured on a short-haul round trip, 2048 MB, direct flights only:

TimeRecords
2 dates, healthy70–90 s~209
4 dates, healthy180–240 s~427
Any date, partial~10, top 4 fully enriched

Cost is roughly $0.07–0.09 per run, dominated by residential proxy transfer ($8/GB), not by compute. It was $0.01–0.03 on the custom BUYPROXIES94952 group, which is included in the plan rather than metered per GB — but PerimeterX has flagged that group, so RESIDENTIAL is the working default. If you have a clean custom residential group, put it first in proxy_groups and the cost drops back. 2048 MB is the sweet spot; the run is bound by Skyscanner's own search latency (~15–25 s per date), not by local compute.

Extra dates are asked for in one burst from the first date's warm tab (parallel_replays, on by default). Measured on 4 dates, 5 runs per arm: identical data in every run, wall median 174 s → 114 s and p90 256 s → 141 s.

That is not the same thing as search_concurrency, which opens a tab per date and lets the site's own JS run in each. That one was measured worse — slower and prone to losing a whole date — and stays off. The difference is the extra tabs, not the concurrency.

Standby mode — a warm endpoint instead of a run

A normal run pays for a container boot and a PerimeterX pass every time. Standby keeps both alive and answers over HTTP, so a warm request is 9–12 s instead of 67–179 s. The first request after a cold start still pays the mint (~150 s).

GET https://<user>--skyscanner-flight.apify.actor/?origin=LHR&target=JFK
&depart=2026-12-08&return=2026-12-15
Authorization: Bearer <APIFY_TOKEN>

Several dates in one request — pass one return per depart, or none for one-way. Repeated params and comma lists are equivalent, so use whichever your client produces:

?origin=LHR&target=JFK&depart=2026-12-08&depart=2026-12-15
&return=2026-12-15&return=2026-12-22
?origin=LHR&target=JFK&depart=2026-12-08,2026-12-15&return=2026-12-15,2026-12-22

Every date runs on the one warm session, so a price calendar costs one round trip rather than N. Max 10 dates per request; all dates share the route and the filters.

Response — records is the flat list across all dates, and every record already carries search_depart / search_return, so it is groupable without reading trips:

{
"records": [ /* … */ ],
"count": 185,
"search_complete": false, // true only if EVERY date completed
"seconds": 11.6,
"warm": true,
"trips": [ // absent fields never change meaning for a 1-date request
{ "depart": "2026-08-16", "return": "2026-08-20", "count": 92, "search_complete": true },
{ "depart": "2026-08-23", "return": "2026-08-27", "count": 93, "search_complete": false }
]
}

Same query params as the input schema for non_stop / one_stop / two_stop, sort, limit, currency, adults, children, infants, cabin, partial_enrich_top. GET /healthz reports whether a warm session is held. A date that fails on its own returns an empty entry rather than failing the request; if every date fails the session is re-minted once and the request retried.

Notes

  • Booking URLs are rewritten to the local market host, so links stay on the same site the prices were quoted on.
  • Records are pushed as they land, so aborting a run keeps whatever was collected.
  • A live progress record in the run's key-value store reports the current phase and date-by-date progress, for driving a progress bar.