# Kiwi Cheapest Destinations Explorer (`apt_marble/kiwi-cheapest-destinations-explorer`) Actor

Pick a departure airport and a stretch of dates and get the destination cities Kiwi.com offers from there, each with its cheapest fare and the exact best day to fly. Typically 70-90 places per departure, one-way or return, filtered by price, region or country. 37 currencies.

- **URL**: https://apify.com/apt\_marble/kiwi-cheapest-destinations-explorer.md
- **Developed by:** [Hamza](https://apify.com/apt_marble) (community)
- **Categories:** Travel
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 destination founds

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

## Kiwi Cheapest Destinations Explorer

Turn "I'm free in October and I've got £150" into a ranked list of places you can actually go. Give it a departure airport or city and a stretch of dates, and it comes back with **the destination cities Kiwi.com offers from there** — each one with its cheapest fare, the exact best day to fly, and for return trips the best day to come home too. One departure place typically yields 70–90 destinations, sorted cheapest first; that is the selection Kiwi.com puts forward, not a complete index of everywhere it sells. Point it at several departure places, filter to a region, a set of countries or a price ceiling, and you have the raw material for a deals newsletter, a "where can I go" widget, a route-demand study or a weekly price watch.

### What you can do with it

- **Answer "where can I go for £150?"** Set a fare ceiling and get back only the destinations that fit, each with the date that makes it possible.
- **Build a deals feed or newsletter.** Run it on a schedule from your readers' home airports and every run is a fresh, ranked list of the cheapest escapes.
- **Compare departure airports.** Scan several places at once and see which one gets you further for less — every row records the airport the trip actually leaves from.
- **Plan a weekend break properly.** Return trips take a trip length in nights and pick both dates for you, so the answer is a real trip and not just a fare.
- **Focus on one part of the world.** Keep only Europe, only Asia, only a list of countries, or draw your own area on the map.
- **Feed a map or a dashboard.** Every destination carries its coordinates, its country and, if you want it, the ranking the source gives it — ready to plot.

### What you get

One row per destination city. Real example, abridged:

```json
{
  "source": "kiwi",
  "market": "US",
  "currency": "USD",
  "requestedCurrency": "USD",
  "currencyMismatch": false,
  "currencyMissing": false,
  "collectedAt": "2026-08-16T12:00:00.000Z",
  "scanId": "LON-2026-10-05-2026-10-19",
  "tripType": "oneway",
  "rank": 1,
  "originInput": "LON",
  "originSearched": "LON",
  "originCode": "LTN",
  "originName": "Luton",
  "originCity": "London",
  "originIsRequested": true,
  "destinationCode": "TIA",
  "destinationName": "Tirana International Airport Nënë Tereza",
  "destinationCity": "Tirana",
  "destinationCountry": "Albania",
  "destinationCountryCode": "AL",
  "lat": 41.415,
  "lng": 19.7208333,
  "price": 25,
  "priceFormatted": "$25",
  "priceEur": 21.609474,
  "bestDepartureDate": "2026-10-19",
  "bestDepartureTimeLocal": "2026-10-19T21:50:00",
  "bestReturnDate": null,
  "tripNights": null,
  "tripNightsInRequestedRange": null,
  "departureWindowStart": "2026-10-12",
  "departureWindowEnd": "2026-10-19",
  "requestedWindowStart": "2026-10-05",
  "requestedWindowEnd": "2026-10-19",
  "cabinClass": "ECONOMY",
  "adults": 1,
  "region": null,
  "popularityRank": null,
  "destinationsFound": 87,
  "isEstimated": false
}
```

### Input reference

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `origins` | array | — | Where the trip starts. Airport codes (`LHR`), city-wide codes (`LON`), or names (`Madrid`). Up to 20, each scanned separately. |
| `tripType` | string | `oneway` | `oneway` for the cheapest way out, `return` for a full trip with both dates chosen for you. |
| `departureWindowStart` | string | 30 days ahead | First day you could leave, as `YYYY-MM-DD`. Leave empty and it starts 30 days from the day the run happens. |
| `departureWindowEnd` | string | start + window length | Last day you could leave. The wider the window, the cheaper the best date it can find. |
| `windowLengthDays` | integer | `14` | Window length used when you have not set a last day. Up to 180. |
| `nightsMin` / `nightsMax` | integer | `3` and `7` | Return trips only: the shortest and longest stay you would accept. The best return date is chosen inside that range. |
| `region` | string | `anywhere` | Keep only destinations in one part of the world: Europe, North America, Central America & Caribbean, South America, Africa, Middle East, Asia, Oceania. |
| `geoBox` | object | — | Your own area instead of a named region, as `{"south": 34, "west": -25, "north": 72, "east": 45}`. It replaces the region choice, and rows from that run are labelled `Custom area` rather than with a region name they were not filtered to. |
| `countries` | array | — | Two-letter country codes to keep, for example `ES, PT, IT`. |
| `maxPrice` | integer | — | The "where can I go for 200" control: only destinations at or below this fare, in your chosen currency. |
| `maxStops` | integer | — | `0` for direct trips only, `1` for at most one connection. Empty allows any. |
| `excludeSelfTransferFares` | boolean | `false` | Leave out cheap trips stitched together from separate tickets, where nobody is responsible if the first leg runs late. |
| `maxDestinationsPerOrigin` | integer | `100` | How many destinations to keep per departure place, cheapest first. Up to 500. |
| `includePopularityRank` | boolean | `false` | Also collect the ranking the source gives each destination city. Needs a region or your own area. |
| `allowNearbyOrigins` | boolean | `false` | Keep trips that actually leave from a different airport than the one you asked for. Off by default; see the limits below. |
| `currency` | string | `USD` | One of 37 currencies. Every row also records the currency it was served in. |
| `market` | string | `US` | The market the search is prepared for; recorded on every row. It was measured not to change the fares — see the limits below. |
| `locale` | string | `en` | Language for city, airport and country names. |
| `cabinClass` | string | `ECONOMY` | `ECONOMY`, `PREMIUM_ECONOMY`, `BUSINESS` or `FIRST_CLASS`. |
| `adults` / `children` / `infants` | integer | `1` / `0` / `0` | Who the fares are priced for. Up to 9 seated travellers; infants cannot outnumber adults. |

### Output fields

| Field | What it is |
| --- | --- |
| `rank` | 1 is the cheapest destination from that departure place in that run. |
| `price`, `priceFormatted`, `currency` | The cheapest fare found for the whole party, the fare exactly as the source printed it, and the currency it was actually served in. `currency` is empty when the source named none. |
| `requestedCurrency`, `currencyMismatch` | What you asked for, and whether the fare came back in something else: `true` it did, `false` it did not, **empty when the fare arrived with no currency named** — in that case the number's unit is unknown and it must not be read as the currency you asked for. |
| `currencyMissing` | `true` on exactly those rows: the fare is the source's own number, but no currency came with it. The run summary counts them. |
| `priceEur` | The same fare in euros, always — a stable column to join or compare on. |
| `priceMagnitudeChecked` | `true` when the fare agrees with the price the source prints beside it. |
| `bestDepartureDate`, `bestDepartureTimeLocal` | The exact cheapest day inside your window, and the departure time on it. |
| `bestReturnDate`, `bestReturnTimeLocal` | The matching return leg for a return trip. |
| `tripNights` | Whole days between leaving and coming back. A trip that leaves late at night lands the next day, so this can read one higher than the nights you actually spend there. |
| `tripNightsInRequestedRange` | `false` when the trip spans a different number of days than the trip length you asked for. Those rows are kept, not hidden, so you can decide. |
| `destinationCity`, `destinationName`, `destinationCode` | The destination city, its airport and the airport's three-letter code. |
| `destinationCountry`, `destinationCountryCode` | Where it is. |
| `lat`, `lng` | Coordinates of the destination airport, ready to plot. |
| `originSearched`, `originInput` | The departure place you asked for, as a code and as you typed it. |
| `originCode`, `originName`, `originCity`, `originIsRequested` | The airport the trip really leaves from, and whether it is the one you asked for. |
| `tripType`, `cabinClass`, `adults`, `children`, `infants` | What was priced. |
| `departureWindowStart`, `departureWindowEnd` | The narrower window the source settled on for this destination. |
| `requestedWindowStart`, `requestedWindowEnd`, `nightsMin`, `nightsMax` | What you asked for, so a flexible search stays traceable. |
| `region` | The area the run was actually limited to: the region name, `Custom area` when you drew your own area (which replaces the region choice), or empty when you searched everywhere. The exact corners of a custom area are in the run summary. |
| `popularityRank` | The ranking the source gives that destination city; empty unless you turned ranking on. |
| `destinationsFound` | How many destinations matched everything you asked for, so you know what your per-origin limit cut off. |
| `destinationCityId` | A stable identifier for the destination city, handy for joining runs together. |
| `scanId`, `market`, `source`, `collectedAt` | Which scan produced the row, which market it was for, where it came from and when. |
| `isEstimated` | Always `false`: every value on the row was published by the source; nothing is modelled or filled in. |

### Pricing

You pay per destination, plus a small charge for each departure place that produces results:

| What you pay for | Price |
| --- | --- |
| **Destination found** — one destination city written to your dataset, with its cheapest fare and best date | **$2.50 per 1,000** |
| **Departure place scanned** — one departure place swept for the destinations the source offers from it | **$0.025 each** |

Worked example: three departure places, keeping the 100 cheapest destinations from each. That is 300 destinations at $2.50 per 1,000 — **$0.75** — plus three scans at $0.025 — **$0.075** — for **$0.83** in total.

A departure place that produces no destinations is not charged at all: the scan charge only applies once a destination from that place has actually been written. A departure place that could not be swept is reported as failed in the run summary and likewise costs nothing.

### Limits & what this actor cannot do

- **Fares are a snapshot.** Airline pricing moves continuously; a fare collected now can be gone minutes later.
- **It reports what Kiwi.com publishes to the public. It does not book, hold or reserve anything**, and cannot guarantee a fare is still available when you act on it.
- **The fare is the cheapest one for the search you asked for.** Taxes, carrier fees and baggage rules vary by airline and fare type, and extras are not included in this figure.
- **One row per destination city, not a list of flights.** Each row is the single cheapest way to reach that city inside your window. It is a discovery tool, not a timetable.
- **Sometimes the source offers a trip that leaves from a different airport than the one you asked for** — occasionally from another country — with nothing in the answer marking it. Those trips are left out by default and counted in the run summary, and every row tells you the airport it really leaves from. Turn on nearby departure airports if you would rather keep them.
- **A departure place with nothing to show is not always a real answer.** When a run asks an unrestricted question and gets a blank back, the scan is reported as **inconclusive** rather than as "there is nowhere to go from here", because those two look identical from the outside. Add a fare ceiling, a connection limit or a single-ticket rule and a blank answer becomes a real one: nothing matched.
- **A scan the source refuses is reported as a failed scan, never as "no destinations found."** If nothing at all could be swept, the run ends as failed rather than handing you an empty table that looks like a result.
- **The destinations you get are the source's own selection, not every city it sells.** How many come back is its decision: measured between 70 and 90 for a big-city departure, and six sweeps of the same departure place on the same dates came back with 81, 83, 85, 86, 87 and 88 of them. A city that is missing from a run was not put forward in that answer — it does not mean the city cannot be reached, and nothing on the row can tell those two apart. A small airport gives fewer. Your per-origin limit is applied on top, cheapest first.
- **Some city-wide codes cannot be searched.** Where a city code has no searchable entry, the actor refuses it up front and names the airports to use instead, rather than returning an empty list that looks like "nowhere to go".
- **A trip length in nights is a preference, not a promise.** A few trips come back a day longer or shorter than the range you set, usually because a late-night departure lands the next morning. Those rows are kept and flagged rather than quietly dropped, and the run summary counts them.
- **One-way and return trips only.** Multi-stop and open-jaw trips are not offered, because they are not reliably available here.
- **The currency changes the fare; the market does not.** Choosing a different currency gives you a different number, as you would expect. The market does not: the same search prepared for five different markets came back with identical fares in the same currency every time. The market is recorded on every row so runs stay traceable, but sweeping the same departure place in several markets is not a way to find different prices, and paying for it twice buys you the same fares with a different label. Whether a market ever changes which trips are on offer was not established either way.
- **Destination ranking is the source's own ordering**, published for a map area. It is not a measure of quality, and it is only collected when you ask for it and give the run an area to work in.
- **Routes are seasonal.** A place with nothing on offer in your window simply will not appear. Read that as "not offered here", not as "you cannot get there".
- **Speed depends on the size of the job** and on how quickly the source answers; no fixed throughput is promised.
- **Kiwi.com's terms govern automated access.** You are responsible for using the data lawfully and in line with those terms.

### FAQ

**Do I need an account on Kiwi.com?**
No. No account, no login, nothing to sign up for.

**Does it need my password or any personal detail?**
No. You give it departure places and dates, and nothing else.

**Can I schedule it?**
Yes. A daily or weekly run is the natural rhythm for a deals feed, and each run appends a fresh snapshot so you build your own history. Very short intervals are not worth paying for — these fares do not move minute to minute.

**Are these fares bookable?**
They were on sale when they were collected. Nothing is held or reserved for you, and airlines can change or withdraw a fare at any time, so treat every row as a lead rather than a guarantee.

**Why does a row leave from a different airport than the one I searched?**
It shouldn't — by default those trips are removed and counted separately, because the source will occasionally answer a small airport with departures from a big one hundreds of kilometres away. If you switched on nearby departure airports, they are kept and `originIsRequested` is `false` on each of them.

**How wide should my date window be?**
Wider is cheaper. A two-week window is a good default; up to 180 days is allowed, and the extra flexibility is exactly what lets it find the one cheap day.

**What is the difference between an empty scan and a failed one?**
An empty scan means the source ran your search and genuinely had nothing that matched your restrictions. A failed scan means it could not be swept at all. The run summary counts them separately, and a run where nothing could be swept ends as a failure rather than a green run with an empty table.

**Can I get fares in my own currency?**
Yes, choose from 37 currencies. Each row also carries the currency it was actually served in and a euro reference price, so you can always tell what a number means.

# Actor input Schema

## `origins` (type: `array`):

Where the trip starts. Use airport codes (LHR), city-wide codes (LON), or names (Madrid). Each one is scanned separately and produces its own list of destinations.

## `tripType` (type: `string`):

One-way gives you the cheapest way out. Return gives you a full trip with both dates chosen for you.

## `departureWindowStart` (type: `string`):

First day you could leave, as YYYY-MM-DD. Leave empty to start 30 days from the day the run happens, which is handy for a scheduled run.

## `departureWindowEnd` (type: `string`):

Last day you could leave, as YYYY-MM-DD. Leave empty and the window length below is used instead. The wider the window, the cheaper the best date it can find.

## `windowLengthDays` (type: `integer`):

How many days the departure window covers when you have not set a latest date. Up to 180.

## `nightsMin` (type: `integer`):

Return trips only: the shortest stay you would accept. The best return date is chosen inside this range. Leave empty for 3 to 7 nights.

## `nightsMax` (type: `integer`):

Return trips only: the longest stay you would accept. Leave empty for 3 to 7 nights.

## `region` (type: `string`):

Keep only destinations in this part of the world. Choose Anywhere to keep everything.

## `geoBox` (type: `object`):

An exact area to keep destinations inside, as {"south": 34, "west": -25, "north": 72, "east": 45}. Overrides the region choice above: rows are then labelled "Custom area" rather than with a region name the run was not limited to.

## `countries` (type: `array`):

Optional. Two-letter country codes to keep, for example ES, PT, IT. Everything else is left out.

## `maxPrice` (type: `integer`):

Only show destinations at or below this fare, in the currency you chose. This is the "where can I go for 200" control. Leave empty for no ceiling.

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

0 for direct trips only, 1 for at most one connection. Leave empty to allow any number.

## `excludeSelfTransferFares` (type: `boolean`):

Leave out the cheap trips that are stitched together from separate tickets, where nobody is responsible if the first leg runs late.

## `maxDestinationsPerOrigin` (type: `integer`):

How many destinations to keep for each departure place, cheapest first. Up to 500.

## `includePopularityRank` (type: `boolean`):

Also collect the ranking the source gives each destination city, so you can sort by prominence as well as by price. Needs a region or a custom map area.

## `allowNearbyOrigins` (type: `boolean`):

Off by default. When a trip actually leaves from a different airport than the one you asked for, it is left out. Turn this on to keep those trips; every row says which airport it really leaves from.

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

Currency the fares are requested in. Every row also records the currency it was actually served in.

## `market` (type: `string`):

The market the search is prepared for. It is recorded on every row so runs stay traceable. The same search prepared for five different markets came back with identical fares in the same currency, so changing it is not a way to find different prices — the currency setting is what changes the number.

## `locale` (type: `string`):

Language used for city, airport and country names.

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

Cabin to price the trips in.

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

Adult travellers the fares are priced for.

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

Children aged 2 to 11 travelling in their own seat.

## `infants` (type: `integer`):

Infants travelling on an adult's lap. Cannot be more than the number of adults.

## Actor input object example

```json
{
  "origins": [
    "LON"
  ],
  "tripType": "oneway",
  "windowLengthDays": 14,
  "region": "anywhere",
  "excludeSelfTransferFares": false,
  "maxDestinationsPerOrigin": 100,
  "includePopularityRank": false,
  "allowNearbyOrigins": false,
  "currency": "USD",
  "market": "US",
  "locale": "en",
  "cabinClass": "ECONOMY",
  "adults": 1,
  "children": 0,
  "infants": 0
}
```

# Actor output Schema

## `destinations` (type: `string`):

One row per destination: cheapest fare, best date to fly, airport, city and country.

## `overview` (type: `string`):

The same results as a table, cheapest first.

## `spreadsheet` (type: `string`):

All destinations in one CSV file.

## `runSummary` (type: `string`):

Counts for this run: destinations collected, departure places scanned, scans with nothing to show, and scans that could not be completed.

# 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 = {
    "origins": [
        "LON"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("apt_marble/kiwi-cheapest-destinations-explorer").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 = { "origins": ["LON"] }

# Run the Actor and wait for it to finish
run = client.actor("apt_marble/kiwi-cheapest-destinations-explorer").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "origins": [
    "LON"
  ]
}' |
apify call apt_marble/kiwi-cheapest-destinations-explorer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,apt_marble/kiwi-cheapest-destinations-explorer"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/2ZMx2IHwiwaope111/builds/tvYOgW91FGoSjzTbG/openapi.json
