# Flight Price Aggregator API 💰 — 5 Sources, One Query (`memo23/flights-aggregator-scraper`) Actor

Compare flight prices across Ryanair, Wizz Air, EasyJet, Google Flights & Skyscanner in one call. Fans out in parallel, merges by flight identity & cross-source price-matches — so you see who sells each flight cheapest. One-way & round-trip: fares, airlines, times, stops. Pure HTTP, JSON/CSV

- **URL**: https://apify.com/memo23/flights-aggregator-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Travel, AI, Agents
- **Stats:** 30 total users, 27 monthly users, 99.7% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $2.50 / 1,000 flight offers

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Flight Price Aggregator API — 7 Sources, One Query 💰

> ⭐ **Useful?** [Leave a review](https://apify.com/memo23/flights-aggregator-scraper/reviews) — it takes 10 seconds and is the single biggest thing that helps other travel developers find this aggregator.

**Compare flight prices across Google Flights, Skyscanner, Kiwi, Ryanair, Wizz Air, EasyJet and Norwegian in a single call — or batch up to 50 routes in one run.** One search fans out to all seven sources in parallel, merges the results by flight identity, and cross-source price-matches — so for every flight you instantly see which source sells it cheapest.

![How it works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-flights-aggregator.png)

### Why this actor

Most flight scrapers give you **one** source's view. Low-cost carriers are almost always cheaper booked **direct** than through a metasearch aggregator — but you only know that if you can see both prices side by side. This actor does exactly that:

- **Direct LCC fares** from Ryanair, Wizz Air, EasyJet and Norwegian — the real, bookable price, not a marked-up resale.
- **Broad coverage** from Google Flights, Skyscanner and Kiwi — every airline on the route, including the legacy carriers the LCCs don't show.
- **One merged view** — the same physical flight from multiple sources collapses into a single row with a per-source price map (`prices`), the list of sources that returned it (`sourcesFound`), and the cheapest seller tagged (`cheapestSource`).
- **Batch mode** — pass a `routes` array and monitor a whole route portfolio in one run instead of scheduling N separate actors.
- **Result filters** — `directOnly`, `maxStops`, `maxDurationMinutes` applied server-side, so your downstream code gets exactly the flights you care about.

The payoff is concrete: on a typical London→Paris search the same EasyJet flight comes back at **£14.99 direct** vs **$41 via Google Flights** — and the actor tags the direct fare as cheapest automatically.

### Use cases

- **Fare-alert products** — batch your subscribers' routes into one scheduled run, push a notification whenever the cheapest price drops below a threshold. Per-source price, cheapest seller and booking URL are already in the payload.
- **Price comparison / meta-search tools** — power a "compare all sources" feature without integrating seven APIs and managing seven sets of blocks.
- **Deal hunting at scale** — 50 routes per run × nonstop-only filter = a clean daily feed of direct-flight deals.
- **Market & pricing research** — see how direct LCC pricing compares to aggregator pricing at scale, with aligned timestamps across sources in one run.
- **AI agents** — pair with the companion MCP server to give an assistant a `search_flights` tool.

### Input

| Field | Required | Description |
|---|---|---|
| `origin` | ✅\* | Origin — city name (`London`) or airport IATA code (`LGW`) |
| `destination` | ✅\* | Destination — city name or IATA code |
| `departDate` | ✅\* | Outbound date `YYYY-MM-DD` |
| `returnDate` | | Return date `YYYY-MM-DD` (omit for one-way) |
| `routes` | | **Batch mode:** array of `{origin, destination, departDate?, returnDate?}` — up to 50 routes in one run. Per-route dates fall back to the top-level dates. When set, top-level `origin`/`destination` are ignored. |
| `adults` / `children` | | Passenger counts (defaults 1 / 0) |
| `currency` | | Preferred display currency, ISO 4217 (default `USD`) |
| `cabinClass` | | `ECONOMY` / `PREMIUM_ECONOMY` / `BUSINESS` / `FIRST` |
| `directOnly` | | `true` = only nonstop flights |
| `maxStops` | | Only flights with at most this many stops |
| `maxDurationMinutes` | | Only flights up to this total duration (e.g. `300` = 5 h) |
| `sources` | | Subset of the seven sources; omit to query all |
| `maxPerSource` | | Cap on offers per source before merging (default 10) |

\* Either set `origin` + `destination` + `departDate` at the top level, or provide them per route in `routes`.

#### Single route

```json
{
  "origin": "LGW",
  "destination": "CDG",
  "departDate": "2026-08-12",
  "returnDate": "2026-08-19",
  "maxPerSource": 8
}
```

#### Batch mode — one run, many routes

```json
{
  "routes": [
    { "origin": "London", "destination": "Barcelona" },
    { "origin": "London", "destination": "Madrid" },
    { "origin": "STN", "destination": "DUB", "departDate": "2026-09-01" }
  ],
  "departDate": "2026-08-12",
  "directOnly": true,
  "currency": "EUR"
}
```

### Output

One row per merged offer:

```json
{
  "source": "easyjet",
  "direction": "outbound",
  "origin": "LGW",
  "destination": "CDG",
  "date": "2026-08-12",
  "price": 14.99,
  "currency": "GBP",
  "carrier": "easyJet",
  "flightNumber": "U28405",
  "departTime": "16:10",
  "arriveTime": "18:25",
  "stops": 0,
  "prices": { "easyjet": 14.99, "googleflights": 41 },
  "sourcesFound": ["easyjet", "googleflights"],
  "cheapestSource": "easyjet",
  "isCheapest": true
}
```

| Field | Meaning |
|---|---|
| `prices` | Every source's price for this exact flight, keyed by source |
| `sourcesFound` | Which sources returned this flight |
| `cheapestSource` | The source selling it cheapest |
| `isCheapest` | `true` on the single cheapest offer per direction |
| `stops` / `durationMinutes` | Stop count and total duration (filterable via input) |

Export as JSON, CSV, Excel or XML from the run page, or pull via the Apify API.

### Code examples

#### curl

```bash
curl -X POST 'https://api.apify.com/v2/acts/memo23~flights-aggregator-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "routes": [
      { "origin": "LHR", "destination": "BCN" },
      { "origin": "LHR", "destination": "MAD" }
    ],
    "departDate": "2026-08-12",
    "directOnly": true,
    "currency": "EUR"
  }'
```

#### Python (apify-client)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("memo23/flights-aggregator-scraper").call(run_input={
    "origin": "London",
    "destination": "Barcelona",
    "departDate": "2026-08-12",
    "returnDate": "2026-08-19",
    "maxStops": 1,
})

for offer in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(offer["flightNumber"], offer["price"], offer["cheapestSource"], offer["prices"])
```

#### Node.js (apify-client)

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('memo23/flights-aggregator-scraper').call({
  routes: [{ origin: 'STN', destination: 'DUB' }, { origin: 'LTN', destination: 'KRK' }],
  departDate: '2026-08-12',
  directOnly: true,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.table(items.map((o) => ({ flight: o.flightNumber, price: o.price, cheapest: o.cheapestSource })));
```

### How it compares

| Feature | This actor | Single-source scraper (Google Flights only) | Other multi-source scraper | Browser-based scraper |
|---|---|---|---|---|
| Sources per run | **7** (Google Flights, Skyscanner, Kiwi, Ryanair, Wizz Air, EasyJet, Norwegian) | 1 | 5–7 | 1–2 |
| Routes per run | **Up to 50** (batch mode) | 1 | 1 | 1 |
| Per-source price map + cheapest seller | **Yes** | No | Yes | No |
| Direct-only / max-stops / max-duration filters | **Yes, server-side** | Rare | No (post-process yourself) | Rare |
| City-name input (auto-resolves to IATA) | **Yes** | IATA only | IATA only | Varies |
| Children in passenger count | **Yes** | Varies | Adults only | Varies |
| Scraping engine | **Raw HTTP** | Raw HTTP | Raw HTTP | Headless browser |
| Companion MCP server for AI agents | **Yes** | Rare | Varies | Rare |

The honest take: if you only need one source's view of one route, a single-source scraper is fine. If you monitor multiple routes, want the *bookable direct* LCC fare next to the metasearch price, or need filtered results without post-processing, this is the one to pick.

### Notes & FAQ

- **Which routes do the LCCs cover?** Ryanair, Wizz Air, EasyJet and Norwegian only return flights on **their own** (mostly European) routes. For a route they don't fly they simply return nothing — Google Flights, Skyscanner and Kiwi still cover it. A source with no data for a route never fails the run.
- **How do the filters treat price-only rows?** Some sources (e.g. Skyscanner round-trip totals) return a price without full stop/duration data. Norwegian rows come from its low-fare calendar (per-day cheapest price incl. stop count, no flight number/times). Whenever `directOnly`, `maxStops` or `maxDurationMinutes` is set, those price-only rows are excluded — an "unknown stops" row surviving a nonstop filter would be misleading.
- **Currencies.** Sources that accept a currency use yours; a couple price in the departure-country currency, so cross-currency comparison is best-effort. Each row carries its own `currency`.
- **Skyscanner rows.** Skyscanner returns one combined round-trip price rather than a per-leg fare, so its rows are marked as price-only and kept out of the per-flight cross-source match (to avoid comparing a round-trip total against a one-way fare).
- **Past dates.** A route with a past `departDate` is skipped with an explicit `PAST_DATE` row in the dataset (instead of silently returning nothing); the remaining routes still run.
- **Proxies.** Residential proxy is recommended (and the default) for the broadest, most stable coverage.
- **How often should I run it for fare monitoring?** Once or twice a day covers most routes; hourly for hot routes. More often than every 15 minutes rarely surfaces new prices.

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/flights-aggregator-scraper`).

**Purpose:** Fan out one flight search to 7 sources (Google Flights, Skyscanner, Kiwi, Ryanair, Wizz Air, EasyJet, Norwegian), merge by flight identity, and tag the cheapest seller per flight.

**Minimal input:**

```json
{
  "origin": "London",
  "destination": "Barcelona",
  "departDate": "2026-08-12",
  "maxPerSource": 8
}
```

Batch mode (overrides top-level origin/destination): `{ "routes": [{"origin":"LHR","destination":"BCN"},{"origin":"LHR","destination":"MAD"}], "departDate": "2026-08-12" }`

**Output:** one row per merged offer — source, direction, origin, destination, date, price, currency, carrier, flightNumber, departTime, arriveTime, stops, durationMinutes, prices {source: price}, sourcesFound, cheapestSource, isCheapest.

**Behaviors an agent should know:**

- No maxItems field; cap volume with `maxPerSource` (default 10, max 100) — keep ≤ 50 for quick runs. Row count scales with sources × routes.
- `routes` (array, max 50) is batch mode; when set the top-level `origin`/`destination` are ignored.
- The four LCC sources (Ryanair, Wizz Air, EasyJet, Norwegian) only return their own mostly-European routes; a source with no data never fails the run.
- `directOnly` / `maxStops` / `maxDurationMinutes` filter server-side and drop price-only calendar rows (e.g. Skyscanner round-trip totals, Norwegian low-fare-calendar rows).
- A past `departDate` is skipped with an explicit `PAST_DATE` row; other routes still run.
- Pay-per-event billing — an actor-start fee, one **route search** fee per origin→destination searched (batch mode bills one per route), and a small per-result fee. Current rates are on the Pricing tab of the actor page.
- **Maximum charge per run** works as a hard cost ceiling: the actor stops cleanly at the cap and pushes a `MAX_CHARGE_REACHED` row telling you how many routes were left unsearched. Long batches that would hit the run timeout stop early the same way (`TIME_BUDGET_EXHAUSTED` row) instead of timing out mid-route.

### ⚠️ Disclaimer

This actor collects only publicly available flight pricing and schedule information. It does not access any private, authenticated, or personal data. You are responsible for ensuring your use complies with the applicable websites' terms and with all relevant laws (including data-protection regulations). Prices and availability are provided as-is and change constantly — always confirm the final price on the airline's or seller's own site before booking.

### SEO Keywords

flight price comparison API, cheap flights scraper, multi-source flight scraper, Ryanair scraper, Wizz Air scraper, EasyJet scraper, Google Flights API, Skyscanner scraper, Kiwi flights API, Norwegian air scraper, flight fare aggregator, airfare comparison, low-cost carrier prices, flight deals API, metasearch flights, batch flight search, direct flights filter, one-way and round-trip fares, flight price monitoring, fare alert API, travel data API, MCP flight search

# Actor input Schema

## `origin` (type: `string`):

Origin as a city name (e.g. London, Barcelona) or an airport IATA code (e.g. LHR, JFK). A city resolves to its primary airport — pass an exact IATA to target another (e.g. STN).

## `destination` (type: `string`):

Destination as a city name (e.g. Barcelona) or an airport IATA code (e.g. BCN, CDG).

## `departDate` (type: `string`):

Outbound date. Format YYYY-MM-DD (e.g. 2026-08-12).

## `returnDate` (type: `string`):

Return date for a round trip. Leave empty for one-way.

## `adults` (type: `integer`):

Number of adult passengers.

## `children` (type: `integer`):

Number of child passengers.

## `currency` (type: `string`):

Preferred display currency (ISO 4217, e.g. USD, GBP, EUR). Some sources price in the departure-country currency; cross-currency comparison is best-effort.

## `cabinClass` (type: `string`):

Preferred cabin class.

## `routes` (type: `array`):

Optional batch mode: search many routes in ONE run. Array of objects — each needs "origin" and "destination" (city name or IATA); "departDate"/"returnDate" are optional per route and fall back to the top-level dates. When set, the top-level origin/destination are ignored. Max 50 routes per run; each route bills one route-search event. Example: \[{"origin":"LHR","destination":"BCN"},{"origin":"LHR","destination":"MAD","departDate":"2026-09-01"}]

## `directOnly` (type: `boolean`):

Only keep nonstop flights (stops = 0). Price-only calendar rows (no stop data) are excluded whenever a stop/duration filter is active.

## `maxStops` (type: `integer`):

Only keep flights with at most this many stops (0 = nonstop). Leave empty for no limit.

## `maxDurationMinutes` (type: `integer`):

Only keep flights whose total duration is at most this many minutes (e.g. 300 = 5h). Leave empty for no limit.

## `includeRouteSummary` (type: `boolean`):

Emit one cross-source comparison row per route into a separate "route-summaries" dataset: cheapest price and source per direction, the spread between the cheapest and most expensive source (what you save by booking the right one), every source's price, plus which sources answered, failed, or timed out. Kept out of the main dataset so your flight rows and CSV columns stay unchanged.

## `sources` (type: `array`):

Which flight sources to fan out to. Leave empty to query all seven. Note: Ryanair, Wizz Air, EasyJet and Norwegian only fly their own (mostly European) routes.

## `maxPerSource` (type: `integer`):

Cap on the number of offers returned by each source before merging.

## `proxyConfiguration` (type: `object`):

Proxy for the rate-limit-sensitive sources (Skyscanner, Google Flights). Residential is recommended.

## Actor input object example

```json
{
  "origin": "London",
  "destination": "Barcelona",
  "departDate": "2026-08-12",
  "returnDate": "2026-08-19",
  "adults": 1,
  "children": 0,
  "currency": "USD",
  "cabinClass": "ECONOMY",
  "directOnly": false,
  "includeRouteSummary": false,
  "sources": [],
  "maxPerSource": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "origin": "London",
    "destination": "Barcelona",
    "departDate": "2026-08-12"
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/flights-aggregator-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "origin": "London",
    "destination": "Barcelona",
    "departDate": "2026-08-12",
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/flights-aggregator-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "origin": "London",
  "destination": "Barcelona",
  "departDate": "2026-08-12"
}' |
apify call memo23/flights-aggregator-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=memo23/flights-aggregator-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/IkVzHICvrnnkSbkQY/builds/Yoc0JYDqhf3nr9Lm0/openapi.json
