# Google Flights Price Watch – Alerts, History & Deal Detection (`almoutasem_nabil/google-flights-price-watch`) Actor

Schedule it, get each route's best fare with history and Google's price verdict, plus alert rows when fares drop, hit a new low or fall below your target.

- **URL**: https://apify.com/almoutasem\_nabil/google-flights-price-watch.md
- **Developed by:** [Al Moutasem Nabil](https://apify.com/almoutasem_nabil) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $15.00 / 1,000 route checks

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Flights Price Watch – Alerts, History & Deal Detection

Give it routes and dates, run it on a schedule, and every run returns each route's best Google Flights fare
with its price history, the change since the last check and Google's own low / typical / high verdict — plus
an alert row when a fare drops, hits a new low or falls below your target.

### Who it's for

- **Travel agencies and corporate travel desks** watching their top routes every morning.
- **Deal newsletters** looking for fares that just dropped or went under a threshold.
- **Developers building fare-alert apps** who want history and alerts without running their own database.
- **AI agents** that need "is this a good price right now?" answered with numbers and Google's verdict.
- **Buyers in the Gulf and Levant**: fares in SAR, AED, JOD or EGP, and the local market's prices when you set
  the proxy country.

### What makes it different

Flight scrapers return today's fares and forget them. This Actor **remembers**:

- **History per route.** Each run adds a point; the row carries the last 30 checks, the previous price,
  the change in percent, and the lowest price seen (with when).
- **Alert rows you can wire to Slack or email.** `below-target`, `new-low`, `price-drop` (and `price-rise`
  if you want it), at most one per route per run.
- **Google's verdict built in.** `priceLevel` (low, typical, high) and Google's `typicalRange` for the trip.
- **Rolling dates.** `"+30d"` always means 30 days from the run, and keeps one history line even though the
  actual date moves every day.
- **Free failures.** A route Google blocked, a route with no flights, or a typo in the input comes back as a
  free error row that says why — never on the invoice.

### Quick start

1. Open the Actor, keep the prefilled form, and click **Start**. It watches two routes: AMM → LHR one-way in
   45 days (1 adult), and DXB → CAI round trip in 30 days, back 7 days later (2 adults, target $350):

```json
{
    "from": "AMM",
    "to": "LHR",
    "departDate": "+45d",
    "routes": [
        {
            "from": "DXB",
            "to": "CAI",
            "departDate": "+30d",
            "returnDate": "+37d",
            "adults": 2,
            "targetPrice": 350
        }
    ],
    "currency": "USD",
    "mode": "watch",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
}
```

2. It finishes in 15–30 seconds and costs $0.035. The **first run** returns one row per route with the best
   fare, `priceLevel`, `typicalRange` and the top itineraries; `previousPrice` and `changePct` are `null` and
   there are no alerts yet, because there is nothing to compare with. That first run is the baseline.
3. Run it again (or schedule it) with the same `trackingId`: rows now show `previousPrice`, `changePct` and a
   longer `history`, and alert rows appear when a rule fires.

### Alerts, and getting them into Slack or email

| Alert          | Fires when                                                                                                                                                                                                                      |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `below-target` | The best fare is at or under the route's `targetPrice` and no below-target alert has fired for that target yet. Fires again only if the fare falls a further `dropThresholdPct` while still below, or after it went back above. |
| `new-low`      | The best fare is lower than every earlier check of the route.                                                                                                                                                                   |
| `price-drop`   | The best fare fell at least `dropThresholdPct` (default 10%) since the previous check.                                                                                                                                          |
| `price-rise`   | Only with `alertOnRise`: the best fare rose at least `dropThresholdPct`.                                                                                                                                                        |

When several apply, the first in this table wins, so a route never produces more than one alert per run. The
first run for a tracking ID never alerts; a route that is already under its target on that first run alerts on
the second.

To be notified:

1. **Save as task** with your routes, then add a **Schedule** (daily is plenty for most routes).
2. Under **Integrations**, add Slack, Email or a webhook on *Run succeeded*.
3. Every run stores its alert rows alone in the `ALERTS` record of its key-value store — a JSON array, empty
   on a quiet run. Point a webhook, Make, Zapier or n8n at it (it is the **Alerts** output of the run), or
   filter the dataset on `type = "alert"`. The dataset's **Alerts** view shows the same rows in Console.

### Input

| Field                                                 | Default          | What it does                                                                                                                                                                            |
| ----------------------------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `from`, `to`                                          | –                | Quick route: 3-letter IATA airport codes, e.g. `AMM`, `LHR`.                                                                                                                            |
| `departDate`                                          | –                | `YYYY-MM-DD` or rolling `+Nd` (days after the run starts, UTC).                                                                                                                         |
| `returnDate`                                          | empty = one-way  | Same formats. Round-trip fares are the total for the whole trip.                                                                                                                        |
| `label`                                               | `FROM→TO`        | Your name for the route in the output.                                                                                                                                                  |
| `routes`                                              | `[]`             | More routes as JSON: `{ from, to, departDate, returnDate?, label? }` plus any of `cabin`, `adults`, `children`, `infantsInSeat`, `infantsOnLap`, `maxStops`, `targetPrice`, `dateFlex`. |
| `cabin`                                               | `economy`        | `economy`, `premium_economy`, `business` or `first`.                                                                                                                                    |
| `adults`, `children`, `infantsInSeat`, `infantsOnLap` | 1, 0, 0, 0       | Travellers; at most 9 in total and one lap infant per adult. Fares are for all of them together.                                                                                        |
| `maxStops`                                            | `any`            | `any`, `0` (nonstop), `1` or `2`.                                                                                                                                                       |
| `dateFlex`                                            | 0                | Also check up to ±3 days around each departure (the return date moves with it). Each extra date is one more route check: `2` = 5 checks per route.                                      |
| `currency`                                            | `USD`            | 3-letter ISO code.                                                                                                                                                                      |
| `topN`                                                | 10               | Itineraries (cheapest first) kept in each route row, 1–20.                                                                                                                              |
| `mode`                                                | `watch`          | `watch` keeps history and raises alerts; `snapshot` returns today's fares only.                                                                                                         |
| `trackingId`                                          | `default`        | Name of the watch. Two schedules with different IDs never share history.                                                                                                                |
| `dropThresholdPct`                                    | 10               | Minimum move, in percent, for `price-drop` and `price-rise`.                                                                                                                            |
| `alertOnRise`                                         | `false`          | Also alert on rises.                                                                                                                                                                    |
| `targetPrice`                                         | none             | Default target for every route; a route's own `targetPrice` wins.                                                                                                                       |
| `maxConcurrency`                                      | 2                | Routes checked in parallel, 1–5.                                                                                                                                                        |
| `proxyConfiguration`                                  | Apify datacenter | Set a proxy country to see that market's fares.                                                                                                                                         |

A run checks at most 100 route-dates (after date flex); anything beyond that is skipped and named in one free
`info` row. A departure date in the past returns a free `date-in-past` row and its history is dropped.

### Output

Every field is documented in the dataset schema; Console shows an **Overview** view (one row per route) and an
**Alerts** view. There are four row types:

- `type: "route"` — one per route and date checked successfully (charged as a route check).
- `type: "alert"` — one per alert (charged as a price alert), stored right after its route row.
- `type: "error"` — free; `reason` is `blocked`, `no-flights`, `parse-failed`, `invalid-input` or
  `date-in-past`, and `message` says what happened.
- `type: "info"` — free; e.g. routes skipped over the 100-check cap.

A few things worth knowing about route rows:

- `bestPrice` is the lowest total fare across everything Google listed ("Top flights" and "Other flights").
  `bestItinerary` is that itinerary; `itineraries` holds the top `topN` with prices and CO₂ estimates.
- For **round trips**, Google prices the whole trip but lists outbound flights, so itinerary times, airlines and
  stops describe the outbound flight.
- `departAt` / `arriveAt` are local times at each airport; `durationMin` is door to door across time zones.
- `pointOfSale` is the country sent to Google as the market (your proxy country); `null` means Google chose
  from the proxy's IP.
- `googleFlightsUrl` opens the same search on Google Flights.

### Sample output

Real items from the second platform run of the example input in September 2026 (with the DXB→CAI target raised
to $600 to trigger an alert), plus a real error row for an unknown airport. `itineraries` is shortened to two of nine;
field names and values are otherwise exactly as the Actor returns them.

```json
[
    {
        "type": "route",
        "label": "AMM→LHR",
        "from": "AMM",
        "to": "LHR",
        "departDate": "2026-11-10",
        "returnDate": null,
        "tripType": "one-way",
        "cabin": "economy",
        "passengers": {
            "adults": 1,
            "children": 0,
            "infantsInSeat": 0,
            "infantsOnLap": 0
        },
        "maxStops": null,
        "currency": "USD",
        "pointOfSale": null,
        "checkedAt": "2026-09-26T22:06:43Z",
        "bestPrice": 205,
        "bestItinerary": {
            "airlines": ["BA"],
            "airlineNames": ["British Airways"],
            "flightNumbers": ["BA 401"],
            "departAt": "2026-11-10T09:40",
            "arriveAt": "2026-11-10T12:30",
            "durationMin": 350,
            "stops": 0,
            "layovers": [],
            "aircraft": ["Airbus A320neo"],
            "isBest": true
        },
        "priceLevel": "typical",
        "typicalRange": {
            "low": 200,
            "high": 305
        },
        "resultCount": 9,
        "itineraries": [
            {
                "price": 205,
                "airlines": ["BA"],
                "airlineNames": ["British Airways"],
                "flightNumbers": ["BA 401"],
                "departAt": "2026-11-10T09:40",
                "arriveAt": "2026-11-10T12:30",
                "durationMin": 350,
                "stops": 0,
                "layovers": [],
                "aircraft": ["Airbus A320neo"],
                "co2Grams": 251000,
                "co2TypicalGrams": 298000,
                "isBest": true
            },
            {
                "price": 313,
                "airlines": ["RJ"],
                "airlineNames": ["Royal Jordanian"],
                "flightNumbers": ["RJ 111"],
                "departAt": "2026-11-10T11:55",
                "arriveAt": "2026-11-10T14:20",
                "durationMin": 325,
                "stops": 0,
                "layovers": [],
                "aircraft": ["Boeing 787"],
                "co2Grams": 214000,
                "co2TypicalGrams": 298000,
                "isBest": true
            }
        ],
        "targetPrice": null,
        "previousPrice": 205,
        "previousCheckedAt": "2026-09-26T21:55:45Z",
        "changePct": 0,
        "lowestSeen": 205,
        "lowestSeenAt": "2026-09-26T21:55:45Z",
        "history": [
            {
                "at": "2026-09-26T21:55:45Z",
                "price": 205
            },
            {
                "at": "2026-09-26T22:06:43Z",
                "price": 205
            }
        ],
        "googleFlightsUrl": "https://www.google.com/travel/flights?tfs=GhoSCjIwMjYtMTEtMTBqBRIDQU1NcgUSA0xIUkIBAUgBmAEC&hl=en&curr=USD",
        "source": "google-flights"
    },
    {
        "type": "alert",
        "alertType": "below-target",
        "label": "DXB→CAI",
        "from": "DXB",
        "to": "CAI",
        "departDate": "2026-10-26",
        "returnDate": "2026-11-02",
        "currency": "USD",
        "price": 519,
        "previousPrice": 519,
        "changePct": 0,
        "targetPrice": 600,
        "lowestSeen": 519,
        "bestItinerary": {
            "airlines": ["J9"],
            "airlineNames": ["Jazeera"],
            "flightNumbers": ["J9 124", "J9 733"],
            "departAt": "2026-10-26T16:45",
            "arriveAt": "2026-10-26T22:40",
            "durationMin": 415,
            "stops": 1,
            "layovers": [
                {
                    "airport": "KWI",
                    "minutes": 120
                }
            ],
            "aircraft": ["Airbus A320neo", "Airbus A320"],
            "isBest": true
        },
        "googleFlightsUrl": "https://www.google.com/travel/flights?tfs=GhoSCjIwMjYtMTAtMjZqBRIDRFhCcgUSA0NBSRoaEgoyMDI2LTExLTAyagUSA0NBSXIFEgNEWEJCAgEBSAGYAQE%3D&hl=en&curr=USD",
        "checkedAt": "2026-09-26T22:06:57Z"
    },
    {
        "type": "error",
        "reason": "no-flights",
        "message": "Google found no flights for this route and date. Not charged.",
        "label": "ZZX→LHR",
        "from": "ZZX",
        "to": "LHR",
        "departDate": "2026-10-26",
        "googleFlightsUrl": "https://www.google.com/travel/flights?tfs=GhoSCjIwMjYtMTAtMjZqBRIDWlpYcgUSA0xIUkIBAUgBmAEC&hl=en&curr=USD"
    }
]
```

### Pricing

Pay-per-event; compute and proxy are included.

| Event       | Price  | When                                                                                      |
| ----------- | ------ | ----------------------------------------------------------------------------------------- |
| Actor start | $0.005 | Once per run (per GB of memory; the default 512 MB is one start).                         |
| Route check | $0.015 | Per route and date checked successfully. Blocked, no-flights and invalid routes are free. |
| Price alert | $0.02  | Per alert row, watch mode only. At most one per route per run.                            |

- **The example input**: 2 × $0.015 + $0.005 = **$0.035** per run, plus $0.02 for each alert.
- **Watching 20 routes daily** = 20 × $0.015 + $0.005 = **$0.305 per day**, about **$9.15 for 30 days**,
  plus $0.02 per alert. With `dateFlex: 2` each route becomes 5 checks.
- Snapshot mode never charges for alerts.

If you set a maximum cost per run, the Actor stops cleanly when it is reached: it saves the history first, never
fetches a route it can no longer charge for, and a route that did not fit is left out of the history, so the next
run carries on normally.

### Scheduling tips

- **Daily is enough** for most routes; fares rarely move in a way that matters hour to hour.
- **Use rolling dates** (`"+30d"`, `"+60d"`) for "what does this route cost a month out" watches, and fixed
  dates for a trip you are actually planning. A fixed date's history is dropped once the date has passed.
- **One `trackingId` per schedule**, so two watches never overwrite each other's history.
- **Changing a route starts a new history line.** History is keyed on the route, the dates as you typed them,
  cabin, travellers, stops and currency; a changed target price keeps the history.
- **Set the proxy country to your market to see local fares** (for example `SA` with currency `SAR`).

### Limits, honestly

- **Google Flights only.** One-way and round trip; no multi-city.
- **No booking.** The Actor returns fares and a link to the same search on Google Flights; it does not book or
  return airline booking links.
- **Fares depend on the market.** The same route can price differently by point of sale; set the proxy country
  to your market.
- **Currencies.** Tested to return their own prices: USD, EUR, GBP, SAR, AED, JOD and EGP. Google may ignore a
  currency it does not support and show another one.
- **Blocking.** Google occasionally refuses automated requests. A refused request is retried (through a
  residential proxy when the datacenter one was refused) and never billed; if Google refuses 4 routes in a row,
  the run stops early and reports the rest as free `blocked` rows instead of burning time.
- **Round trips** show outbound flights with the whole-trip price.
- **"Price unavailable".** When Google lists flights but no prices (it happens on some routes and cabins), the
  route returns a free `no-flights` row.
- **No personal data.** The Actor sends a search and reads public fares; there are no names, accounts or
  booking details anywhere in it.

Google and Google Flights are trademarks of Google LLC; this Actor is not affiliated with Google.

### Changelog

- **0.1 (September 2026)** — First release: watch and snapshot modes, rolling dates and date flex, history,
  change since last check, Google's price level and typical range, four alert types.

# Actor input Schema

## `from` (type: `string`):

Departure airport as a 3-letter IATA code, e.g. AMM, DXB, JFK.

## `to` (type: `string`):

Arrival airport as a 3-letter IATA code, e.g. LHR, CAI, LAX.

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

A fixed date (2026-12-15) or a rolling one: "+30d" means 30 days after the day the run starts (UTC). A rolling date keeps one price history even though the actual date moves each day.

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

Same formats as the departure date. Leave empty for one-way. Round-trip fares are the total for the whole trip.

## `label` (type: `string`):

Your name for this route in the output, e.g. "AMM→LHR Christmas". Defaults to FROM→TO.

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

Each entry: { from, to, departDate, returnDate?, label? } plus optional per-route overrides of the defaults below: cabin, adults, children, infantsInSeat, infantsOnLap, maxStops, targetPrice, dateFlex. At most 100 route-date checks run per run, counted after date flex.

## `cabin` (type: `string`):

Cabin class to search.

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

Adult travellers. Fares are the total for all travellers together. Google searches at most 9 travellers.

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

Children (2-11).

## `infantsInSeat` (type: `integer`):

Infants with their own seat.

## `infantsOnLap` (type: `integer`):

Infants on an adult's lap; at most one per adult.

## `maxStops` (type: `string`):

Limit the number of stops. "Any" leaves it to Google.

## `dateFlex` (type: `integer`):

Also check this many days before and after each departure date; the return date moves by the same amount. Each extra date is one more route check (and charge): 2 = 5 checks per route. Dates that would fall before today are skipped.

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

3-letter ISO code for fares, e.g. USD, EUR, GBP, SAR, AED, JOD, EGP.

## `topN` (type: `integer`):

How many itineraries (cheapest first) to include in each route row.

## `mode` (type: `string`):

"watch" remembers every route's fares between runs and adds history, change since the last check and alert rows. "snapshot" returns today's fares only: no history, no alerts.

## `trackingId` (type: `string`):

Name of this watch. History is stored under it in the 'google-flights-watch-state' key-value store; two schedules with different IDs never see each other's history.

## `dropThresholdPct` (type: `number`):

Minimum fall versus the previous check that raises a price-drop alert (and a price-rise alert, when that is on). New lows and target crossings alert regardless.

## `alertOnRise` (type: `boolean`):

Also raise a price-rise alert when a fare rises by at least the threshold.

## `targetPrice` (type: `number`):

Raise a below-target alert when a route's best fare crosses under this total (in the chosen currency, all travellers). A route's own targetPrice wins. It fires again only if the fare later falls a further threshold % while still below.

## `maxConcurrency` (type: `integer`):

Routes checked in parallel. Keep it low: each worker already paces itself, and more parallel requests mostly means more blocked ones.

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

Proxy for the requests to Google. Set the proxy country to your market to see local fares. When a datacenter request is blocked, it is retried once through residential proxy.

## Actor input object example

```json
{
  "from": "AMM",
  "to": "LHR",
  "departDate": "+45d",
  "routes": [
    {
      "from": "DXB",
      "to": "CAI",
      "departDate": "+30d",
      "returnDate": "+37d",
      "adults": 2,
      "targetPrice": 350
    }
  ],
  "cabin": "economy",
  "adults": 1,
  "children": 0,
  "infantsInSeat": 0,
  "infantsOnLap": 0,
  "maxStops": "any",
  "dateFlex": 0,
  "currency": "USD",
  "topN": 10,
  "mode": "watch",
  "trackingId": "default",
  "dropThresholdPct": 10,
  "alertOnRise": false,
  "maxConcurrency": 2,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One row per route checked (best fare, history, change, Google's verdict), plus alert rows and free error rows.

## `alerts` (type: `string`):

This run's alert rows only (price drop, new low, below target, rise if enabled), as a JSON array - empty when nothing alerted. The URL to point Slack, email or webhook integrations at.

## `state` (type: `string`):

Price history of every watched route, one record per tracking ID, in the named 'google-flights-watch-state' key-value store. The next run compares against it.

## `summary` (type: `string`):

Routes checked, alerts, errors by reason, requests and bytes per request, seconds per check, and the charged-event tally.

# 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 = {
    "from": "AMM",
    "to": "LHR",
    "departDate": "+45d",
    "routes": [
        {
            "from": "DXB",
            "to": "CAI",
            "departDate": "+30d",
            "returnDate": "+37d",
            "adults": 2,
            "targetPrice": 350
        }
    ],
    "currency": "USD",
    "mode": "watch",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("almoutasem_nabil/google-flights-price-watch").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 = {
    "from": "AMM",
    "to": "LHR",
    "departDate": "+45d",
    "routes": [{
            "from": "DXB",
            "to": "CAI",
            "departDate": "+30d",
            "returnDate": "+37d",
            "adults": 2,
            "targetPrice": 350,
        }],
    "currency": "USD",
    "mode": "watch",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("almoutasem_nabil/google-flights-price-watch").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 '{
  "from": "AMM",
  "to": "LHR",
  "departDate": "+45d",
  "routes": [
    {
      "from": "DXB",
      "to": "CAI",
      "departDate": "+30d",
      "returnDate": "+37d",
      "adults": 2,
      "targetPrice": 350
    }
  ],
  "currency": "USD",
  "mode": "watch",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call almoutasem_nabil/google-flights-price-watch --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,almoutasem_nabil/google-flights-price-watch"
        }
    }
}
```

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/EOQlXLSdeY4f4Dnda/builds/Znn1vwzVy10NyJh8c/openapi.json
