{
  "openapi": "3.0.1",
  "info": {
    "title": "Travel Budget Planner",
    "description": "Plan any trip: aggregates whole-market Airbnb & Booking.com nightly-price stats (median & average over the full market, via a price-histogram Actor) plus flights, buses and trains, in your chosen currency, and writes a formatted itinerary into your Notion via an MCP Connector.",
    "version": "0.8",
    "x-build-id": "0fdujYOJFTW7BMmjG"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/inovaflow~travel-planner/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-inovaflow-travel-planner",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/inovaflow~travel-planner/runs": {
      "post": {
        "operationId": "runs-sync-inovaflow-travel-planner",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/inovaflow~travel-planner/run-sync": {
      "post": {
        "operationId": "run-sync-inovaflow-travel-planner",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "required": [
          "origin",
          "destination",
          "checkIn",
          "checkOut"
        ],
        "properties": {
          "origin": {
            "title": "Origin (where you start)",
            "type": "string",
            "description": "The city you are travelling FROM. Type a city name or an airport/IATA code — e.g. \"Berlin\" or \"BER\". Used to find flights, buses, and trains. Required."
          },
          "destination": {
            "title": "Destination (where you're going)",
            "type": "string",
            "description": "The city you are travelling TO. Type a city name or an airport/IATA code — e.g. \"Lisbon\" or \"LIS\". Tip: for a common city name you can add the country (\"Lisbon, Portugal\") to be sure it finds the right place, but it isn't required. Required."
          },
          "checkIn": {
            "title": "Check-in / departure date",
            "type": "string",
            "description": "The first night of your stay and the day you travel out. Pick a date that is today or later (past dates can't be priced) — e.g. 2026-07-01. Required."
          },
          "checkOut": {
            "title": "Check-out / return date",
            "type": "string",
            "description": "The last morning of your stay and the day you travel back. Must be after the check-in date, and the whole stay can be at most 90 nights — e.g. 2026-07-04. Required."
          },
          "travellers": {
            "title": "Travellers",
            "minimum": 1,
            "maximum": 16,
            "type": "integer",
            "description": "How many people are travelling. One number for the whole trip — it sizes your place to stay and prices flights and buses & trains for the same party. Any whole number from 1 to 16. Example: 2. Optional — defaults to 1.",
            "default": 1
          },
          "currency": {
            "title": "Currency for all prices",
            "enum": [
              "EUR",
              "USD",
              "GBP",
              "CHF",
              "PLN",
              "CZK",
              "AUD",
              "BRL",
              "CAD",
              "CNY",
              "DKK",
              "HUF",
              "INR",
              "JPY",
              "KRW",
              "MXN",
              "NOK",
              "SEK",
              "TRY"
            ],
            "type": "string",
            "description": "The currency every price is requested and shown in. No conversion is done — each source is asked directly for prices in this currency. Example: EUR. Optional — defaults to Euro (EUR). (The optional Rome2Rio source is the one exception: its rough prices may come back in their own currency, often USD.)",
            "default": "EUR"
          },
          "tripType": {
            "title": "Trip type",
            "enum": [
              "round_trip",
              "one_way"
            ],
            "type": "string",
            "description": "Whether to price a round-trip or a one-way journey. 'Round trip' (the default) prices flights out AND back using your two dates. 'One way' prices the outbound flight only — useful if you're not returning, or booking the return separately. This affects flight pricing only; your accommodation is always priced for the nights between check-in and check-out. Optional — defaults to round trip.",
            "default": "round_trip"
          },
          "smartTransport": {
            "title": "Smart transport (skip modes that don't make sense)",
            "type": "boolean",
            "description": "When on (the default), the planner uses the driving distance between your origin and destination to skip transport that doesn't make sense — it won't suggest flights for a short hop (under ~2 hours' drive) and won't suggest driving for a very long haul (over ~6 hours' drive), and it explains why on your trip page. Turn off to always show every mode regardless of distance. The explanation is shown either way. Applies to the whole trip (flights and buses & trains). Optional — defaults to on.",
            "default": true
          },
          "accommodationSources": {
            "title": "Search for a place to stay",
            "type": "array",
            "description": "Which accommodation platforms to search for real places to stay (with links). Both are on by default. 'Airbnb' is the cheapest, fastest source; 'Booking.com' adds hotels & apartments but is slower & costlier because it runs a real browser to clear anti-bot checks (adds a few cents per run). Turn Booking off for a faster, cheaper Airbnb-only search; clear both to skip accommodation entirely and plan travel only. Optional — defaults to Airbnb + Booking.com.",
            "items": {
              "type": "string",
              "enum": [
                "airbnb",
                "booking"
              ],
              "enumTitles": [
                "Airbnb — real listings with links",
                "Booking.com — hotels & apartments (slower & costlier: uses a browser)"
              ]
            },
            "default": [
              "airbnb",
              "booking"
            ]
          },
          "minHotelStars": {
            "title": "Minimum hotel star rating",
            "minimum": 1,
            "maximum": 5,
            "type": "integer",
            "description": "Only request accommodation rated at least this many stars from the listing scrapers (Airbnb / Booking.com). Any whole number from 1 to 5. Example: 4. Optional — leave blank for any rating."
          },
          "hotelAmenities": {
            "title": "Required amenities",
            "type": "array",
            "description": "Only request accommodation that has the selected amenities (type to search). Sent to the listing scrapers (Airbnb / Booking.com). Optional — leave empty to not filter on amenities.",
            "items": {
              "type": "string",
              "enum": [
                "wifi",
                "pool",
                "gym",
                "parking",
                "breakfast",
                "air_conditioning",
                "kitchen",
                "washer",
                "hot_tub",
                "balcony",
                "sea_view",
                "elevator",
                "accessible",
                "workspace",
                "ev_charger",
                "pet_friendly"
              ],
              "enumTitles": [
                "Wi-Fi",
                "Pool",
                "Gym",
                "Parking",
                "Breakfast",
                "Air conditioning",
                "Kitchen",
                "Washer / laundry",
                "Hot tub",
                "Balcony / terrace",
                "Sea view",
                "Elevator / lift",
                "Wheelchair accessible",
                "Dedicated workspace",
                "EV charger",
                "Pet friendly"
              ]
            }
          },
          "refundableOnly": {
            "title": "Free cancellation / refundable only",
            "type": "boolean",
            "description": "When on, ask the hotel and flight sources for free-cancellation stays and refundable fares only. Sent to the underlying scrapers as a search filter. Optional — defaults to off.",
            "default": false
          },
          "stayMinPrice": {
            "title": "Minimum price per night",
            "minimum": 0,
            "type": "integer",
            "description": "Only show places to stay at or above this nightly price, in your chosen currency. Example: 40. Optional — leave blank for no lower bound."
          },
          "stayMaxPrice": {
            "title": "Maximum price per night (nightly budget)",
            "minimum": 0,
            "type": "integer",
            "description": "Only show places to stay at or below this nightly price, in your chosen currency — your nightly budget. Must be greater than or equal to the minimum if both are set. Example: 150. Optional — leave blank for no upper bound."
          },
          "priceComparison": {
            "title": "Add a price comparison (real market average)",
            "type": "boolean",
            "description": "Off by default. Turn it on and a separate price-index actor scans the whole market for your dates to find the real average nightly price — a benchmark for how good your listings are. Because that scan has to load the live booking sites in full (Booking.com needs a real browser to clear its bot-check), turning it on can cost about as much as your whole trip search. Leave off to skip it.",
            "default": false
          },
          "includeFlights": {
            "title": "Search for flights",
            "type": "boolean",
            "description": "Search for the cheapest flights from a 1000+ airline aggregator (Skyscanner). Turn off to skip flights entirely (e.g. a short hop you'd rather drive or take the bus for). Optional — defaults to on.",
            "default": true
          },
          "flightSources": {
            "title": "Flight sources",
            "type": "array",
            "description": "Which flight engine(s) to search — only used when \"Search for flights\" is on. 'Skyscanner' aggregates 1000+ airlines (cheapest fares; rows link to the route search). 'Kiwi.com' is our own built-in search and returns a direct, bookable link for each specific flight (and finds self-transfer combos Skyscanner may miss). Pick either or both — picking both merges and ranks their results together. Optional — defaults to Skyscanner.",
            "items": {
              "type": "string",
              "enum": [
                "skyscanner",
                "kiwi"
              ],
              "enumTitles": [
                "Skyscanner — 1000+ airline aggregator",
                "Kiwi.com — built-in, with direct booking links"
              ]
            },
            "default": [
              "skyscanner"
            ]
          },
          "flightClass": {
            "title": "Cabin class",
            "enum": [
              "economy",
              "premium_economy",
              "business",
              "first"
            ],
            "type": "string",
            "description": "Which cabin to price flights in. Sent to the flight search, so fares come back for the chosen class. Optional — defaults to Economy.",
            "default": "economy"
          },
          "directOnly": {
            "title": "Direct flights only",
            "type": "boolean",
            "description": "When on, keep only non-stop flights and drop any flight with a layover. Affects flights only — buses and trains are unchanged. (For a softer limit, use \"Max stops (flights)\" below.) A flight whose stop count isn't reported by the source is kept (we can't confirm it has a stop). Optional — defaults to off.",
            "default": false
          },
          "flightMaxStops": {
            "title": "Max stops (flights)",
            "minimum": 0,
            "maximum": 3,
            "type": "integer",
            "description": "Only keep flights with at most this many stops/layovers. Set 0 for non-stop only. A flight whose stop count isn't reported is kept. Any whole number from 0 to 3. Optional — leave blank for no limit. (Buses & trains have their own \"Max transfers\" filter in their section.)"
          },
          "flexibleDates": {
            "title": "Flexible dates (± days)",
            "minimum": 0,
            "maximum": 7,
            "type": "integer",
            "description": "Search this many days either side of your check-in / departure date for a cheaper nearby flight. Any whole number from 0 to 7 (0 = exact dates only). A wider window can surface bigger savings; the flight search stays budget-capped either way, so it won't run away on cost. Example: 3. Optional — defaults to 0 (exact dates).",
            "default": 0
          },
          "preferredAirlines": {
            "title": "Preferred airlines",
            "type": "array",
            "description": "Keep only flights operated by the airlines you pick from the list (type to search). Matched by name, so e.g. \"TAP Air Portugal\" also catches a fare just labelled \"TAP\". Affects flights only; buses and trains are unchanged. Leave empty to allow any airline. Optional.",
            "items": {
              "type": "string",
              "enum": [
                "Ryanair",
                "easyJet",
                "Wizz",
                "Vueling",
                "Transavia",
                "Eurowings",
                "Norwegian",
                "Pegasus",
                "Lufthansa",
                "Air France",
                "KLM",
                "British Airways",
                "Iberia",
                "TAP",
                "SWISS",
                "Austrian",
                "Brussels",
                "SAS",
                "Finnair",
                "Aer Lingus",
                "Air Europa",
                "ITA Airways",
                "Aegean",
                "LOT Polish",
                "TAROM",
                "Croatia",
                "Turkish",
                "Emirates",
                "Qatar",
                "Etihad",
                "Singapore",
                "Qantas",
                "United",
                "Delta",
                "American",
                "Air Canada",
                "JetBlue"
              ],
              "enumTitles": [
                "Ryanair",
                "easyJet",
                "Wizz Air",
                "Vueling",
                "Transavia",
                "Eurowings",
                "Norwegian",
                "Pegasus Airlines",
                "Lufthansa",
                "Air France",
                "KLM",
                "British Airways",
                "Iberia",
                "TAP Air Portugal",
                "SWISS",
                "Austrian Airlines",
                "Brussels Airlines",
                "SAS (Scandinavian)",
                "Finnair",
                "Aer Lingus",
                "Air Europa",
                "ITA Airways",
                "Aegean Airlines",
                "LOT Polish Airlines",
                "TAROM",
                "Croatia Airlines",
                "Turkish Airlines",
                "Emirates",
                "Qatar Airways",
                "Etihad Airways",
                "Singapore Airlines",
                "Qantas",
                "United Airlines",
                "Delta Air Lines",
                "American Airlines",
                "Air Canada",
                "JetBlue"
              ]
            }
          },
          "flightMinPrice": {
            "title": "Minimum flight price per person",
            "minimum": 0,
            "type": "integer",
            "description": "Drop flights whose per-person fare is below this amount, in your chosen currency. The fare is for the whole party, so it's compared per person (fare ÷ travellers). Example: 50. Optional — leave blank for no lower bound."
          },
          "flightMaxPrice": {
            "title": "Maximum flight price per person (flight budget)",
            "minimum": 0,
            "type": "integer",
            "description": "Drop flights whose per-person fare is above this amount, in your chosen currency — your flight budget. Compared per person (fare ÷ travellers). Must be greater than or equal to the minimum if both are set. Example: 300. Optional — leave blank for no upper bound."
          },
          "includeGroundTransport": {
            "title": "Search for buses & trains",
            "type": "boolean",
            "description": "Turn on to price ground transport for your route, then choose providers below. Off by default. Leave off to skip buses & trains entirely. Optional — defaults to off.",
            "default": false
          },
          "groundTransportSources": {
            "title": "Bus & train providers",
            "type": "array",
            "description": "Which ground-transport providers to search — only used when \"Search for buses & trains\" above is on. 'Buses + FlixTrain rail' = bookable FlixBus/FlixTrain fares with departure times and booking links. 'Deutsche Bahn' = real DB train tickets & connections across Germany and Europe (ICE/EC/IC/regional, incl. cross-border) with times and transfers — best for German and cross-border rail; returns nothing for routes the DB network doesn't cover. 'Rome2Rio' = rough indicative prices only (often in USD, no departure times) — a 'does this route exist and roughly what does it cost' signal, not bookable fares. Pick any combination. Optional — defaults to Buses + FlixTrain rail.",
            "items": {
              "type": "string",
              "enum": [
                "bus",
                "db",
                "rome2rio"
              ],
              "enumTitles": [
                "Buses + FlixTrain rail — bookable FlixBus/FlixTrain fares with times",
                "Deutsche Bahn — real German & European train tickets with times",
                "Rome2Rio — rough indicative prices only (often USD, no times)"
              ]
            },
            "default": [
              "bus"
            ]
          },
          "onroadMaxTransfers": {
            "title": "Max transfers (buses & trains)",
            "minimum": 0,
            "maximum": 3,
            "type": "integer",
            "description": "Only keep bus/train options with at most this many transfers. Set 0 for direct only. An option whose transfer count isn't reported is kept. Any whole number from 0 to 3. Optional — leave blank for no limit."
          },
          "onroadMinPrice": {
            "title": "Minimum bus/train price per person",
            "minimum": 0,
            "type": "integer",
            "description": "Drop bus/train options whose per-person fare is below this amount, in your chosen currency. Compared per person (fare ÷ travellers). Example: 10. Optional — leave blank for no lower bound."
          },
          "onroadMaxPrice": {
            "title": "Maximum bus/train price per person",
            "minimum": 0,
            "type": "integer",
            "description": "Drop bus/train options whose per-person fare is above this amount, in your chosen currency. Compared per person (fare ÷ travellers). Must be greater than or equal to the minimum if both are set. Example: 120. Optional — leave blank for no upper bound."
          },
          "outputEmail": {
            "title": "Your email address",
            "type": "string",
            "description": "Type an email address and the finished plan is sent there when the run completes — a formatted summary in the email body, with the full Markdown itinerary (the same content as the Notion page) attached as itinerary.md. Sent from travel-planner@apify.inovaflow.app (no-reply). Leave blank to skip the email. Example: me@example.com."
          },
          "notionMcpConnector": {
            "title": "Or send it to Notion",
            "type": "string",
            "description": "Your authorized Notion MCP connector — a one-time saved link from Apify to your Notion. The Actor writes the trip through it and never sees your Notion login. How to create it (one time): 1) In the Apify Console go to Settings → Integrations → MCP Connectors and add a connector. 2) Set the MCP server URL to https://mcp.notion.com/mcp. 3) Sign in to Notion and choose which pages/workspace it may access; make sure it can create pages (and ideally update pages, so re-running a trip refreshes the same page). 4) In Notion, open the page you'll keep trips under, click ••• (top-right) → Connections → add that same integration so it's allowed to write there. Then pick the connector here. Leave blank to skip the Notion write entirely."
          },
          "notionParentPageId": {
            "title": "Notion parent page (optional)",
            "type": "string",
            "description": "Where in Notion the new trip page should be created. Paste the 32-character page ID found at the end of any Notion page's URL — e.g. for https://www.notion.so/My-Trips-1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d the ID is 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d. The page must be one your Notion connector above can access (the same page you shared with the integration). Example: 1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d. Optional — leave blank and Notion will create a standalone page in the authorized workspace instead."
          },
          "apifyMcpConnector": {
            "title": "Run the scrapers under a different Apify account (connector)",
            "type": "string",
            "description": "An Apify MCP connector whose account runs and pays for the source scrapers — you authorize once, with no token to paste. Create it in the Apify Console under Settings → Integrations → MCP Connectors with the server URL https://mcp.apify.com, then pick it here. Takes precedence over the API token below if both are set. Optional — leave blank to run on the account executing this Actor."
          },
          "apifyToken": {
            "title": "…or paste an Apify API token",
            "type": "string",
            "description": "An Apify API token — the no-connector alternative to the connector option above. When set (and no connector is given), every paid source scraper (and the read of its results) runs on — and is billed to — THIS token's account instead of the account running the planner. Find or create a token in the Apify Console under Settings → API & Integrations. Treat it like a password. Optional — leave blank to run everything on the account executing this Actor."
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}