{
  "openapi": "3.1.0",
  "info": {
    "title": "FlightPowers Travel Data API",
    "version": "1.1.0",
    "summary": "Real-time Google Flights fares and Booking.com hotel prices as clean JSON.",
    "description": "Live flight and hotel prices, and the context to judge them. Two data products behind one host:\n\n- **Flights**: live Google Flights searches. Every itinerary carries Google's own historical price band (`price_insights_low`/`price_insights_high`) and a `low | typical | high` verdict, plus a booking deep link.\n- **Hotels**: live Booking.com searches, including `proxy_country` (query from any market's viewpoint via residential proxies - the basis for rate-parity and geo-pricing monitoring).\n\n**Auth**: bring your own RapidAPI key; every request is billed to your own subscription. Subscribe at https://rapidapi.com/mtnrabi/api/google-flights-live-api/pricing (flights) or https://rapidapi.com/mtnrabi/api/booking-live-api/pricing (hotels). They are separate subscriptions.\n\n**Honesty contract**: flight searches report their own outcome in `X-Search-Status` (`ok | empty | partial | degraded`), so an empty array is never ambiguous. No uptime or latency figures are published; a public status probe runs every 15 minutes at https://github.com/mtnrabi/travel-agent-skills.\n\nThe same data is available as hosted MCP servers (https://flightpowers.com/mcp), an n8n community node (`n8n-nodes-flightpowers`), Apify actors, and 8 open-source agent skills.",
    "contact": {
      "name": "FlightPowers",
      "url": "https://flightpowers.com/contact",
      "email": "mtnrabi@gmail.com"
    },
    "termsOfService": "https://flightpowers.com/terms"
  },
  "externalDocs": {
    "description": "Human docs with captured example responses",
    "url": "https://flightpowers.com/flights-api"
  },
  "servers": [
    {
      "url": "https://api.flightpowers.com",
      "description": "Production. The RapidAPI hosts (google-flights-live-api.p.rapidapi.com, booking-live-api.p.rapidapi.com) serve the same engines with RapidAPI-native paths; this host is the canonical REST front."
    }
  ],
  "security": [
    {
      "ApiKeyHeader": []
    },
    {
      "RapidApiKeyHeader": []
    },
    {
      "BearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Flights",
      "description": "Live Google Flights searches with price-insight context."
    },
    {
      "name": "Hotels",
      "description": "Live Booking.com searches with per-market pricing via proxy_country."
    },
    {
      "name": "Account",
      "description": "Key verification."
    }
  ],
  "paths": {
    "/v1/verify": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "verifyKey",
        "summary": "Check that an API key works, as cheaply as possible",
        "description": "Validates the caller's RapidAPI key against the cheapest possible upstream call. A key subscribed to either API is valid. Built for platform \"Test connection\" buttons.",
        "responses": {
          "200": {
            "description": "The key is valid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifyResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, or unsubscribed key.",
            "content": {
              "application/json": {
                "schema": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "$ref": "#/components/schemas/VerifyResponse"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/flights/oneway": {
      "post": {
        "tags": [
          "Flights"
        ],
        "operationId": "searchOnewayFlights",
        "summary": "Search one-way flights (live Google Flights results)",
        "description": "Returns live Google Flights itineraries for a route and date. Every result carries Google's historical price band (`price_insights_low`/`price_insights_high`) and a `low | typical | high` verdict. Read `X-Search-Status` before interpreting an empty array: `empty` means Google genuinely has no itineraries, `degraded` means the search did not complete and should be retried (or send `\"strict\": true` to get an HTTP 503 instead). Responses also carry your RapidAPI plan quota as `x-plan-*` headers (forwarded from the upstream billing layer), so you can watch usage without a dashboard.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OnewayRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A JSON array of itineraries, cheapest patterns first. May be empty: check `X-Search-Status`.",
            "headers": {
              "X-Search-Status": {
                "description": "How the search went: `ok` (complete results), `empty` (search completed and Google genuinely has no itineraries; the empty array IS the answer), `partial` (results present but part of the search failed), `degraded` (the search did not complete; an empty array says nothing about availability - retry it).",
                "schema": {
                  "type": "string",
                  "enum": [
                    "ok",
                    "empty",
                    "partial",
                    "degraded"
                  ]
                }
              },
              "X-Search-Reason": {
                "description": "Why the search went wrong (e.g. `blocked_page`, `upstream_timeout`, `upstream_status`). Present only when something did.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Search-Results": {
                "description": "Number of itineraries in the body.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Search-Attempts": {
                "description": "How much work the search did.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Search-Combinations": {
                "description": "Route/date combinations attempted.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Search-Retries": {
                "description": "Retries performed for pages that failed to read.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OnewayFlight"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request body is not a JSON object, or a field failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key supplied, or the key is malformed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is not subscribed to the Google Flights Live API on RapidAPI.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Your RapidAPI plan quota or rate limit is exhausted. Retrying spends more quota; upgrade instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Only with `\"strict\": true`: the search did not complete (`search_incomplete`). Safe to retry; nothing was booked or changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/flights/roundtrip": {
      "post": {
        "tags": [
          "Flights"
        ],
        "operationId": "searchRoundtripFlights",
        "summary": "Search round-trip itineraries (paired legs, priced together)",
        "description": "Returns paired round-trip itineraries: each result is one outbound + return combination priced together, the way Google actually sells them. Every result carries Google's historical price band (`price_insights_low`/`price_insights_high`) and a `low | typical | high` verdict. Read `X-Search-Status` before interpreting an empty array: `empty` means Google genuinely has no itineraries, `degraded` means the search did not complete and should be retried (or send `\"strict\": true` to get an HTTP 503 instead). Responses also carry your RapidAPI plan quota as `x-plan-*` headers (forwarded from the upstream billing layer), so you can watch usage without a dashboard.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RoundtripRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A JSON array of itineraries, cheapest patterns first. May be empty: check `X-Search-Status`.",
            "headers": {
              "X-Search-Status": {
                "description": "How the search went: `ok` (complete results), `empty` (search completed and Google genuinely has no itineraries; the empty array IS the answer), `partial` (results present but part of the search failed), `degraded` (the search did not complete; an empty array says nothing about availability - retry it).",
                "schema": {
                  "type": "string",
                  "enum": [
                    "ok",
                    "empty",
                    "partial",
                    "degraded"
                  ]
                }
              },
              "X-Search-Reason": {
                "description": "Why the search went wrong (e.g. `blocked_page`, `upstream_timeout`, `upstream_status`). Present only when something did.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Search-Results": {
                "description": "Number of itineraries in the body.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Search-Attempts": {
                "description": "How much work the search did.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Search-Combinations": {
                "description": "Route/date combinations attempted.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Search-Retries": {
                "description": "Retries performed for pages that failed to read.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RoundtripItinerary"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request body is not a JSON object, or a field failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key supplied, or the key is malformed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is not subscribed to the Google Flights Live API on RapidAPI.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Your RapidAPI plan quota or rate limit is exhausted. Retrying spends more quota; upgrade instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Only with `\"strict\": true`: the search did not complete (`search_incomplete`). Safe to retry; nothing was booked or changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/hotels/search": {
      "post": {
        "tags": [
          "Hotels"
        ],
        "operationId": "searchHotels",
        "summary": "Search hotels at a destination",
        "description": "Live Booking.com search for a free-text destination. Supports 24 filters, a per-night budget, and `proxy_country` for market-specific pricing. The destination field is `destination` (not `location`). Responses also carry your RapidAPI plan quota as `x-plan-*` headers (forwarded from the upstream billing layer), so you can watch usage without a dashboard.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HotelSearchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Properties at the destination with total-stay prices, review scores, and booking deep links.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelSearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request body is not a JSON object, or a field failed validation (e.g. sending `location` instead of `destination`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key supplied, or the key is malformed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is not subscribed to the Booking Live API on RapidAPI.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Your RapidAPI plan quota or rate limit is exhausted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/hotels/by-name": {
      "post": {
        "tags": [
          "Hotels"
        ],
        "operationId": "findHotelByName",
        "summary": "Look up one hotel by name: availability + price in one call",
        "description": "Searches Booking.com for the named hotel, verifies the name matches, and returns its availability and pricing. Use `area` to disambiguate generic names. With `proxy_country`, this is the rate-parity primitive: the same hotel, priced from different markets. Responses also carry your RapidAPI plan quota as `x-plan-*` headers (forwarded from the upstream billing layer), so you can watch usage without a dashboard.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HotelByNameRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The hotel's availability and price for the stay. `available: false` means no rooms for those dates.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelByNameResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request body is not a JSON object, or a field failed validation (e.g. sending `location` instead of `destination`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key supplied, or the key is malformed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is not subscribed to the Booking Live API on RapidAPI.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Your RapidAPI plan quota or rate limit is exhausted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/hotels/rooms": {
      "post": {
        "tags": [
          "Hotels"
        ],
        "operationId": "getHotelRooms",
        "summary": "Room-level availability and pricing for one hotel",
        "description": "Every bookable room type for a specific hotel (by its Booking.com id), with per-room prices and board. Responses also carry your RapidAPI plan quota as `x-plan-*` headers (forwarded from the upstream billing layer), so you can watch usage without a dashboard.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HotelRoomsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Room inventory with prices and a pre-filled booking URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HotelRoomsResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request body is not a JSON object, or a field failed validation (e.g. sending `location` instead of `destination`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key supplied, or the key is malformed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is not subscribed to the Booking Live API on RapidAPI.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Your RapidAPI plan quota or rate limit is exhausted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/hotels/resolve": {
      "post": {
        "tags": [
          "Hotels"
        ],
        "operationId": "resolveHotelId",
        "summary": "Resolve a hotel name to its Booking.com id",
        "description": "Turns a free-text hotel name into the `hotel_booking_id` that `/v1/hotels/rooms` takes. Responses also carry your RapidAPI plan quota as `x-plan-*` headers (forwarded from the upstream billing layer), so you can watch usage without a dashboard.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolveRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The matched hotel and its id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResolveResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request body is not a JSON object, or a field failed validation (e.g. sending `location` instead of `destination`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No API key supplied, or the key is malformed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is not subscribed to the Booking Live API on RapidAPI.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Your RapidAPI plan quota or rate limit is exhausted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Your RapidAPI key. The default for Zapier-style custom auth."
      },
      "RapidApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "x-rapidapi-key",
        "description": "Your RapidAPI key, in the header RapidAPI users already have in hand."
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "`Authorization: Bearer <RapidAPI key>`. `?api_key=` in the query string also works but is discouraged: keys in URLs end up in logs and history."
      }
    },
    "schemas": {
      "OnewayRequest": {
        "type": "object",
        "required": [
          "from_airport",
          "to_airport",
          "departure_date"
        ],
        "properties": {
          "from_airport": {
            "type": "string",
            "description": "Origin IATA code, e.g. `JFK`.",
            "examples": [
              "JFK"
            ]
          },
          "to_airport": {
            "type": "string",
            "description": "Destination IATA code, e.g. `LHR`.",
            "examples": [
              "LHR"
            ]
          },
          "departure_date": {
            "type": "string",
            "format": "date",
            "description": "YYYY-MM-DD.",
            "examples": [
              "2026-10-15"
            ]
          },
          "max_stops": {
            "type": "integer",
            "description": "Maximum stops, e.g. 0 for non-stop only."
          },
          "airline_codes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Only include these airlines (IATA codes)."
          },
          "exclude_airline_codes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Exclude these airlines (IATA codes)."
          },
          "departure_time_min": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Earliest departure hour (0-23)."
          },
          "departure_time_max": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Latest departure hour (0-23)."
          },
          "departure_arrival_time_min": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Earliest arrival hour (0-23)."
          },
          "departure_arrival_time_max": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Latest arrival hour (0-23)."
          },
          "sort_type": {
            "type": "string",
            "enum": [
              "Overall",
              "Price",
              "Duration"
            ],
            "default": "Overall",
            "description": "Result ordering."
          },
          "currency": {
            "type": "string",
            "default": "USD",
            "description": "ISO currency code for prices, e.g. `USD`, `EUR`."
          },
          "max_price": {
            "type": "integer",
            "description": "Only return itineraries at or below this price."
          },
          "seat_type": {
            "type": "integer",
            "enum": [
              1,
              3
            ],
            "description": "Cabin: 1 = Economy, 3 = Business."
          },
          "passengers": {
            "type": "array",
            "items": {
              "type": "integer",
              "enum": [
                1,
                2,
                3,
                4
              ]
            },
            "description": "One integer per passenger: 1 = adult, 2 = child, 3 = infant on lap, 4 = infant in seat. Default: one adult."
          },
          "limit": {
            "type": "integer",
            "default": 10,
            "description": "Maximum number of results to return."
          },
          "use_fallback": {
            "type": "boolean",
            "description": "A secondary flight-data source the API can escalate to when the live Google Flights search does not come back with results. Last resort, not part of the normal path: a search that succeeds never touches it. Send `false` to switch the escalation off for a request. Responses that do use it can take longer."
          },
          "strict": {
            "type": "boolean",
            "default": false,
            "description": "Opt-in. Return HTTP 503 with a `search_incomplete` error body instead of an empty array when the search did not complete (X-Search-Status `degraded`)."
          },
          "use_ext_proxy": {
            "type": "boolean",
            "default": true,
            "description": "When true, requests may be routed through an external residential proxy to reduce blocks from Google. Set false to force a direct request (lower latency, more block risk)."
          }
        }
      },
      "RoundtripRequest": {
        "type": "object",
        "required": [
          "from_airport",
          "to_airport",
          "departure_date",
          "return_date"
        ],
        "properties": {
          "from_airport": {
            "type": "string",
            "description": "Origin IATA code.",
            "examples": [
              "JFK"
            ]
          },
          "to_airport": {
            "type": "string",
            "description": "Destination IATA code.",
            "examples": [
              "LHR"
            ]
          },
          "departure_date": {
            "type": "string",
            "format": "date",
            "description": "Outbound date, YYYY-MM-DD."
          },
          "return_date": {
            "type": "string",
            "format": "date",
            "description": "Return date, YYYY-MM-DD."
          },
          "max_departure_stops": {
            "type": "integer",
            "description": "Max stops on the outbound leg."
          },
          "max_return_stops": {
            "type": "integer",
            "description": "Max stops on the return leg."
          },
          "departure_airline_codes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Only these airlines on the outbound leg."
          },
          "return_airline_codes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Only these airlines on the return leg."
          },
          "departure_exclude_airline_codes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Exclude these airlines on the outbound leg."
          },
          "return_exclude_airline_codes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Exclude these airlines on the return leg."
          },
          "departure_departure_time_min": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Outbound leg: earliest departure hour."
          },
          "departure_departure_time_max": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Outbound leg: latest departure hour."
          },
          "departure_arrival_time_min": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Outbound leg: earliest arrival hour."
          },
          "departure_arrival_time_max": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Outbound leg: latest arrival hour."
          },
          "return_departure_time_min": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Return leg: earliest departure hour."
          },
          "return_departure_time_max": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Return leg: latest departure hour."
          },
          "return_arrival_time_min": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Return leg: earliest arrival hour."
          },
          "return_arrival_time_max": {
            "type": "integer",
            "minimum": 0,
            "maximum": 23,
            "description": "Return leg: latest arrival hour."
          },
          "sort_type": {
            "type": "string",
            "enum": [
              "Overall",
              "Price",
              "Duration"
            ],
            "default": "Overall",
            "description": "Result ordering."
          },
          "currency": {
            "type": "string",
            "default": "USD",
            "description": "ISO currency code for prices, e.g. `USD`, `EUR`."
          },
          "max_price": {
            "type": "integer",
            "description": "Only return itineraries at or below this price."
          },
          "seat_type": {
            "type": "integer",
            "enum": [
              1,
              3
            ],
            "description": "Cabin: 1 = Economy, 3 = Business."
          },
          "passengers": {
            "type": "array",
            "items": {
              "type": "integer",
              "enum": [
                1,
                2,
                3,
                4
              ]
            },
            "description": "One integer per passenger: 1 = adult, 2 = child, 3 = infant on lap, 4 = infant in seat. Default: one adult."
          },
          "limit": {
            "type": "integer",
            "default": 10,
            "description": "Maximum number of results to return."
          },
          "use_fallback": {
            "type": "boolean",
            "description": "A secondary flight-data source the API can escalate to when the live Google Flights search does not come back with results. Last resort, not part of the normal path: a search that succeeds never touches it. Send `false` to switch the escalation off for a request. Responses that do use it can take longer."
          },
          "strict": {
            "type": "boolean",
            "default": false,
            "description": "Opt-in. Return HTTP 503 with a `search_incomplete` error body instead of an empty array when the search did not complete (X-Search-Status `degraded`)."
          },
          "use_ext_proxy": {
            "type": "boolean",
            "default": true,
            "description": "When true, requests may be routed through an external residential proxy to reduce blocks from Google. Set false to force a direct request (lower latency, more block risk)."
          }
        }
      },
      "OnewayFlight": {
        "type": "object",
        "description": "One itinerary from a one-way search. Flat JSON: no nested envelopes to unwrap.",
        "properties": {
          "price_range_in_relation_to_other_periods": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "low",
              "typical",
              "high",
              null
            ],
            "description": "Google's verdict on this fare vs the route's historical range. The field rival Google Flights APIs generally do not return."
          },
          "price_insights_low": {
            "type": [
              "number",
              "null"
            ],
            "description": "Bottom of Google's historical price range for this route and dates."
          },
          "price_insights_high": {
            "type": [
              "number",
              "null"
            ],
            "description": "Top of Google's historical price range for this route and dates."
          },
          "from_airport": {
            "type": "string"
          },
          "to_airport": {
            "type": "string"
          },
          "departure_date": {
            "type": "string",
            "format": "date"
          },
          "price": {
            "type": "string",
            "description": "Display price, e.g. `$433`."
          },
          "price_as_number": {
            "type": "number",
            "description": "Numeric price for sorting and comparison."
          },
          "duration": {
            "type": "string",
            "description": "Human-readable duration."
          },
          "duration_seconds": {
            "type": "integer"
          },
          "buy_link": {
            "type": "string",
            "format": "uri",
            "description": "Deep link to book this exact itinerary on Google Flights."
          },
          "airline": {
            "type": "string"
          },
          "stops": {
            "type": "integer"
          },
          "stops_info": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "stop_airport": {
                  "type": "string"
                },
                "stop_duration_seconds": {
                  "type": "integer"
                }
              }
            }
          },
          "departure_description": {
            "type": "string",
            "description": "e.g. `8:35 PM on Thu, Oct 15`."
          },
          "arrival_description": {
            "type": "string"
          }
        }
      },
      "RoundtripItinerary": {
        "type": "object",
        "description": "One PAIRED round-trip itinerary: outbound and return legs priced together, the way Google actually sells them - not two stapled one-way searches.",
        "properties": {
          "price_range_in_relation_to_other_periods": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "low",
              "typical",
              "high",
              null
            ],
            "description": "Google's verdict on this fare vs the route's historical range. The field rival Google Flights APIs generally do not return."
          },
          "price_insights_low": {
            "type": [
              "number",
              "null"
            ],
            "description": "Bottom of Google's historical price range for this route and dates."
          },
          "price_insights_high": {
            "type": [
              "number",
              "null"
            ],
            "description": "Top of Google's historical price range for this route and dates."
          },
          "from_airport": {
            "type": "string"
          },
          "to_airport": {
            "type": "string"
          },
          "departure_date": {
            "type": "string",
            "format": "date"
          },
          "return_date": {
            "type": "string",
            "format": "date"
          },
          "total_price": {
            "type": "string",
            "description": "Display price for the whole trip."
          },
          "total_price_as_number": {
            "type": "number"
          },
          "total_duration_seconds": {
            "type": "integer"
          },
          "total_stops": {
            "type": "integer"
          },
          "buy_link": {
            "type": "string",
            "format": "uri"
          },
          "departure_flight_departure_description": {
            "type": "string"
          },
          "departure_flight_arrival_description": {
            "type": "string"
          },
          "departure_flight_airline": {
            "type": "string"
          },
          "departure_flight_stops": {
            "type": "integer"
          },
          "departure_flight_duration": {
            "type": "string"
          },
          "return_flight_departure_description": {
            "type": "string"
          },
          "return_flight_arrival_description": {
            "type": "string"
          },
          "return_flight_airline": {
            "type": "string"
          },
          "return_flight_stops": {
            "type": "integer"
          },
          "return_flight_duration": {
            "type": "string"
          }
        }
      },
      "HotelSearchRequest": {
        "type": "object",
        "required": [
          "destination",
          "checkin_date",
          "checkout_date"
        ],
        "properties": {
          "destination": {
            "type": "string",
            "description": "Free-text destination, e.g. `Paris`, `Tokyo Shibuya`. NOTE: the field is `destination` - a `location` key is rejected with a 400.",
            "examples": [
              "Lisbon"
            ]
          },
          "checkin_date": {
            "type": "string",
            "format": "date"
          },
          "checkout_date": {
            "type": "string",
            "format": "date"
          },
          "adults": {
            "type": "integer",
            "default": 2
          },
          "children": {
            "type": "integer",
            "default": 0
          },
          "currency": {
            "type": "string",
            "default": "USD"
          },
          "proxy_country": {
            "type": "string",
            "description": "Two-letter country code to route the request through a residential proxy in that country (e.g. `us`, `de`, `il`). Booking.com shows different prices to different markets; this is how you observe them - the basis for rate-parity and geo-pricing monitoring. When omitted, the request uses the full global residential pool."
          },
          "budget_per_night": {
            "type": "number",
            "description": "Maximum price per night, in the request currency."
          },
          "filters": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "free_cancellation",
                "breakfast_included",
                "breakfast_and_lunch",
                "breakfast_and_dinner",
                "all_meals_included",
                "all_inclusive",
                "free_wifi",
                "swimming_pool",
                "gym",
                "parking",
                "front_desk_24h",
                "review_score_7",
                "review_score_8",
                "review_score_9",
                "private_bathroom",
                "air_conditioning",
                "stars_3",
                "stars_4",
                "stars_5",
                "pets_allowed",
                "adults_only",
                "sauna",
                "very_good_breakfast",
                "accepts_online_payment"
              ]
            },
            "default": [],
            "description": "Booking.com search filters, 24 supported."
          }
        }
      },
      "HotelSearchResponse": {
        "type": "object",
        "properties": {
          "destination": {
            "type": "string"
          },
          "checkin_date": {
            "type": "string",
            "format": "date"
          },
          "checkout_date": {
            "type": "string",
            "format": "date"
          },
          "applied_filters": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "budget_per_night": {
            "type": [
              "number",
              "null"
            ]
          },
          "properties": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HotelProperty"
            }
          }
        }
      },
      "HotelProperty": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "price_string": {
            "type": "string",
            "description": "Display price for the whole stay, e.g. `US$2,434`."
          },
          "price": {
            "type": "number"
          },
          "review_score": {
            "type": [
              "number",
              "null"
            ]
          },
          "review_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "room_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "location": {
            "type": [
              "string",
              "null"
            ],
            "description": "e.g. `2.9 km from downtown`."
          },
          "image_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "link": {
            "type": "string",
            "format": "uri",
            "description": "Deep link to the property on Booking.com with dates and occupancy pre-filled."
          }
        }
      },
      "HotelByNameRequest": {
        "type": "object",
        "required": [
          "hotel_name",
          "checkin_date",
          "checkout_date"
        ],
        "properties": {
          "hotel_name": {
            "type": "string",
            "description": "Free-text hotel name, e.g. `Kremlin Palace`.",
            "examples": [
              "Four Seasons Limassol"
            ]
          },
          "checkin_date": {
            "type": "string",
            "format": "date"
          },
          "checkout_date": {
            "type": "string",
            "format": "date"
          },
          "area": {
            "type": "string",
            "description": "City/region to disambiguate generic hotel names, e.g. `Budapest`. Search uses `<hotel_name>, <area>`; name matching still uses only `hotel_name`."
          },
          "adults": {
            "type": "integer",
            "default": 2
          },
          "children": {
            "type": "integer",
            "default": 0
          },
          "currency": {
            "type": "string",
            "default": "USD"
          },
          "proxy_country": {
            "type": "string",
            "description": "Two-letter country code to route the request through a residential proxy in that country (e.g. `us`, `de`, `il`). Booking.com shows different prices to different markets; this is how you observe them - the basis for rate-parity and geo-pricing monitoring. When omitted, the request uses the full global residential pool."
          },
          "free_cancellation": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "HotelByNameResponse": {
        "type": "object",
        "description": "Availability and price for the named hotel. When `available` is false, price fields are null.",
        "properties": {
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "available": {
            "type": "boolean"
          },
          "price_string": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "type": [
              "number",
              "null"
            ]
          },
          "review_score": {
            "type": [
              "number",
              "null"
            ]
          },
          "review_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "room_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "image_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "link": {
            "type": [
              "string",
              "null"
            ]
          },
          "nights": {
            "type": [
              "integer",
              "null"
            ]
          },
          "adults": {
            "type": [
              "integer",
              "null"
            ]
          },
          "children": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "HotelRoomsRequest": {
        "type": "object",
        "required": [
          "hotel_booking_id",
          "checkin_date",
          "checkout_date"
        ],
        "properties": {
          "hotel_booking_id": {
            "type": "string",
            "description": "Booking.com hotel path id, e.g. `cy/four-seasons-limassol`. Get it from `/v1/hotels/resolve`.",
            "examples": [
              "cy/four-seasons-limassol"
            ]
          },
          "checkin_date": {
            "type": "string",
            "format": "date"
          },
          "checkout_date": {
            "type": "string",
            "format": "date"
          },
          "adults": {
            "type": "integer",
            "default": 2
          },
          "children": {
            "type": "integer",
            "default": 0
          },
          "currency": {
            "type": "string",
            "default": "USD"
          },
          "proxy_country": {
            "type": "string",
            "description": "Two-letter country code to route the request through a residential proxy in that country (e.g. `us`, `de`, `il`). Booking.com shows different prices to different markets; this is how you observe them - the basis for rate-parity and geo-pricing monitoring. When omitted, the request uses the full global residential pool."
          },
          "free_cancellation": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "HotelRoomsResponse": {
        "type": "object",
        "properties": {
          "hotel_booking_id": {
            "type": "string"
          },
          "checkin_date": {
            "type": "string",
            "format": "date"
          },
          "checkout_date": {
            "type": "string",
            "format": "date"
          },
          "booking_url": {
            "type": "string",
            "format": "uri"
          },
          "rooms": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "room_type": {
                  "type": "string"
                },
                "room_economy": {
                  "type": "string",
                  "description": "Board line, e.g. `Exceptional breakfast included`."
                },
                "guests": {
                  "type": "integer"
                },
                "price_as_number": {
                  "type": "number"
                },
                "price": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ResolveRequest": {
        "type": "object",
        "required": [
          "hotel_name"
        ],
        "properties": {
          "hotel_name": {
            "type": "string",
            "description": "Free-text hotel name.",
            "examples": [
              "boffenigo boutique italy"
            ]
          },
          "proxy_country": {
            "type": "string",
            "description": "Two-letter country code to route the request through a residential proxy in that country (e.g. `us`, `de`, `il`). Booking.com shows different prices to different markets; this is how you observe them - the basis for rate-parity and geo-pricing monitoring. When omitted, the request uses the full global residential pool."
          }
        }
      },
      "ResolveResponse": {
        "type": "object",
        "properties": {
          "hotel_name": {
            "type": "string"
          },
          "hotel_booking_id": {
            "type": "string",
            "description": "Use with `/v1/hotels/rooms`."
          },
          "matched_name": {
            "type": "string"
          }
        }
      },
      "VerifyResponse": {
        "type": "object",
        "properties": {
          "valid": {
            "type": "boolean"
          },
          "checks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "api": {
                  "type": "string",
                  "enum": [
                    "flights",
                    "hotels"
                  ]
                },
                "ok": {
                  "type": "boolean"
                },
                "checked": {
                  "type": "string"
                },
                "reason": {
                  "type": "string"
                }
              }
            }
          },
          "api_usage": {
            "type": "object",
            "description": "Your RapidAPI plan quota counters."
          },
          "key_source": {
            "type": "string",
            "description": "Which channel supplied the key (header name or query param). Never the value."
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "description": "e.g. `missing_api_key`, `invalid_api_key`, `invalid_request`, `search_incomplete`."
              },
              "reason": {
                "type": "string",
                "description": "Present on `search_incomplete`, e.g. `blocked_page`."
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}
