# Wego for Travelers & AI Agents > Read it yourself or hand it to your agent. Search and compare flights and hotels on Wego. Index: https://docs.wego.com/llms.txt # Agent setup > Give your agent the whole trip. Add the Wego skill and let your AI agent plan your travel, flights and hotels, just by asking. Source: https://docs.wego.com/agent-setup/ · Markdown: https://docs.wego.com/agent-setup/index.md Just tell your agent where you want to go. Add the Wego skill to your AI agent and it plans the flights and hotels with you, start to finish. It searches and compares; you review and book on Wego. ## Give your agent this line ```text Install the Wego CLI by running: curl -fsSL https://docs.wego.com/cli/install | bash. Then read and follow https://docs.wego.com/skills/agent-onboarding/SKILL.md ``` Paste it into your AI agent (Claude Code, ChatGPT, or any agent you already use). It installs the CLI with your OK, then reads the setup doc and finishes on its own. The install command is spelled out in the line you paste on purpose: an agent's permission gate can treat your own paste as consent to run it, where a command it discovered inside a fetched page does not clear that bar. ## How it works 1. **Paste the line into your agent.** Claude Code, ChatGPT, or any agent you already use. 2. **It sets itself up.** The agent installs the CLI (with your OK) and logs you in through your browser. No API key to paste; the browser login stores a credential locally for you. 3. **Ask for any trip.** However you say it. It searches and compares flights and hotels, then hands you a Wego link to review and book. ## What you can ask for - Cheapest nonstop from Jakarta to Bangkok in March - Business class to London, cheapest fare this month - Refundable 4-star in Dubai Marina under $250 a night - 3 nights in Bali for two, flights from KL and a beach hotel ## The commands it runs Prefer to set up by hand? The installer brings the skill with the CLI, so it is two steps: ```bash # 1. Install the CLI and the agent skill together curl -fsSL https://docs.wego.com/cli/install | bash # 2. Log in once (opens your browser, stores the token locally) wego login ``` Piped into `bash` the installer takes the skill step without asking. Set `WEGO_CLI_INSTALL_SKILL=0` first to skip it, and add it later with `wego skill install`. `wego skill path` prints where it landed. Then ask for any trip, or drive it yourself with [Wego CLI](/cli). For the HTTP contract, see the [API Reference](/api). # API Reference > Every Wego API endpoint, plus the contract rules that govern all of them. Source: https://docs.wego.com/api/ · Markdown: https://docs.wego.com/api/index.md All `/v1` operations require an OAuth2 bearer token (see [Authentication](/authentication)). This reference is written to double as an agent tool contract: every `operationId` is a stable tool name, and every error is machine branchable. ## Read these first These rules govern every endpoint: - [How search works](/api/how-search-works): searches are asynchronous; read results until they settle. - [IDs expire](/api/ids-expire): every id is opaque and expires; a 404 means search again. - [Fares, rates and partners](/api/fares-rates-partners): which results are bookable on Wego. - [Conventions](/api/conventions): query casing, default sort, entity reads. - [Errors](/api/errors): the RFC 9457 Problem envelope; branch on `code`. - [Rate limits](/api/rate-limits): quota headers and `Retry-After`. ## Endpoints - **Places**: resolve free text to typed travel locations, and find the airports near one. - **Countries**: public holidays in a market, and where a passport travels without a visa. - **Flights**: create a search, read ranked results, open a trip, read its per-leg experience signals, list fare options, get the booking link. Plus two reads that need no search: the published timetable for a route, and a durable wego.com search link. - **Hotels**: create a search, read ranked results, open a hotel, read its guest reviews, list rates, get the checkout link. Plus a durable wego.com search link. - **User**: the authenticated caller behind the bearer token. - **Feedback**: send feedback about the Wego CLI and API. Four of these need no search at all – holidays, visa-free destinations, flight schedules and nearby airports – so an agent can answer a reference question without starting a funnel. # Conventions > Query-parameter casing, default sort, and entity-read shapes. Source: https://docs.wego.com/api/conventions/ · Markdown: https://docs.wego.com/api/conventions/index.md ## Query-parameter casing One rule: a parameter that mirrors a create-body or search-context field keeps that field's **camelCase** (`pageSize`, `searchId`, `siteCode`), while a net-new filter knob that exists only as a query parameter is **kebab-case** (`min-price`, `max-star`, `booking-types`, `stopover-airports`). A single results query mixes the two by design: `?pageSize=20&min-price=100` is one author following the rule, not two. ## Default sort Default sort differs per vertical for historical reasons and is kept stable: flights default to `score_desc`, hotels to `relevance`. Both mean the metasearch ranking. The names diverged before launch, and renaming either now would break the live contract for no functional gain, so they stay as-is. ## Ambiguous places `getPlaces` resolves free text to cities, airports, states, districts and hotels. When more than one place is a plausible reading of the query it sets `metadata.hasAmbiguity`, which is the signal to ask the traveler which one they meant rather than to take the top row. An agent that resolves silently books the wrong Cambridge. ## Entity reads Entity reads are asymmetric on purpose. A hotel is a stable entity at the vertical root, so `GET /v1/hotels/{hotelId}` needs no search context. A flight trip is a snapshot inside one search, so `GET /v1/flights/trips/{tripId}?searchId=...` resolves only with the `searchId` it came from (trip ids are context bound and expire). Each shape fits its domain. # Countries > Country-keyed reference data an agent can call before any search, and combine. Source: https://docs.wego.com/api/countries/ · Markdown: https://docs.wego.com/api/countries/index.md Country-keyed reference data an agent can call before any search, and combine. `getCountryHolidays` returns a market's public holidays for spotting long weekends; `getVisaFreeDestinations` returns where a passport travels without a visa, as one complete list. Both take an ISO 3166-1 alpha-2 code as their only path key, which is what groups them under one tag – their results combine (visa-free supplies the destinations, holidays the free dates), rather than intersecting on a shared row field, since a holiday row carries no country code of its own. The visa list carries no visa type and no permitted stay – an absent country means absent from Wego's list, not that a visa is required. ## Operations | Verb | Operation | Carries onward | |---|---|---| | `GET` | [Public holidays for a market](/api/countries/get-country-holidays) · `/v1/countries/{countryCode}/holidays` | `terminal` | | `GET` | [Visa-free destinations for a passport](/api/countries/get-visa-free-destinations) · `/v1/countries/{countryCode}/visa-free-destinations` | `terminal` | # Public holidays for a market > Public holidays in one Wego market over a date range, for spotting long weekends before searching flights. Source: https://docs.wego.com/api/countries/get-country-holidays/ · Markdown: https://docs.wego.com/api/countries/get-country-holidays/index.md `GET /v1/countries/{countryCode}/holidays` Public holidays in one Wego market over a date range, for spotting long weekends before searching flights. Give both fromDate and toDate, or neither – omit both and the API searches the next 90 days and says so in metadata.window/from/to. Operation ID: `getCountryHolidays` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `countryCode` | path | string | yes | pattern `^[A-Z]{2}$` | ISO 3166-1 alpha-2 code of the Wego market whose public holidays you want (e.g. AE). A destination MARKET, not a passport: it must be one of Wego's markets, and an unknown one is rejected 400. The same path segment means a passport on the visa-free route. | | `fromDate` | query | string | no | pattern `^\d{4}-\d{2}-\d{2}$` | Inclusive ISO start date. Give both fromDate and toDate, or neither. | | `toDate` | query | string | no | pattern `^\d{4}-\d{2}-\d{2}$` | Inclusive ISO end date. Give both fromDate and toDate, or neither. | | `locale` | query | string | no | length 1..35; default `"en"` | Locale for localized names (e.g. en, ar). Defaults to en. | ## Responses | Status | Description | |---|---| | `200` | Holidays in the searched range, plus that range. | | `400` | Unknown market, malformed date, only one of fromDate/toDate, or fromDate after toDate. | | `401` | Missing or invalid bearer token. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream holidays service returned an invalid response. | | `503` | The holidays service is temporarily unavailable; retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `results` | object[] | yes | Public holidays in the resolved window. | | ↳ `name` | string | yes | Localized holiday name. | | ↳ `key` | string | yes | Stable upstream slug (e.g. national_day) – the same holiday carries the same key across locales. | | ↳ `startDate` | string | yes | Inclusive ISO YYYY-MM-DD start. | | ↳ `endDate` | string | yes | Inclusive ISO YYYY-MM-DD end; equals startDate for one-day holidays. | | `metadata` | object | yes | The market, the resolved date window, and the result count for this read. | | ↳ `resultCount` | integer | yes | Number of holidays returned. | | ↳ `countryCode` | string | yes | The market the holidays are for. | | ↳ `window` | `explicit` \| `upcoming` | yes | `explicit` when the caller supplied both dates, `upcoming` when the API chose the range (stated in from/to). | | ↳ `from` | string | yes | Inclusive ISO start of the range actually searched. | | ↳ `to` | string | yes | Inclusive ISO end of the range actually searched. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/countries/$COUNTRY_CODE/holidays" ``` # Visa-free destinations for a passport > Where a passport can travel without a visa, as one complete list (the API walks the upstream's pages). Source: https://docs.wego.com/api/countries/get-visa-free-destinations/ · Markdown: https://docs.wego.com/api/countries/get-visa-free-destinations/index.md `GET /v1/countries/{countryCode}/visa-free-destinations` Where a passport can travel without a visa, as one complete list (the API walks the upstream's pages). An inspiration list, not a visa rule: it carries no visa type and no permitted stay, and a country's absence means absent from Wego's list, never that a visa is required. Operation ID: `getVisaFreeDestinations` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `countryCode` | path | string | yes | pattern `^[A-Z]{2}$` | ISO 3166-1 alpha-2 code of the PASSPORT whose visa-free destinations you want (e.g. AE). A passport, not a market: any well-formed code is accepted (a passport need not be a Wego market) and an unrecognized one returns an honest empty list. The same path segment means a market on the holidays route. | | `locale` | query | string | no | length 1..35; default `"en"` | Locale for localized names (e.g. en, ar). Defaults to en. | | `page` | query | integer | no | 1..20; default `1` | Page number, 1-based (max 20). Defaults to 1. The list is a bounded registry the API assembles whole, so paging is rarely needed and the cap is low by design. | | `pageSize` | query | integer | no | 1..200; default `200` | Rows per page (1-200). Defaults to 200, which is also the maximum: this is a bounded registry the API walks completely, so the default returns the whole list. pageSize exists only to narrow a long answer, never to force paging. | ## Responses | Status | Description | |---|---| | `200` | Visa-free destinations for this passport, keyed on countryCode for joining. | | `400` | Malformed passport country code, page or pageSize. | | `401` | Missing or invalid bearer token. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream destinations service returned an invalid response. | | `503` | The destinations service is temporarily unavailable; retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `results` | object[] | yes | Visa-free destinations for the passport. | | ↳ `countryCode` | string | yes | ISO 3166-1 alpha-2 code – the key to join this list on. | | ↳ `name` | string | yes | Localized country name. | | ↳ `keyCityCode` | string | no | The country's principal city code, ready for a flight search. | | ↳ `currencyCode` | string | no | The destination's ISO 4217 currency code, when known. | | ↳ `latitude` | number | no | Latitude of the destination's principal city, when known. | | ↳ `longitude` | number | no | Longitude of the destination's principal city, when known. | | `metadata` | object | yes | The passport, the walk's coverage, and the counts for this read. | | ↳ `resultCount` | integer | yes | Number of destinations on the current page. | | ↳ `totalCandidates` | integer | yes | Destinations assembled across every upstream page, pre-pagination. 0 means Wego lists none for this passport – NOT that a visa is required. | | ↳ `hasMore` | boolean | yes | True when a further page exists. | | ↳ `passportCountryCode` | string | yes | The passport the list is for. | | ↳ `upstreamPagesFetched` | integer | yes | How many upstream pages were read to assemble this list. | | ↳ `coverage` | `complete` \| `truncated` | yes | `complete` when the walk ended on a short upstream page. `truncated` when the page cap stopped it on a full page, so totalCandidates is a FLOOR and destinations may exist that this response does not carry. At exactly the cap (200) a complete list also reports `truncated`, since telling the two apart would cost another upstream page. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/countries/$COUNTRY_CODE/visa-free-destinations" ``` # Errors > What the error body carries, and the closed list of codes to branch on. Source: https://docs.wego.com/api/errors/ · Markdown: https://docs.wego.com/api/errors/index.md Every error is an RFC 9457 Problem Details body (`application/problem+json`), the `Problem` schema every endpoint shares. Branch on `code` (a closed enum), never on the human `title` or `detail`. `trace_id` equals the `x-trace-id` response header; quote it when reporting an issue. ## Codes The list is closed, so a value your client does not recognise means the client is older than the API: fall back to the HTTP status. | `code` | Status | Means | What to do | |---|---|---|---| | `validation_failed` | 400 | A parameter or body field was rejected before any work happened. | Read `detail`, which names the field and the fix. Do not retry the same request unchanged. | | `invalid_token` | 401 | No bearer token, or one that is expired or not accepted. | Get a fresh access token and retry once. With the CLI, `wego login`. | | `insufficient_scope` | 403 | The token is valid but lacks a scope this operation needs. | Re-authorize asking for the required scope, then retry. | | `not_found` | 404 | On a search-scoped id, usually an expired id rather than a wrong one: every `searchId`, trip id, fare id and rate id is opaque, context-bound and short-lived. On a `hotelId`, which does not expire, it means the hotel is unknown. | For a search-scoped id, create a new search and rethread the ids from it; retrying the same id never recovers. For a `hotelId`, check the id: searching again will not help. | | `rates_require_hotel_search` | 409 | `getHotelRates` was given a `searchId` from a city or geo search. Only a search created with `hotelId` prices a hotel's full room list; a city search holds a sample of it, and re-reading never deepens that sample. | Create a search with `hotelId` plus the same dates and occupancy, then read its rates. With the CLI, `wego hotels rooms `. | | `rate_limited` | 429 | You exceeded a quota. | Wait `Retry-After` seconds; it takes precedence over the reset in `RateLimit`. Creating a search is the costly step, so thread one `searchId` through `results` rather than re-creating. | | `bad_gateway` | 502 | A travel provider answered with something the API could not use. | Retry once. If it persists, quote `trace_id`. | | `upstream_unavailable` | 503 | A travel provider is temporarily unreachable. | Honor `Retry-After` and retry. | | `upstream_rate_limited` | 503 | A travel provider's own rate limit rejected the call. That quota is shared across callers, so you can hit it without having sent much yourself. | Wait the full `Retry-After` (60 seconds). If the retry gets the same error, wait the new `Retry-After` again: the provider also enforces a longer window, so one wait is not always enough. Pace search creation rather than bursting. | | `internal_error` | 500 | A fault on the API side. | Retry once, then quote `trace_id` if it persists. | `status` repeats the HTTP status and `title` is fixed per code, so neither carries information `code` does not. Only `detail` varies per occurrence, and it is written for a human reader. # Fares, rates and partners > Which flight fares and hotel rates are bookable on wego.com, and which are not. Source: https://docs.wego.com/api/fares-rates-partners/ · Markdown: https://docs.wego.com/api/fares-rates-partners/index.md A flight trip carries one or more fares, each with `kind` `"wego"`, `"airline"` or `"partner"`. Wego fares are bookable on wego.com and continue into `getFareOptions` (`/v1/flights/fares/{fareId}/options`, the fare's bookable options such as Saver and Flex; an option's id is its `fareOptionId`) and `getFareBookingLink` (`/v1/flights/fares/{fareId}/booking-link`). Airline and partner fares are fulfilled by the airline or the travel agency directly and do not work with the fare endpoints: `getFareOptions` rejects a non-wego `fareId` with a `400`. Every fare carries a `handoffUrl` on the trip read (`GET /v1/flights/trips/{tripId}`, `fares[].handoffUrl`), whatever its `kind`, so it is not what tells the kinds apart: on a wego fare it points at wego.com checkout, on the other two at the provider. ## One option, or one per leg `getFareOptions` returns the options ordered by **leg first, then price within a leg**, so `options[0]` is the cheapest option of leg 1 rather than the cheapest of the fare. How many ids you carry into `getFareBookingLink` depends on what each option prices, which it states in `price.covers`: - `"trip"` – the option covers the whole journey. Pass exactly one `fareOptionId`. - `"leg"` – the option covers only its own leg, and its `legId` names which. Pass **one id per leg**, as a comma-separated `fareOptionId` (at most 8, none repeated, order irrelevant). The response's `legs[]` is the set to cover; it is present whenever every option carries a `legId` matching one of them, which is the case this branch describes. Passing a single `"leg"` id is the failure worth knowing about, because nothing rejects it: the booking page opens on the whole round trip while pricing one leg of it, so the traveler sees a total that is not the trip's. `covers` is a **positive witness** – absent means the upstream did not let us attribute the option, never that it covers the trip. For the trip total, read the response's own top-level `price`, which is the whole-trip price for the fare. Never sum the options and never take `min(options)`: one is wrong on a `"trip"` fare, the other on a `"leg"` one. From the CLI, `wego flights fares ` lists them and `wego flights booking-link` takes `--fare-option` once per leg. ## Hotel rates Hotel rates follow the same rule: everything `getHotelRates` (`/v1/hotels/{hotelId}/rates`) returns is bookable on wego.com, each rate carrying board and refundability fields plus a rate id for the checkout link. # Feedback > Send feedback about the Wego CLI/API experience. Source: https://docs.wego.com/api/feedback/ · Markdown: https://docs.wego.com/api/feedback/index.md Send feedback about the Wego CLI/API experience. `submitFeedback` records an optional category plus at least one of a rating (1-5) or a free-text message (the CLI's `wego feedback`); a category alone is rejected. ## Operations | Verb | Operation | Carries onward | |---|---|---| | `POST` | [Submit feedback](/api/feedback/submit-feedback) · `/v1/feedback` | `terminal` | # Submit feedback > Records feedback about the Wego CLI/API experience – a rating (1-5), a category, and/or a free-text message. Source: https://docs.wego.com/api/feedback/submit-feedback/ · Markdown: https://docs.wego.com/api/feedback/submit-feedback/index.md `POST /v1/feedback` Records feedback about the Wego CLI/API experience – a rating (1-5), a category, and/or a free-text message. At least one of rating or message is required. Fire-and-forget: returns 202 and never blocks on recording. Operation ID: `submitFeedback` ## Request body `application/json` | Field | Type | Required | Constraints | Description | |---|---|---|---|---| | `rating` | integer | no | 1..5 | Overall rating, 1 (poor) to 5 (great). | | `category` | `flights` \| `hotels` \| `other` | no | - | Which area the feedback is about. | | `message` | string | no | length 1..2000 | Free-text feedback (bugs, ideas, what worked). | | `version` | string | no | length *..50 | CLI version the feedback came from. | ## Responses | Status | Description | |---|---| | `202` | Feedback accepted. | | `400` | Invalid feedback body (e.g. neither rating nor message provided). | | `401` | Missing or invalid bearer token. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | ### `202` body | Field | Type | Always present | Description | |---|---|---|---| | `status` | string | yes | The feedback was accepted. | ## Example ```bash curl -s -X POST -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"rating":5,"message":"Fare options were exactly what I needed."}' \ "https://api.wego.com/v1/feedback" ``` # Flights > The flight funnel: `createFlightSearch` starts an async search, `getFlightSearchResults` reads ranked snapshots while providers answer, `getFlightTrip` opens one trip with every fare. Source: https://docs.wego.com/api/flights/ · Markdown: https://docs.wego.com/api/flights/index.md The flight funnel: `createFlightSearch` starts an async search, `getFlightSearchResults` reads ranked snapshots while providers answer, `getFlightTrip` opens one trip with every fare. Fares with `kind: "wego"` continue into `getFareOptions` and `getFareBookingLink`; airline and partner fares carry their own handoff URL instead. Ids expire; a `404` means search again. `getFlightSchedules` sits outside the funnel – a published timetable with no prices and no search to settle. ## Operations | Verb | Operation | Carries onward | |---|---|---| | `POST` | [Create a flight search](/api/flights/create-flight-search) · `/v1/flights/searches` | `searchId` | | `GET` | [Published timetable for a route](/api/flights/get-flight-schedules) · `/v1/flights/schedules` | `terminal` | | `GET` | [Read ranked flight results](/api/flights/get-flight-search-results) · `/v1/flights/searches/{searchId}/results` | `tripId` | | `GET` | [Open one flight trip](/api/flights/get-flight-trip) · `/v1/flights/trips/{tripId}` | `fareId` | | `GET` | [Read a trip's experience signals](/api/flights/get-trip-experience) · `/v1/flights/trips/{tripId}/experience` | `terminal` | | `GET` | [List a fare's options](/api/flights/get-fare-options) · `/v1/flights/fares/{fareId}/options` | `fareOptionId` | | `GET` | [Build a fare's wego.com booking link](/api/flights/get-fare-booking-link) · `/v1/flights/fares/{fareId}/booking-link` | `wego.com URL` | | `GET` | [Build a durable wego.com search link](/api/flights/get-flight-search-link) · `/v1/flights/search-link` | `wego.com URL` | # Create a flight search > Creates a metasearch for the given route/dates/passengers and returns its searchId. Source: https://docs.wego.com/api/flights/create-flight-search/ · Markdown: https://docs.wego.com/api/flights/create-flight-search/index.md `POST /v1/flights/searches` Creates a metasearch for the given route/dates/passengers and returns its searchId. Results accrue asynchronously – poll getFlightSearchResults with the returned searchId to read ranked trips. Operation ID: `createFlightSearch` ## Request body `application/json` | Field | Type | Required | Constraints | Description | |---|---|---|---|---| | `from` | string | yes | pattern `^[A-Z]{3}$` | Origin airport or city IATA code, e.g. DXB. | | `to` | string | yes | pattern `^[A-Z]{3}$` | Destination airport or city IATA code, e.g. LHR. | | `fromDate` | string | yes | pattern `^\d{4}-\d{2}-\d{2}$` | Outbound departure date, YYYY-MM-DD. Not in the past, within a year. | | `toDate` | string | no | pattern `^\d{4}-\d{2}-\d{2}$` | Return date, YYYY-MM-DD. Omit for a one-way search. | | `cabin` | `economy` \| `premium_economy` \| `business` \| `first` | no | default `"economy"` | Cabin class requested for all passengers. | | `adults` | integer | no | 1..9; default `1` | Adult passengers (1-9). Defaults to 1. Note the hotel search defaults adults to 2, since a room sleeps two. | | `children` | integer | no | 0..8; default `0` | Child passengers (0-8). Defaults to 0. | | `infants` | integer | no | 0..8; default `0` | Infant passengers (0-8). Must not exceed adults. Defaults to 0. | | `currency` | string | no | pattern `^[A-Z]{3}$`; default `"USD"` | Pricing currency as a 3-letter ISO 4217 code. Defaults to USD. | | `locale` | string | no | length 1..35; default `"en"` | Response language tag (e.g. en, ar). Defaults to en. | | `siteCode` | string | no | pattern `^[A-Z]{2}$` | Wego market (point of sale) as a 2-letter code, e.g. AE. Optional: if omitted the API defaults to US. A client that knows the user's market (the wego CLI derives it from the id_token) passes it as an explicit siteCode; the response always reports the siteCode used. | ## Responses | Status | Description | |---|---| | `201` | Search created. | | `400` | Invalid request body/query/path parameters. | | `401` | Missing or invalid bearer token. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream flights service returned an invalid response. | | `503` | The flights service is temporarily unavailable (`upstream_unavailable`) or rate-limited upstream (`upstream_rate_limited`); retry after the `Retry-After` seconds. | ### `201` body | Field | Type | Always present | Description | |---|---|---|---| | `searchId` | string | yes | The id of the created search; pass it to the results and trip reads. | | `siteCode` | string | yes | The site code (Wego market) the search was created for. | | `siteCodeSource` | `explicit` \| `default` | yes | How the API resolved siteCode: explicit (caller-supplied – including a market a client derived and passed) or default (US, no site supplied). | ## Example ```bash FROM_DATE=$(date -u -v+60d +%F 2>/dev/null || date -u -d '+60 days' +%F) curl -s -X POST -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"from":"DXB","to":"LHR","fromDate":"'"$FROM_DATE"'"}' \ "https://api.wego.com/v1/flights/searches" ``` # Build a fare's wego.com booking link > Builds the wego.com booking deep-link for a chosen Book-on-Wego fare, with one fare option pre-selected PER LEG. Source: https://docs.wego.com/api/flights/get-fare-booking-link/ · Markdown: https://docs.wego.com/api/flights/get-fare-booking-link/index.md `GET /v1/flights/fares/{fareId}/booking-link` Builds the wego.com booking deep-link for a chosen Book-on-Wego fare, with one fare option pre-selected PER LEG. A pure, stateless URL build from the caller-supplied search context - no upstream call, no booking, no payment. The caller passes back the trip/leg/passenger context. Operation ID: `getFareBookingLink` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `fareId` | path | string | yes | pattern `^[A-Za-z0-9._:~=-]{1,256}$` | The Book-on-Wego fare id from a flight-search trip detail. | | `tripId` | query | string | yes | pattern `^[A-Za-z0-9._~=-]+:[A-Za-z0-9._:~=-]+$`; length *..256 | The trip the fare belongs to, from a flight-search result. Shaped {searchId}:{tripCode}. | | `searchId` | query | string | no | pattern `^[A-Za-z0-9._:~=-]{1,256}$` | Optional cross-check. When supplied it must equal the tripId's first :-segment; a mismatch is rejected. | | `fareOptionId` | query | string | yes | pattern `^[A-Za-z0-9._:~=-]{1,256}(?:,[A-Za-z0-9._:~=-]{1,256}){0,7}$` | The fare option(s) to pre-select, from GET /v1/flights/fares/{fareId}/options. Required: without it the booking page has no fare to open and dead-ends. When that read reported price.covers=leg, pass ONE id per trip leg as a comma-separated list, in any order: a single id then prices only its own leg while the page still presents the whole round trip. A whole-trip fare (price.covers=trip) takes exactly one id. At most 8, and no id twice. | | `from` | query | string | yes | pattern `^[A-Z]{3}$` | Origin airport or city code, as sent to the flight search. | | `to` | query | string | yes | pattern `^[A-Z]{3}$` | Destination airport or city code, as sent to the flight search. | | `fromCity` | query | boolean \| `0` \| `1` \| `true` \| `false` | no | - | Whether `from` is a city code rather than an airport code. | | `toCity` | query | boolean \| `0` \| `1` \| `true` \| `false` | no | - | Whether `to` is a city code rather than an airport code. | | `fromDate` | query | string | yes | pattern `^\d{4}-\d{2}-\d{2}$` | Outbound departure date, YYYY-MM-DD, as sent to the flight search. | | `toDate` | query | string | no | pattern `^\d{4}-\d{2}-\d{2}$` | Return date, YYYY-MM-DD. Omit for a one-way handoff. | | `cabin` | query | `economy` \| `premium_economy` \| `business` \| `first` | no | default `"economy"` | Cabin class, as sent to the flight search. | | `adults` | query | integer | no | 1..9; default `1` | Adult passengers (1-9). | | `children` | query | integer | no | 0..8; default `0` | Child passengers (0-8). | | `infants` | query | integer | no | 0..8; default `0` | Infant passengers (0-8). Must not exceed adults. | | `siteCode` | query | string | no | pattern `^[A-Z]{2}$` | Wego market (point of sale) as a 2-letter code, e.g. AE. Optional: if omitted the API defaults to US. A client that knows the user's market (the wego CLI derives it from the id_token) passes it as an explicit siteCode. | | `currency` | query | string | no | pattern `^[A-Z]{3}$` | Pricing currency as a 3-letter ISO 4217 code (e.g. AED). Optional: when omitted the built URL carries NO currency parameter – it is not defaulted to USD, so wego.com shows the market's own default. Pass it to pin the handoff to a currency. | | `locale` | query | string | no | length 1..35; default `"en"` | Response language tag for the wego.com page (e.g. en, ar). Defaults to en. | ## Responses | Status | Description | |---|---| | `200` | The booking handoff URL. | | `400` | Invalid fare id or query parameters. | | `401` | Missing or invalid bearer token. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `bookingUrl` | string | yes | A wego.com booking deep-link with the chosen fare pre-selected. | | `expires` | boolean | yes | Always true: this link is bound to a live search and stops working with it, in about five to seven minutes. To send someone a link that lasts, use GET /v1/flights/search-link. | ## Example ```bash FROM_DATE=$(date -u -v+60d +%F 2>/dev/null || date -u -d '+60 days' +%F) curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/flights/fares/$FARE_ID/booking-link?tripId=$TRIP_ID&fareOptionId=$FARE_OPTION_ID&from=DXB&to=LHR&fromDate=$FROM_DATE" ``` # List a fare's options > Returns a Book-on-Wego fare's bookable options (price, baggage, refundability, penalties, and the carrier's terms links when it publishes any), ordered by leg then price. Source: https://docs.wego.com/api/flights/get-fare-options/ · Markdown: https://docs.wego.com/api/flights/get-fare-options/index.md `GET /v1/flights/fares/{fareId}/options` Returns a Book-on-Wego fare's bookable options (price, baggage, refundability, penalties, and the carrier's terms links when it publishes any), ordered by leg then price. A multi-leg trip needs ONE option per leg - read the top-level price, never min(options). Non-wego fareId 400; stale 404. Operation ID: `getFareOptions` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `fareId` | path | string | yes | pattern `^[A-Za-z0-9._:~=-]{1,256}$` | The Book-on-Wego fare id from a flight-search trip detail. | | `currency` | query | string | no | pattern `^[A-Z]{3}$`; default `"USD"` | Pricing currency as a 3-letter ISO 4217 code. | | `locale` | query | string | no | length 1..35; default `"en"` | Response language tag. | ## Responses | Status | Description | |---|---| | `200` | The fare options, ordered by leg then cheapest-first within a leg. | | `400` | Invalid request query/path parameters, or the fares service rejected the fareId or currency (`validation_failed`; the `detail` names the recovery). | | `401` | Missing or invalid bearer token. | | `404` | The fare was not found or its search has expired. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream flights service returned an invalid response. | | `503` | The flights service is temporarily unavailable (`upstream_unavailable`) or rate-limited upstream (`upstream_rate_limited`); retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `fareId` | string | yes | The fare id these options are for. | | `currencyCode` | string | yes | The currency the prices are quoted in – the same value as metadata.currencyCode, which carries currencyCodeSource beside it. Unlike the results read, this route quotes in the currency it asked for, so the two cannot disagree. | | `price` | object | no | The whole-trip price for this fare, so no caller has to add per-leg options together. Absent when the upstream did not state one. | | ↳ `total` | number | yes | Whole-trip, whole-party total for the cheapest combination of options, as the upstream states it. Not computed here. | | ↳ `totalUsd` | number | no | The same figure in USD. | | ↳ `currency` | string | yes | ISO 4217 currency of total. | | ↳ `scope` | string | yes | total covers the whole party (adults + children + infants), not per-person. | | `legs` | object[] | no | The legs the options are attributed to, in upstream leg order. Present only when every option carries a legId that matches one of them. | | ↳ `legId` | number | yes | The leg this group of options prices, as the upstream numbers it (1 = the first leg). | | ↳ `from` | string | no | Departure airport code of the leg. | | ↳ `to` | string | no | Arrival airport code of the leg. | | ↳ `departsAt` | string | no | Local departure date-time of the leg. | | ↳ `airlines` | string[] | no | Marketing airline codes on the leg. | | `options` | object[] | yes | The full fare option list – no filter, no pagination. Ordered by legId, then cheapest-first inside each leg, so the two legs of a split fare never interleave. | | ↳ `fareOptionId` | string | yes | The fare option id (a UUID); sent to wego.com as the booking link's branded_fare param. | | ↳ `name` | string | yes | The marketing name, e.g. Economy Lite. | | ↳ `price` | object | yes | The price of one fare option (pass-through display values). Always whole-party for passengers; read covers for how much of the trip it pays for. | | ↳ `refundable` | boolean | yes | Whether this option is refundable. | | ↳ `exchangeable` | boolean | yes | Whether this option allows a date or flight change. | | ↳ `baggage` | object | yes | Baggage allowance display strings for one fare option. | | ↳ `penalties` | object[] | yes | Always both change and cancel, in that order. | | ↳ `termsUrls` | string[] | no | The airline's terms and conditions links for this option, in the order the carrier lists them. Absent when the carrier publishes its rules as text rather than links, or when the terms read was unavailable - never an empty array. These are the carrier's own pages, not a machine-readable rulebook: refundability, exchangeability, baggage and the change/cancel penalties are the fields on this option, and are what an agent should reason over. | | ↳ `legId` | number | no | The leg this option prices, matching a legs[] entry. Present when the upstream attributes it; absent when it does not. | | `metadata` | object | yes | What this read resolved currency and locale to, and how each was decided. | | ↳ `currencyCode` | string | yes | The currency this read ASKED upstream for, and the one every price on it is meant to be in. Read it beside currencyCodeSource before you show a number: a price computed in the wrong currency renders as a perfectly normal price, with no error and no odd shape to notice, so the response states which one rather than leaving it to be inferred. Where the operation also publishes a top-level currencyCode, that field reports the currency the prices actually came back in; the two agree unless upstream declined to reprice. | | ↳ `currencyCodeSource` | `explicit` \| `default` | yes | How the API resolved currencyCode: explicit (the caller sent currency – including a value equal to the default) or default (USD, no currency sent). A default here is the one signal that the request never carried the currency you meant. | | ↳ `locale` | string | yes | The language tag this read asked upstream for – what any localized text on it was resolved in (room and board names, airline and airport names, review prose). | | ↳ `localeSource` | `explicit` \| `default` | yes | How the API resolved locale: explicit (the caller sent locale – including a value equal to the default) or default (en, no locale sent). A default here explains text that came back in a language the caller did not ask for. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/flights/fares/$FARE_ID/options" ``` # Published timetable for a route > What actually flies a route – times, duration, aircraft, and the weekdays each flight runs – with no prices and no search to settle. Source: https://docs.wego.com/api/flights/get-flight-schedules/ · Markdown: https://docs.wego.com/api/flights/get-flight-schedules/index.md `GET /v1/flights/schedules` What actually flies a route – times, duration, aircraft, and the weekdays each flight runs – with no prices and no search to settle. Nonstop flights only. Airport codes resolve to their parent city (LHR to LON), and metadata echoes what each side resolved to. Operation ID: `getFlightSchedules` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `from` | query | string | yes | pattern `^[A-Z]{3}$` | Departure city or airport code; an airport resolves to its city. | | `to` | query | string | yes | pattern `^[A-Z]{3}$` | Arrival city or airport code; an airport resolves to its city. | | `airline` | query | string | no | pattern `^[A-Z0-9]{2}$` | Filter to one marketing carrier (e.g. SQ). | | `siteCode` | query | string | no | pattern `^[A-Z]{2}$` | Wego market as a 2-letter code. Omitted, the API defaults to US and says so in metadata.siteCodeSource. | | `locale` | query | string | no | length 1..35; default `"en"` | Response language tag. | | `page` | query | integer | no | 1..20; default `1` | Page number, 1-based (max 20). Defaults to 1. A timetable is a bounded list the API reads whole, so paging is rarely needed and the cap is low by design. | | `pageSize` | query | integer | no | 1..200; default `200` | Rows per page (1-200). Defaults to 200, which is also the maximum, so most routes return whole on one page. A busier route exceeds it and says so with hasMore. pageSize exists to narrow a long answer, never to force paging. | ## Responses | Status | Description | |---|---| | `200` | Scheduled flights, plus the resolved route and the market used. | | `400` | Malformed code, a code that resolves to no city, or a page or pageSize outside its range – an out-of-range paging value is rejected, never clamped. | | `401` | Missing or invalid bearer token. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream schedules service returned an invalid response. | | `503` | The schedules service is temporarily unavailable (`upstream_unavailable`) or rate-limited upstream (`upstream_rate_limited`); retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `results` | object[] | yes | The scheduled flights for this route, a timetable with no prices. | | ↳ `airlineCode` | string | yes | Marketing carrier code – what `airline` filters on. | | ↳ `departureAirportCode` | string | yes | Departure airport IATA code. | | ↳ `arrivalAirportCode` | string | yes | Arrival airport IATA code. | | ↳ `departureTime` | string | yes | Local HH:MM at the departure airport. | | ↳ `arrivalTime` | string | yes | Local HH:MM at the arrival airport. | | ↳ `durationMinutes` | number | yes | Total scheduled duration in minutes. | | ↳ `stopsCount` | number | yes | Stops on the route, as the timetable reports them; 0 is nonstop. This read covers nonstop scheduled flights, so a connecting itinerary is absent rather than listed with a stop. | | ↳ `arrivalDayOffset` | number | yes | Days the arrival falls after departure; 1 means next-day. | | ↳ `segments` | object[] | yes | The individual flights that make up this schedule. | | ↳ `operatingPeriods` | object[] | yes | When this flight runs – one entry per published operating period. Empty when the upstream states no recurrence, which means unknown, never daily. | | ↳ `flightNumber` | string | no | The marketed designator, present on a single-segment schedule. | | ↳ `aircraftCode` | string | no | Aircraft type code, when reported. | | `metadata` | object | yes | The page returned, how much the upstream held, the resolved route endpoints, and the market echoed. | | ↳ `page` | integer | yes | The 1-based page returned. | | ↳ `pageSize` | integer | yes | Rows requested per page. | | ↳ `resultCount` | integer | yes | Scheduled flights on this page (always <= pageSize). | | ↳ `totalCandidates` | integer | yes | Scheduled flights the upstream held for this route, pre-pagination – the ceiling paging can reach. 0 means the upstream publishes no timetable for this route, NOT that nothing flies it. | | ↳ `hasMore` | boolean | yes | True when a further page exists. | | ↳ `coverage` | `complete` \| `truncated` | yes | complete when the upstream returned its whole set for this route, truncated when it filled the API's upstream ceiling and may hold more. While truncated, read totalCandidates as a floor rather than a total. | | ↳ `from` | object | yes | Departure route endpoint: what the caller sent and the city code it resolved to. | | ↳ `to` | object | yes | Arrival route endpoint: what the caller sent and the city code it resolved to. | | ↳ `siteCode` | string | yes | The market this request resolved to, as a 2-letter code. Echoed for consistency with the priced reads – a published timetable does not vary by market, so it does not change these rows. | | ↳ `siteCodeSource` | `explicit` \| `default` | yes | explicit when the caller supplied siteCode, default when the API applied the US floor. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/flights/schedules?from=DXB&to=LHR" ``` # Build a durable wego.com search link > Builds a shareable wego.com flight-search URL from the caller's own route, dates, cabin and passengers. Source: https://docs.wego.com/api/flights/get-flight-search-link/ · Markdown: https://docs.wego.com/api/flights/get-flight-search-link/index.md `GET /v1/flights/search-link` Builds a shareable wego.com flight-search URL from the caller's own route, dates, cabin and passengers. A pure, stateless string build - no upstream call, no search created. It carries no search-scoped id, so it does not expire: whoever opens it runs the search live. Operation ID: `getFlightSearchLink` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `from` | query | string | yes | pattern `^[A-Z]{3}$` | Origin airport or city code, as sent to the flight search. | | `to` | query | string | yes | pattern `^[A-Z]{3}$` | Destination airport or city code, as sent to the flight search. | | `fromCity` | query | boolean \| `0` \| `1` \| `true` \| `false` | no | - | Whether `from` is a city code rather than an airport code. | | `toCity` | query | boolean \| `0` \| `1` \| `true` \| `false` | no | - | Whether `to` is a city code rather than an airport code. | | `fromDate` | query | string | yes | pattern `^\d{4}-\d{2}-\d{2}$` | Outbound departure date, YYYY-MM-DD, as sent to the flight search. | | `toDate` | query | string | no | pattern `^\d{4}-\d{2}-\d{2}$` | Return date, YYYY-MM-DD. Omit for a one-way handoff. | | `cabin` | query | `economy` \| `premium_economy` \| `business` \| `first` | no | default `"economy"` | Cabin class, as sent to the flight search. | | `adults` | query | integer | no | 1..9; default `1` | Adult passengers (1-9). | | `children` | query | integer | no | 0..8; default `0` | Child passengers (0-8). | | `infants` | query | integer | no | 0..8; default `0` | Infant passengers (0-8). Must not exceed adults. | | `siteCode` | query | string | no | pattern `^[A-Z]{2}$` | Wego market (point of sale) as a 2-letter code, e.g. AE. Optional: if omitted the API defaults to US. A client that knows the user's market (the wego CLI derives it from the id_token) passes it as an explicit siteCode. | | `currency` | query | string | no | pattern `^[A-Z]{3}$` | Pricing currency as a 3-letter ISO 4217 code (e.g. AED). Optional: when omitted the built URL carries NO currency parameter – it is not defaulted to USD, so wego.com shows the market's own default. Pass it to pin the handoff to a currency. | | `locale` | query | string | no | length 1..35; default `"en"` | Response language tag for the wego.com page (e.g. en, ar). Defaults to en. | ## Responses | Status | Description | |---|---| | `200` | The durable wego.com search URL. | | `400` | Invalid query parameters. fromDate must be a real calendar date, not in the past, and within 365 days; toDate must be a real date on or after it. A durable link cannot carry a date no live search can represent. | | `401` | Missing or invalid bearer token. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `searchUrl` | string | yes | A wego.com flight-search URL for this route, dates, cabin and passengers. Opening it runs the search live. | | `expires` | boolean | yes | Always false: the URL carries no search-scoped id, so it keeps working. The prices behind it are whatever a live search returns when it is opened. | ## Example ```bash FROM_DATE=$(date -u -v+60d +%F 2>/dev/null || date -u -d '+60 days' +%F) curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/flights/search-link?from=DXB&to=LHR&fromDate=$FROM_DATE" ``` # Read ranked flight results > Ranked trips as lean list cards (default 10, max 50 per page; out-of-range rejected 400), filters + sort applied. Source: https://docs.wego.com/api/flights/get-flight-search-results/ · Markdown: https://docs.wego.com/api/flights/get-flight-search-results/index.md `GET /v1/flights/searches/{searchId}/results` Ranked trips as lean list cards (default 10, max 50 per page; out-of-range rejected 400), filters + sort applied. No completion flag: re-read (300ms→3s) until snapshotFareCount holds steady across two reads AND snapshotTripCount > 0. No fares[] on a card – read the trip for fares. Operation ID: `getFlightSearchResults` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `searchId` | path | string | yes | pattern `^[A-Za-z0-9._:~=-]{1,256}$` | The opaque searchId returned by createFlightSearch. Ids expire (a few minutes); a 404 means the search is gone – create a new one. | | `page` | query | integer | no | 1..100; default `1` | Page number, 1-based (max 100). | | `pageSize` | query | integer | no | 1..50; default `10` | Results per page (default 10, max 50); out-of-range values are rejected with 400, never clamped. | | `sort` | query | `score_desc` \| `price_asc` \| `duration_asc` \| `leg1_departure_time_asc` \| `leg1_departure_time_desc` \| `leg2_departure_time_asc` \| `leg2_departure_time_desc` | no | default `"score_desc"` | Sort mode; score_desc (default) ranks by the metasearch score, the leg1/leg2 modes sort by that leg's local departure time. | | `airlines` | query | string[] | no | items 1..* | Airline IATA codes, repeat or comma-separate (e.g. ?airlines=SQ,TR). Values are OR'd together; by default a trip matches if ANY leg (outbound or return) carries any listed airline, not a trip-wide AND. Pass airlines-match=all for the trip-wide AND that wego.com applies. | | `airlines-match` | query | `any` \| `all` | no | - | How the airlines list is matched across a trip's legs. 'any' (the default) keeps a trip when ANY leg carries a listed airline. 'all' requires EVERY leg to, which is what wego.com does, so a Saudia-out / Emirates-back trip is dropped under ?airlines=EK. A leg marketed by two carriers still passes when one of them is listed, so add same-airline=true to require a single carrier as well. Ignored when airlines is absent. | | `same-airline` | query | `0` \| `1` \| `true` \| `false` | no | - | Keep only trips where every leg is marketed by exactly ONE airline and it is the same airline on every leg, so an interline or self-transfer leg marketed by two carriers is dropped. Matches the MARKETING carrier only: a codeshare, where one airline sells a flight another operates, is NOT excluded, because wego.com's 'same airline for the complete trip' does not exclude it either. Independent of airlines, so it works on its own; combine the two to mean 'only SQ, on every leg'. | | `alliances` | query | string[] | no | items 1..* | Alliance codes, repeat or comma-separate. OR'd together; a trip matches if ANY leg (outbound or return) belongs to any listed alliance. Case-insensitive. NOT a fixed set – read metadata.filterOptions.alliances for this snapshot's own values, which include upstream groupings that are not strictly alliances (lcc for low-cost carriers, value_alliance). An unknown code matches nothing rather than failing the request. | | `stops` | query | integer[] | no | items 1..* | Stop counts, repeat or comma-separate (e.g. ?stops=0,1). OR'd together; matched against the trip-level stop count (the MAX across legs, i.e. the value the response's stops field exposes) – not a per-leg check, so a mixed-stop round trip (e.g. a direct outbound + a 1-stop return) is kept under ?stops=1. | | `min-price` | query | number | no | 0..* | Minimum cheapest-fare total price (inclusive), in the response currency. | | `max-price` | query | number | no | 0..* | Maximum cheapest-fare total price (inclusive), in the response currency. | | `max-duration` | query | integer | no | 1..9007199254740991 | Maximum total trip duration in minutes (inclusive). | | `min-stopover-duration` | query | string | no | pattern `^\d{1,7}$` | Minimum layover time in minutes (inclusive), a floor on a trip's WORST leg. Judged per trip on the LARGEST leg total across its legs, the same fold stops applies, NOT on an individual connection: a leg with two 40-minute connections totals 80 and passes ?min-stopover-duration=60. A DIRECT trip has no layover, so it totals 0 and is DROPPED by any minimum above 0 - combine with ?stops=0 only if you want both. A trip whose layover upstream never stated is dropped by either bound rather than assumed to be 0. Read metadata.filterOptions.stopoverDurations for the span this snapshot carries, and judge the result on metadata.totalCandidates, never on the page. | | `max-stopover-duration` | query | string | no | pattern `^\d{1,7}$` | Maximum layover time in minutes (inclusive), a ceiling on a trip's worst leg. It bounds how LONG a wait is, never when it falls: a 135-minute wait beginning 04:00 is under any sane ceiling and is still an overnight one, and a 465-minute wait beginning 11:00 is over it and never sees a night. To answer whether a wait falls overnight, read the connection's own clock from GET /v1/flights/trips/{tripId}?view=detail, whose segments carry arrivesAt and departsAt. Judged per trip on the LARGEST leg total across its legs, NOT on an individual connection: a 2-stop leg waiting 450 then 510 totals 960 and is dropped by ?max-stopover-duration=600 even though neither connection exceeds it. Direct trips total 0, so every maximum keeps them. Each card's legs[].layoverMinutesByStop carries the per-connection breakdown that sums to the total judged here. Read metadata.filterOptions.stopoverDurations for the span this snapshot carries, and judge the result on metadata.totalCandidates, never on the page. | | `outbound-departure-blocks` | query | `midnight` \| `morning` \| `afternoon` \| `night`[] | no | items 1..*; each of `midnight`, `morning`, `afternoon`, `night` | Coarse local-time buckets for the OUTBOUND leg's DEPARTURE: midnight 00:00-05:59, morning 06:00-11:59, afternoon 12:00-17:59, night 18:00-23:59, local to that airport. Repeat or comma-separate; OR'd together. The four buckets PARTITION the day, so listing all four returns every trip exactly once - wego.com's own buckets overlap at 06:00, 12:00 and 18:00 and these do not. They are wider than they sound, so prefer the matching -range param whenever the caller gave a hard edge. | | `outbound-departure-range` | query | string | no | pattern `^(?:\s*(?:\d{1,3}\|1[0-3]\d{2}\|14[0-2]\d\|143[0-9])\s*-\s*(?:\d{1,3}\|1[0-3]\d{2}\|14[0-2]\d\|143[0-9])\s*)?$` | Minute-of-day window `min-max` (each 0-1439, local to that airport), both ends inclusive. When min > max the window wraps past midnight, e.g. 1320-360 means 22:00-06:00. Bounds the OUTBOUND leg's DEPARTURE, local to the departure airport. | | `outbound-arrival-blocks` | query | `midnight` \| `morning` \| `afternoon` \| `night`[] | no | items 1..*; each of `midnight`, `morning`, `afternoon`, `night` | Coarse local-time buckets for when the OUTBOUND leg LANDS: midnight 00:00-05:59, morning 06:00-11:59, afternoon 12:00-17:59, night 18:00-23:59, local to that airport. Repeat or comma-separate; OR'd together. The four buckets PARTITION the day, so listing all four returns every trip exactly once - wego.com's own buckets overlap at 06:00, 12:00 and 18:00 and these do not. They are wider than they sound, so prefer the matching -range param whenever the caller gave a hard edge. | | `outbound-arrival-range` | query | string | no | pattern `^(?:\s*(?:\d{1,3}\|1[0-3]\d{2}\|14[0-2]\d\|143[0-9])\s*-\s*(?:\d{1,3}\|1[0-3]\d{2}\|14[0-2]\d\|143[0-9])\s*)?$` | Minute-of-day window `min-max` (each 0-1439, local to that airport), both ends inclusive. When min > max the window wraps past midnight, e.g. 1320-360 means 22:00-06:00. Bounds when the OUTBOUND leg LANDS, local to the ARRIVAL airport - this is the param for "get me in before midnight" or "nothing that lands at 4am". Judged on the clock ALONE, not the calendar: a red-eye landing 04:00 the NEXT day reads as minute 240 and is dropped by 360-1320 exactly as a same-day 04:00 landing would be, and kept by 0-1080 exactly as a same-day one would be. Read each card's legs[].arrivalDayOffset to tell the two apart. | | `return-departure-blocks` | query | `midnight` \| `morning` \| `afternoon` \| `night`[] | no | items 1..*; each of `midnight`, `morning`, `afternoon`, `night` | Coarse local-time buckets for the RETURN leg's DEPARTURE: midnight 00:00-05:59, morning 06:00-11:59, afternoon 12:00-17:59, night 18:00-23:59, local to that airport. Repeat or comma-separate; OR'd together. The four buckets PARTITION the day, so listing all four returns every trip exactly once - wego.com's own buckets overlap at 06:00, 12:00 and 18:00 and these do not. They are wider than they sound, so prefer the matching -range param whenever the caller gave a hard edge. Applies to the RETURN leg (legs[1]) ONLY. A ONE-WAY search has no return leg, so any return-* bound judges a leg that does not exist and matches NOTHING - expect metadata.totalCandidates 0, which is the honest answer rather than a silently ignored filter. Judge the result on metadata.totalCandidates, never on the page. | | `return-departure-range` | query | string | no | pattern `^(?:\s*(?:\d{1,3}\|1[0-3]\d{2}\|14[0-2]\d\|143[0-9])\s*-\s*(?:\d{1,3}\|1[0-3]\d{2}\|14[0-2]\d\|143[0-9])\s*)?$` | Minute-of-day window `min-max` (each 0-1439, local to that airport), both ends inclusive. When min > max the window wraps past midnight, e.g. 1320-360 means 22:00-06:00. Bounds the RETURN leg's DEPARTURE, local to that leg's departure airport. Applies to the RETURN leg (legs[1]) ONLY. A ONE-WAY search has no return leg, so any return-* bound judges a leg that does not exist and matches NOTHING - expect metadata.totalCandidates 0, which is the honest answer rather than a silently ignored filter. Judge the result on metadata.totalCandidates, never on the page. | | `return-arrival-blocks` | query | `midnight` \| `morning` \| `afternoon` \| `night`[] | no | items 1..*; each of `midnight`, `morning`, `afternoon`, `night` | Coarse local-time buckets for when the RETURN leg LANDS, i.e. when the traveller gets home: midnight 00:00-05:59, morning 06:00-11:59, afternoon 12:00-17:59, night 18:00-23:59, local to that airport. Repeat or comma-separate; OR'd together. The four buckets PARTITION the day, so listing all four returns every trip exactly once - wego.com's own buckets overlap at 06:00, 12:00 and 18:00 and these do not. They are wider than they sound, so prefer the matching -range param whenever the caller gave a hard edge. Applies to the RETURN leg (legs[1]) ONLY. A ONE-WAY search has no return leg, so any return-* bound judges a leg that does not exist and matches NOTHING - expect metadata.totalCandidates 0, which is the honest answer rather than a silently ignored filter. Judge the result on metadata.totalCandidates, never on the page. | | `return-arrival-range` | query | string | no | pattern `^(?:\s*(?:\d{1,3}\|1[0-3]\d{2}\|14[0-2]\d\|143[0-9])\s*-\s*(?:\d{1,3}\|1[0-3]\d{2}\|14[0-2]\d\|143[0-9])\s*)?$` | Minute-of-day window `min-max` (each 0-1439, local to that airport), both ends inclusive. When min > max the window wraps past midnight, e.g. 1320-360 means 22:00-06:00. Bounds when the RETURN leg LANDS, local to the ARRIVAL airport - the "home before 22:00" bound. Judged on the clock alone, not the calendar; read legs[].arrivalDayOffset to tell a next-day landing apart. Applies to the RETURN leg (legs[1]) ONLY. A ONE-WAY search has no return leg, so any return-* bound judges a leg that does not exist and matches NOTHING - expect metadata.totalCandidates 0, which is the honest answer rather than a silently ignored filter. Judge the result on metadata.totalCandidates, never on the page. | | `outbound-min-duration` | query | string | no | pattern `^\d{1,7}$` | Minimum elapsed duration of the OUTBOUND leg in minutes (inclusive). Bounds ONE leg, unlike max-duration which bounds the whole trip. | | `outbound-max-duration` | query | string | no | pattern `^\d{1,7}$` | Maximum elapsed duration of the OUTBOUND leg in minutes (inclusive). Bounds ONE leg, unlike max-duration which bounds the whole trip: a 3h outbound paired with a 14h return passes ?outbound-max-duration=300 and no trip-wide ceiling can express that. | | `return-min-duration` | query | string | no | pattern `^\d{1,7}$` | Minimum elapsed duration of the RETURN leg in minutes (inclusive). Applies to the RETURN leg (legs[1]) ONLY. A ONE-WAY search has no return leg, so any return-* bound judges a leg that does not exist and matches NOTHING - expect metadata.totalCandidates 0, which is the honest answer rather than a silently ignored filter. Judge the result on metadata.totalCandidates, never on the page. | | `return-max-duration` | query | string | no | pattern `^\d{1,7}$` | Maximum elapsed duration of the RETURN leg in minutes (inclusive). Bounds ONE leg, unlike max-duration which bounds the whole trip. Applies to the RETURN leg (legs[1]) ONLY. A ONE-WAY search has no return leg, so any return-* bound judges a leg that does not exist and matches NOTHING - expect metadata.totalCandidates 0, which is the honest answer rather than a silently ignored filter. Judge the result on metadata.totalCandidates, never on the page. | | `booking-types` | query | `wego` \| `airline`[] | no | items 1..*; each of `wego`, `airline` | Booking types, repeat or comma-separate. OR'd together across the trip's fares – a trip matches if ANY of its fares has a listed kind (partner fares never match either value). | | `booking-sites` | query | string[] | no | items 1..* | Provider codes, repeat or comma-separate. OR'd together across the trip's fares – a trip matches if ANY of its fares comes from a listed provider. | | `stopover-airports` | query | string[] | no | items 1..* | Stopover airport IATA codes, repeat or comma-separate. OR'd together; a trip matches if ANY leg (outbound or return) stops over at any listed airport. | | `aircraft` | query | string[] | no | items 1..* | Aircraft type CODES, repeat or comma-separate (e.g. ?aircraft=380,789). OR'd together; a trip matches if ANY leg (outbound or return) flies any listed type. These are upstream's short equipment codes (380, 789, 32N), NOT the display labels the results card publishes (A380, B787-9, A320 Neo) – read metadata.filterOptions.aircraft for this snapshot's codes and the label beside each one. Several codes can share one label (321 and 32S are both A321), which is why the code is the filter key. Case-insensitive. NOT a fixed set; an unknown code matches nothing rather than failing the request. | | `currency` | query | string | no | pattern `^[A-Z]{3}$`; default `"USD"` | Pricing currency as a 3-letter ISO 4217 code (e.g. AED). Optional; defaults to USD server-side. Not inherited from the search: a search created in one currency reads back in USD unless you pass currency on every read, so re-send the search's currency to keep prices in it. | | `locale` | query | string | no | length 1..35; default `"en"` | Response language tag (e.g. en, ar). Optional; defaults to en server-side. Not inherited from the search – pass it on each read to keep results in that language. | | `view` | query | `card` | no | default `"card"` | Response projection. `card` is the only value: the lean results-list projection (cheapest-price summary + trip-level stops/duration + per-leg airline/aircraft/stopover, no fares[]). The former `default` projection was removed in issue #1308 – read GET /v1/flights/trips/{tripId} for a trip's fares and segments. | ## Responses | Status | Description | |---|---| | `200` | The current ranked-trip snapshot, as list cards. | | `400` | Invalid request body/query/path parameters. | | `401` | Missing or invalid bearer token. | | `404` | Unknown or expired search. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream flights service returned an invalid response. | | `503` | The flights service is temporarily unavailable (`upstream_unavailable`) or rate-limited upstream (`upstream_rate_limited`); retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `searchId` | string | yes | The id of the search this snapshot belongs to. | | `currencyCode` | string | yes | The currency the prices in this snapshot actually came back in, read off the fares themselves – so this, not metadata.currencyCode, is what a displayed number is denominated in. metadata.currencyCode reports what the read asked for and carries currencyCodeSource beside it; the two agree unless upstream declined to reprice into the requested currency. | | `metadata` | object | yes | Pagination, the snapshot's filter vocabulary, the settle counters for this read, and what it resolved currency and locale to. | | ↳ `page` | integer | yes | 1-based page number of this snapshot. | | ↳ `pageSize` | integer | yes | Trips requested per page. | | ↳ `resultCount` | integer | yes | Trips on this page. The page only – judge a filter on totalCandidates, not this. | | ↳ `totalCandidates` | integer | yes | Trips matching this read's filters across the whole snapshot – the count that judges a filter, never the page (results). Flights have no completion flag: settle on snapshotFareCount steady across two reads with snapshotTripCount above 0. | | ↳ `hasMore` | boolean | yes | Another page of trips follows. | | ↳ `filterOptions` | object | yes | The filter values this snapshot actually carries, ordered by count, over the same trips as snapshotTripCount. Codes are what the matching query param accepts, so pick from here rather than guessing: sending one listed code with no other filter makes metadata.totalCandidates equal that count exactly. It does NOT bound results, which stays the requested page, so compare against totalCandidates and not resultCount. Counts assume the default matching, so airlines-match=all or same-airline=true can keep fewer trips than the airlines count promises. count is trips, not legs or fares, and a trip is counted once however many of its legs or fares carry the value, including when only its return leg does. name is the display label: always present on bookingSites, where the provider code is its own fallback, present on airlines and stopoverAirports only when the snapshot dictionary resolves the code, and never present on alliances, which upstream gives no label. Still growing while the search aggregates, so judge an ABSENT code only once snapshotFareCount holds steady across two reads. | | ↳ `snapshotTripCount` | integer | yes | Renderable trips before filter/sort/page. 0 means upstream has produced none yet; above 0 beside an empty `results` means a filter or page range excluded everything. Settling needs `snapshotFareCount` steady across two reads AND this above 0. | | ↳ `snapshotFareCount` | integer | yes | Upstream progress counter, for cross-read comparison only. Runs ahead of the fares returned and stays non-zero over an empty page, so read `resultCount`/`totalCandidates` for display. Settled = equal non-zero across two reads with `snapshotTripCount` above 0. | | ↳ `createdAt` | string | no | When the upstream search was created (ISO 8601) – the freshness anchor for these prices. Absent when upstream omits it. | | ↳ `currencyCode` | string | yes | The currency this read ASKED upstream for, and the one every price on it is meant to be in. Read it beside currencyCodeSource before you show a number: a price computed in the wrong currency renders as a perfectly normal price, with no error and no odd shape to notice, so the response states which one rather than leaving it to be inferred. Where the operation also publishes a top-level currencyCode, that field reports the currency the prices actually came back in; the two agree unless upstream declined to reprice. | | ↳ `currencyCodeSource` | `explicit` \| `default` | yes | How the API resolved currencyCode: explicit (the caller sent currency – including a value equal to the default) or default (USD, no currency sent). A default here is the one signal that the request never carried the currency you meant. | | ↳ `locale` | string | yes | The language tag this read asked upstream for – what any localized text on it was resolved in (room and board names, airline and airport names, review prose). | | ↳ `localeSource` | `explicit` \| `default` | yes | How the API resolved locale: explicit (the caller sent locale – including a value equal to the default) or default (en, no locale sent). A default here explains text that came back in a language the caller did not ask for. | | `results` | object[] | yes | The requested page of ranked trips, as list cards. | | ↳ `tripId` | string | yes | Opaque trip id; read it back with GET /v1/flights/trips/{tripId}. | | ↳ `badges` | `best_value` \| `cheapest` \| `cheapest_direct`[] | yes | Every featured label that fits this trip. best_value ranks on the score of the trip's CHEAPEST fare, the same statistic sort=score_desc orders by; cheapest and cheapest_direct break a price tie by that score, then by leg-1 departure – cheapest keeps the LATER departure, cheapest_direct the EARLIER one. | | ↳ `stops` | number | yes | Trip-level stop count – the MAX across legs, the same value ?stops= filters on. Do not fold legs[] yourself. | | ↳ `durationMinutes` | number | yes | Trip-level duration – the SUM across legs. | | ↳ `price` | object | yes | Card price summary – the cheapest whole-party total, fee-inclusive; scope names the (party) figure. | | ↳ `legs` | object[] | yes | Per-leg summary for this trip, outbound first then return. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/flights/searches/$SEARCH_ID/results" ``` # Open one flight trip > Returns one trip's full itinerary and every fare on it (each kind-tagged), for the given tripId within its searchId. searchId is required – it comes from the search/results snapshot the tripId was read from. Source: https://docs.wego.com/api/flights/get-flight-trip/ · Markdown: https://docs.wego.com/api/flights/get-flight-trip/index.md `GET /v1/flights/trips/{tripId}` Returns one trip's full itinerary and every fare on it (each kind-tagged), for the given tripId within its searchId. searchId is required – it comes from the search/results snapshot the tripId was read from. Operation ID: `getFlightTrip` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `tripId` | path | string | yes | pattern `^[A-Za-z0-9._:~=-]{1,256}$` | The trip id from a search-results snapshot, shaped {searchId}:{tripCode}. It resolves only together with the searchId it came from, and both expire with the search. | | `searchId` | query | string | yes | pattern `^[A-Za-z0-9._:~=-]{1,256}$` | Required search context: the searchId the tripId was read from (it is the tripId's first :-segment). A trip id resolves only with its own search, and both expire together. | | `currency` | query | string | no | pattern `^[A-Z]{3}$`; default `"USD"` | Pricing currency as a 3-letter ISO 4217 code (e.g. AED). Optional; defaults to USD server-side. Not inherited from the search: a search created in one currency reads back in USD unless you pass currency on every read, so re-send the search's currency to keep prices in it. | | `locale` | query | string | no | length 1..35; default `"en"` | Response language tag (e.g. en, ar). Optional; defaults to en server-side. Not inherited from the search – pass it on each read to keep results in that language. | | `view` | query | `default` \| `detail` | no | default `"default"` | Response projection. default (agent shape): the full itinerary with every fare and per-flight segments. detail: the richer UI view-model (per-segment amenities, seat metadata, provider brand). Defaults to default. | ## Responses | Status | Description | |---|---| | `200` | The full itinerary + all fares for the trip (agent default) or, with ?view=detail, the per-segment detail projection. | | `400` | Invalid request body/query/path parameters. | | `401` | Missing or invalid bearer token. | | `404` | Unknown or expired trip. Ids are context-bound: a tripId resolves only with the searchId it came from, and retrying an expired one never recovers - create a new search and rethread. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream flights service returned an invalid response. | | `503` | The flights service is temporarily unavailable (`upstream_unavailable`) or rate-limited upstream (`upstream_rate_limited`); retry after the `Retry-After` seconds. | ### `200` body (variant 1 of 2) | Field | Type | Always present | Description | |---|---|---|---| | `tripId` | string | yes | Opaque trip id, shaped {searchId}:{tripCode}; read it back with GET /v1/flights/trips/{tripId}. | | `featured` | `best_value` \| `cheapest` \| `cheapest_direct` | no | The single highest-priority featured label, best_value > cheapest > cheapest_direct. best_value ranks on the score of the trip's CHEAPEST fare, the same statistic sort=score_desc orders by, so on a score_desc read it lands on the first result. cheapest and cheapest_direct break a price tie by that same score, then by leg-1 departure – cheapest keeps the LATER departure, cheapest_direct the EARLIER one. Under sort=score_desc the two picks are also MOVED toward positions 2 and 3, matching wego.com. Those positions are targets, not guarantees: a pick already at or above its target stays put, and cheapest_direct lands at position 2 when the first result is itself at the cheapest price. | | `stops` | number | yes | Trip-level stop count, the max across legs. | | `durationMinutes` | number | yes | Total trip duration in minutes, summed across legs. | | `outbound` | object | yes | The outbound leg. | | ↳ `from` | string | yes | Departure airport IATA code. | | ↳ `to` | string | yes | Arrival airport IATA code. | | ↳ `departsAt` | string | yes | Leg departure, ISO 8601 with offset. | | ↳ `arrivesAt` | string | yes | Leg arrival, ISO 8601 with offset. | | ↳ `durationMinutes` | number | yes | Total leg duration in minutes. | | ↳ `stops` | number | yes | Stops on this leg (0 is nonstop). | | ↳ `airlines` | string[] | yes | Raw IATA airline codes on the leg (unchanged). See airlinesDetail for display names. | | ↳ `airlinesDetail` | object[] | no | Index-aligned display names for `airlines`, resolved from the search snapshot's airline dictionary – the same join the results cards use; the bare code is the fallback on a dictionary miss. Omitted when the leg carries no airline codes. Additive – raw `airlines` codes are unaffected. | | ↳ `operatingAirlines` | object[] | no | Carriers that fly a segment of this leg they do not market. airlines / airlinesDetail name the MARKETING carriers only, so on a leg sold by one airline every code here is a carrier absent from the ticket; on an interline leg sold by two, one of these may also market a different segment. Read this before telling a traveller who they fly – mileage accrual, lounge access and baggage rules follow the operating carrier. Distinct codes, folded from this leg's own segments; pair it with segments[] to see which flight each one operates. PRESENT ONLY WHEN A SEGMENT PROVES A CODESHARE: absent means none was proven on this leg, never a promise that the marketing carrier operates every segment. | | ↳ `transportTypes` | `FLIGHT` \| `TRAIN` \| `BUS` \| `OTHER`[] | yes | Distinct transport modes across this leg's segments, in segment order. Always present and never empty: an all-flight leg reads ["FLIGHT"], so one read of this field replaces folding segments[] yourself. Anything else means part of this leg is not a plane, and segments[] says which part. | | ↳ `segments` | object[] | no | Per-segment identity (marketing/operating carrier, flight number, times, transport mode); omitted when upstream carries no segments for the leg. Previously reachable only via ?view=detail. | | `return` | object | no | The return leg; present only on a round trip. | | ↳ `from` | string | yes | Departure airport IATA code. | | ↳ `to` | string | yes | Arrival airport IATA code. | | ↳ `departsAt` | string | yes | Leg departure, ISO 8601 with offset. | | ↳ `arrivesAt` | string | yes | Leg arrival, ISO 8601 with offset. | | ↳ `durationMinutes` | number | yes | Total leg duration in minutes. | | ↳ `stops` | number | yes | Stops on this leg (0 is nonstop). | | ↳ `airlines` | string[] | yes | Raw IATA airline codes on the leg (unchanged). See airlinesDetail for display names. | | ↳ `airlinesDetail` | object[] | no | Index-aligned display names for `airlines`, resolved from the search snapshot's airline dictionary – the same join the results cards use; the bare code is the fallback on a dictionary miss. Omitted when the leg carries no airline codes. Additive – raw `airlines` codes are unaffected. | | ↳ `operatingAirlines` | object[] | no | Carriers that fly a segment of this leg they do not market. airlines / airlinesDetail name the MARKETING carriers only, so on a leg sold by one airline every code here is a carrier absent from the ticket; on an interline leg sold by two, one of these may also market a different segment. Read this before telling a traveller who they fly – mileage accrual, lounge access and baggage rules follow the operating carrier. Distinct codes, folded from this leg's own segments; pair it with segments[] to see which flight each one operates. PRESENT ONLY WHEN A SEGMENT PROVES A CODESHARE: absent means none was proven on this leg, never a promise that the marketing carrier operates every segment. | | ↳ `transportTypes` | `FLIGHT` \| `TRAIN` \| `BUS` \| `OTHER`[] | yes | Distinct transport modes across this leg's segments, in segment order. Always present and never empty: an all-flight leg reads ["FLIGHT"], so one read of this field replaces folding segments[] yourself. Anything else means part of this leg is not a plane, and segments[] says which part. | | ↳ `segments` | object[] | no | Per-segment identity (marketing/operating carrier, flight number, times, transport mode); omitted when upstream carries no segments for the leg. Previously reachable only via ?view=detail. | | `fares` | object[] | yes | Bookable fares for this trip, cheapest-first. | | ↳ `kind` | `wego` \| `airline` \| `partner` | yes | Fare source: wego (Book-on-Wego), airline (booked with the carrier) or partner (an OTA). | | ↳ `fareId` | string | yes | Opaque fare id; pass it to the fare-options and booking-link routes. | | ↳ `providerCode` | string | yes | Booking provider code (the OTA or airline selling this fare). | | ↳ `providerName` | string | yes | Booking provider display name. | | ↳ `price` | object | yes | Fare price. total/totalUsd are the whole-party amount, fee-inclusive, and are the authoritative figure for this fare. Search-time fares carry no per-passenger breakdown. | | ↳ `refundable` | boolean | yes | Whether this fare is refundable, as the provider states it. | | ↳ `hasFareOptions` | boolean | yes | Whether GET /v1/flights/fares/{fareId}/options can expand this fare into branded options. | | ↳ `handoffUrl` | string | yes | Deep link that hands the booking off to the provider or wego.com checkout for this fare. | | `metadata` | object | yes | What this read resolved currency and locale to, and how each was decided. | | ↳ `currencyCode` | string | yes | The currency this read ASKED upstream for, and the one every price on it is meant to be in. Read it beside currencyCodeSource before you show a number: a price computed in the wrong currency renders as a perfectly normal price, with no error and no odd shape to notice, so the response states which one rather than leaving it to be inferred. Where the operation also publishes a top-level currencyCode, that field reports the currency the prices actually came back in; the two agree unless upstream declined to reprice. | | ↳ `currencyCodeSource` | `explicit` \| `default` | yes | How the API resolved currencyCode: explicit (the caller sent currency – including a value equal to the default) or default (USD, no currency sent). A default here is the one signal that the request never carried the currency you meant. | | ↳ `locale` | string | yes | The language tag this read asked upstream for – what any localized text on it was resolved in (room and board names, airline and airport names, review prose). | | ↳ `localeSource` | `explicit` \| `default` | yes | How the API resolved locale: explicit (the caller sent locale – including a value equal to the default) or default (en, no locale sent). A default here explains text that came back in a language the caller did not ask for. | ### `200` body (variant 2 of 2) | Field | Type | Always present | Description | |---|---|---|---| | `tripId` | string | yes | Opaque trip id, shaped {searchId}:{tripCode}; read it back with GET /v1/flights/trips/{tripId}. | | `stops` | number | yes | Trip-level stop count, the max across legs. | | `durationMinutes` | number | yes | Total trip duration in minutes, summed across legs. | | `legs` | object[] | yes | The trip's legs with per-segment detail, outbound first. | | ↳ `direction` | `depart` \| `return` | yes | Which leg this is: depart or return. | | ↳ `from` | object | yes | Departure airport for this leg. | | ↳ `to` | object | yes | Arrival airport for this leg. | | ↳ `departsAt` | string | yes | Leg departure, ISO 8601 with offset. | | ↳ `arrivesAt` | string | yes | Leg arrival, ISO 8601 with offset. | | ↳ `arrivalDayOffset` | number | yes | Calendar days the arrival lands after departure (the +1 badge). | | ↳ `overnight` | boolean | yes | The leg spans a night. | | ↳ `durationMinutes` | number | yes | Leg duration in minutes. | | ↳ `stops` | number | yes | Stops on this leg (0 is nonstop). | | ↳ `layoverMinutes` | number | no | Total layover across the leg's stops, minutes. | | ↳ `segments` | object[] | yes | The individual flights that make up this leg. | | `fares` | object[] | yes | Bookable fares for this trip, cheapest-first. | | ↳ `kind` | `wego` \| `airline` \| `partner` | yes | Fare source: wego (Book-on-Wego), airline (booked with the carrier) or partner (an OTA). | | ↳ `fareId` | string | yes | Opaque fare id; pass it to the fare-options and booking-link routes. | | ↳ `provider` | object | yes | A booking provider resolved to code, name, logo and brand color. | | ↳ `price` | object | yes | Fare price for the whole party, fee-inclusive. | | ↳ `baggage` | object | no | Baggage allowance for this fare, when the provider states it. | | ↳ `refundable` | boolean | yes | Whether this fare is refundable, as the provider states it. | | ↳ `hasFareOptions` | boolean | yes | Whether GET /v1/flights/fares/{fareId}/options can expand this fare into branded options. | | ↳ `handoffUrl` | string | yes | Deep link to the provider or wego.com checkout for this fare. | | `metadata` | object | yes | What this read resolved currency and locale to, and how each was decided. | | ↳ `currencyCode` | string | yes | The currency this read ASKED upstream for, and the one every price on it is meant to be in. Read it beside currencyCodeSource before you show a number: a price computed in the wrong currency renders as a perfectly normal price, with no error and no odd shape to notice, so the response states which one rather than leaving it to be inferred. Where the operation also publishes a top-level currencyCode, that field reports the currency the prices actually came back in; the two agree unless upstream declined to reprice. | | ↳ `currencyCodeSource` | `explicit` \| `default` | yes | How the API resolved currencyCode: explicit (the caller sent currency – including a value equal to the default) or default (USD, no currency sent). A default here is the one signal that the request never carried the currency you meant. | | ↳ `locale` | string | yes | The language tag this read asked upstream for – what any localized text on it was resolved in (room and board names, airline and airport names, review prose). | | ↳ `localeSource` | `explicit` \| `default` | yes | How the API resolved locale: explicit (the caller sent locale – including a value equal to the default) or default (en, no locale sent). A default here explains text that came back in a language the caller did not ask for. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/flights/trips/$TRIP_ID?searchId=$SEARCH_ID" ``` # Read a trip's experience signals > Per-leg signals for what a journey is like to sit through: overnight, longStopover, earlyDeparture, lateArrival, plus positive-only witnesses for a tight connection, aircraft age and carrier rating. Source: https://docs.wego.com/api/flights/get-trip-experience/ · Markdown: https://docs.wego.com/api/flights/get-trip-experience/index.md `GET /v1/flights/trips/{tripId}/experience` Per-leg signals for what a journey is like to sit through: overnight, longStopover, earlyDeparture, lateArrival, plus positive-only witnesses for a tight connection, aircraft age and carrier rating. No score - Recommended sort ranks on the price-adjusted fare score, which one would not match. Operation ID: `getTripExperience` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `tripId` | path | string | yes | pattern `^[A-Za-z0-9._~=-]+:[A-Za-z0-9._:~=-]+$`; length *..256 | The trip from a flight-search result. Shaped {searchId}:{tripCode}. | | `searchId` | query | string | no | pattern `^[A-Za-z0-9._:~=-]{1,256}$` | Optional cross-check. When supplied it must equal the tripId's first :-segment; a mismatch is rejected. | ## Responses | Status | Description | |---|---| | `200` | The trip's per-leg signals. A witness field that is absent was not asserted; it is not a negative. | | `400` | Invalid request body/query/path parameters. | | `401` | Missing or invalid bearer token. | | `404` | Unknown trip, or its search has expired. Search again and re-open the trip - retrying the same id never recovers. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream flights service returned an invalid response. | | `503` | The flights service is temporarily unavailable (`upstream_unavailable`) or rate-limited upstream (`upstream_rate_limited`); retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `tripId` | string | yes | The trip these signals are for. | | `legs` | object[] | yes | Per-leg comfort signals, in itinerary order (outbound first). | | ↳ `id` | string | yes | The upstream leg id, e.g. `SIN-BKK:TR638~3:0`. | | ↳ `departureAirportCode` | string | yes | Departure airport IATA code. | | ↳ `arrivalAirportCode` | string | yes | Arrival airport IATA code. | | ↳ `stopsCount` | integer | yes | Stopovers on this leg – what makes an absent shortStopover readable. | | ↳ `signals` | object | yes | Per-leg comfort signals. newAircraft and highlyRatedCarrier are positive-only witnesses: present only when upstream asserts them, absent otherwise; absent is never a negative. | | `metadata` | object | yes | Trip-level experience metadata. | | ↳ `legCount` | integer | yes | Legs on this trip: 1 one-way, 2 a return. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/flights/trips/$TRIP_ID/experience" ``` # Hotels > The hotel funnel, same shape as flights: `createHotelSearch`, `getHotelSearchResults` for ranked hotels, `getHotel` for static detail, `getHotelRates` for bookable rooms and rates (cheapest first, with board and refundability), `getHotelRateBookingLink` for the wego.com checkout URL. Source: https://docs.wego.com/api/hotels/ · Markdown: https://docs.wego.com/api/hotels/index.md The hotel funnel, same shape as flights: `createHotelSearch`, `getHotelSearchResults` for ranked hotels, `getHotel` for static detail, `getHotelRates` for bookable rooms and rates (cheapest first, with board and refundability), `getHotelRateBookingLink` for the wego.com checkout URL. ## Operations | Verb | Operation | Carries onward | |---|---|---| | `POST` | [Create a hotel search](/api/hotels/create-hotel-search) · `/v1/hotels/searches` | `searchId` | | `GET` | [Read hotel search results](/api/hotels/get-hotel-search-results) · `/v1/hotels/searches/{searchId}/results` | `hotelId` | | `GET` | [Build a hotel booking link](/api/hotels/get-hotel-rate-booking-link) · `/v1/hotels/{hotelId}/rates/{rateId}/booking-link` | `wego.com URL` | | `GET` | [Build a durable wego.com hotel search link](/api/hotels/get-hotel-search-link) · `/v1/hotels/search-link` | `wego.com URL` | | `GET` | [List a hotel's rooms & rates](/api/hotels/get-hotel-rates) · `/v1/hotels/{hotelId}/rates` | `rateId` | | `GET` | [Search a hotel's guest reviews](/api/hotels/get-hotel-reviews) · `/v1/hotels/{hotelId}/reviews` | `terminal` | | `GET` | [Get hotel detail](/api/hotels/get-hotel) · `/v1/hotels/{hotelId}` | `terminal` | # Create a hotel search > Creates a Book-on-Wego hotel search (city, single hotel, or geo point) and returns its opaque searchId plus the occupancy priced upstream (resolved child ages, incl. the age-8 fallback when none supplied). Source: https://docs.wego.com/api/hotels/create-hotel-search/ · Markdown: https://docs.wego.com/api/hotels/create-hotel-search/index.md `POST /v1/hotels/searches` Creates a Book-on-Wego hotel search (city, single hotel, or geo point) and returns its opaque searchId plus the occupancy priced upstream (resolved child ages, incl. the age-8 fallback when none supplied). Poll /results for ranked hotels. A hotelId search is the only one getHotelRates accepts. Operation ID: `createHotelSearch` ## Request body `application/json` | Field | Type | Required | Constraints | Description | |---|---|---|---|---| | `cityCode` | string | no | pattern `^[A-Z]{3}$` | City code to search. One destination only, see oneOf. | | `hotelId` | integer | no | 1..9007199254740991 | Search a single hotel by id. One destination only, see oneOf. | | `lat` | number | no | -90..90 | Latitude. Must be paired with lng. | | `lng` | number | no | -180..180 | Longitude. Must be paired with lat. | | `radius` | number | no | 1..50; default `10` | Search radius in km around lat/lng. | | `checkIn` | string | yes | pattern `^\d{4}-\d{2}-\d{2}$` | Check-in date, YYYY-MM-DD. Not in the past. | | `checkOut` | string | yes | pattern `^\d{4}-\d{2}-\d{2}$` | Check-out date, YYYY-MM-DD. Must be after checkIn. | | `adults` | integer | no | 1..9; default `2` | Adults across the search (1-9). Defaults to 2, since a room sleeps two. Note the flight search defaults adults to 1. | | `children` | integer | no | 0..8; default `0` | Children across the search (0-8). Defaults to 0. | | `rooms` | integer | no | 1..4; default `1` | Rooms to price (1-4). Defaults to 1; cannot exceed adults. | | `childrenAges` | integer[] | no | items *..8 | Per-child ages (integers 0–17). When provided, the count must equal `children`. When omitted, each child is priced at age 8 (the documented fallback). | | `currency` | string | no | pattern `^[A-Z]{3}$`; default `"USD"` | Pricing currency as a 3-letter ISO 4217 code. Defaults to USD. | | `locale` | string | no | length 1..35; default `"en"` | Response language tag (e.g. en, ar). Defaults to en. | | `siteCode` | string | no | pattern `^[A-Z]{2}$` | Wego market (point of sale) as a 2-letter code, e.g. AE. Optional: if omitted the API defaults to US. A client that knows the user's market (the wego CLI derives it from the id_token) passes it as an explicit siteCode. | Provide exactly one of: `cityCode`, `hotelId`, `lat` + `lng`. ## Responses | Status | Description | |---|---| | `201` | Search created. | | `400` | Invalid request parameters. | | `401` | Missing or invalid bearer token. | | `404` | Unknown hotel. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream hotels service returned an invalid response. | | `503` | The hotels service is unavailable (`upstream_unavailable`) or rate-limited upstream (`upstream_rate_limited`); retry after the `Retry-After` seconds. | ### `201` body | Field | Type | Always present | Description | |---|---|---|---| | `searchId` | string | yes | Opaque id for the created search. | | `occupancy` | object | yes | The occupancy priced upstream for this search (ages resolved, incl. fallback). | | ↳ `adults` | integer | yes | Adults priced upstream for this search. | | ↳ `childrenAges` | integer[] | yes | Resolved per-child ages actually sent upstream (age-8 fallback when omitted). | | ↳ `rooms` | integer | yes | Rooms priced upstream for this search. | | `siteCode` | string | yes | The site code (Wego market) the search was created for. | | `siteCodeSource` | `explicit` \| `default` | yes | How the API resolved siteCode: explicit (caller-supplied – including a market a client derived and passed) or default (US, no site supplied). | ## Example ```bash CHECK_IN=$(date -u -v+60d +%F 2>/dev/null || date -u -d '+60 days' +%F) CHECK_OUT=$(date -u -v+67d +%F 2>/dev/null || date -u -d '+67 days' +%F) curl -s -X POST -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"checkIn":"'"$CHECK_IN"'","checkOut":"'"$CHECK_OUT"'","cityCode":"DXB"}' \ "https://api.wego.com/v1/hotels/searches" ``` # Build a hotel booking link > Returns the wego.com checkout URL for a chosen rate. Source: https://docs.wego.com/api/hotels/get-hotel-rate-booking-link/ · Markdown: https://docs.wego.com/api/hotels/get-hotel-rate-booking-link/index.md `GET /v1/hotels/{hotelId}/rates/{rateId}/booking-link` Returns the wego.com checkout URL for a chosen rate. Pure build: no upstream call, no booking, no payment – only 400/401/429. Operation ID: `getHotelRateBookingLink` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `hotelId` | path | integer | yes | 1..9007199254740991 | The hotel's numeric id (a positive integer), as carried by hotel search results (results[].hotelId) and embedded in a rate id. | | `rateId` | path | string | yes | - | The rate's composed booking reference from GET /v1/hotels/{hotelId}/rates (rates[].id), forwarded verbatim. Grammar: {searchId}:hotels.wego.com:{hotelId}:{hash}:{idx} – checkout derives the search from the first segment, which is why the searchId query param is optional here. Opaque: do not construct or reorder it. | | `searchId` | query | string | no | pattern `^[A-Za-z0-9._:~=-]{1,256}$` | Optional. The search the rate belongs to. When omitted it defaults to the rate id's first segment (exactly how the checkout page recovers it), so you rarely need to send it; when sent it must equal that segment or the request is rejected 400. | | `locale` | query | string | no | length 1..35; default `"en"` | Checkout page language tag (e.g. en, ar). Defaults to en. | | `guests` | query | string | no | pattern `^[1-9]\d*(:\d+)*$`; length *..64 | Optional. The adults and child ages to put on the checkout URL, written as the adult count then one age per child: 2:4:9 is 2 adults with children aged 4 and 9. It does not change what checkout charges, which comes from the search the rate id names, so a different value here, or none at all, opens the same stay at the same price. | | `countryCode` | query | string | no | pattern `^[A-Z]{2}$` | Optional. An ISO 3166-1 alpha-2 country code to put on the checkout URL, such as AE. On wego.com this names the country of the place searched, not the traveller's. It does not choose which wego.com site the link opens (that is siteCode), and it does not change the price: the checkout page asks the traveller for their nationality on its own form. | | `siteCode` | query | string | no | pattern `^[A-Z]{2}$` | Wego market (point of sale) as a 2-letter code, e.g. AE; defaults to US. It selects the wego.com CHECKOUT HOST/domain – distinct from countryCode, which is copied onto the link and changes neither the host nor the price. | ## Responses | Status | Description | |---|---| | `200` | The checkout URL, and the fact that it expires. | | `400` | Invalid request parameters. | | `401` | Missing or invalid bearer token. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `bookingUrl` | string | yes | The wego.com hotel checkout URL for the rate. | | `expires` | boolean | yes | Always true: this link is search-scoped and stops working when the rate's search expires. A stale link loads an empty checkout page rather than erroring, so treat it as short-lived and re-price the rate to get a fresh one. To send someone a link that lasts, use the hotel's pageUrl. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/hotels/$HOTEL_ID/rates/$RATE_ID/booking-link" ``` # List a hotel's rooms & rates > Returns the Book-on-Wego rooms & rates for a hotel (cheapest-first): room name, board, refundability, price, and each rate's composed booking reference id. searchId must name a hotel-scoped search (one created with hotelId); a city or geo search is a 409. Source: https://docs.wego.com/api/hotels/get-hotel-rates/ · Markdown: https://docs.wego.com/api/hotels/get-hotel-rates/index.md `GET /v1/hotels/{hotelId}/rates` Returns the Book-on-Wego rooms & rates for a hotel (cheapest-first): room name, board, refundability, price, and each rate's composed booking reference id. searchId must name a hotel-scoped search (one created with hotelId); a city or geo search is a 409. Operation ID: `getHotelRates` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `hotelId` | path | integer | yes | 1..9007199254740991 | The hotel's numeric id (a positive integer), as carried by hotel search results (results[].hotelId) and embedded in a rate id. | | `searchId` | query | string | yes | pattern `^[A-Za-z0-9._:~=-]{1,256}$` | The search the hotel was found in (from createHotelSearch); rates are priced within that search context. Required. | | `currency` | query | string | no | pattern `^[A-Z]{3}$`; default `"USD"` | Pricing currency as a 3-letter ISO 4217 code (e.g. AED). Defaults to USD. | | `locale` | query | string | no | length 1..35; default `"en"` | Response language tag (e.g. en, ar). Defaults to en. | ## Responses | Status | Description | |---|---| | `200` | Cheapest-first Book-on-Wego rates. | | `400` | Invalid request parameters. | | `401` | Missing or invalid bearer token. | | `404` | Unknown hotel, or unknown/expired search. | | `409` | The searchId names a city or geo search, which never holds a hotel's full rate list. Create a hotel-scoped search (createHotelSearch with hotelId) and read its rates. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream hotels service returned an invalid response. | | `503` | The hotels service is unavailable (`upstream_unavailable`) or rate-limited upstream (`upstream_rate_limited`); retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `hotelId` | number | yes | The hotel these rates are for. | | `searchId` | string | yes | The search these rates were priced within. | | `currencyCode` | string | yes | The currency every rate price on this read was computed in – the same value as metadata.currencyCode, which carries currencyCodeSource beside it. Read it before you show a number: a price computed in the wrong currency renders as a perfectly normal price. | | `searchComplete` | boolean | yes | Advisory: true means upstream reports it finished aggregating rates for this search. It is not a guarantee that the list on this page is final, so do not block on it. | | `stay` | object | no | What upstream priced: the dates and occupancy every price on this read covers. Present when upstream states them. | | ↳ `checkIn` | string | yes | Check-in date priced upstream, YYYY-MM-DD. | | ↳ `checkOut` | string | yes | Check-out date priced upstream, YYYY-MM-DD. | | ↳ `nights` | integer | yes | Nights between checkIn and checkOut. price.total covers this many nights of price.amountPerNight. | | ↳ `occupancy` | object | yes | The occupancy priced upstream for this search (ages resolved, incl. fallback). | | `rates` | object[] | yes | Bookable rates for the hotel in this search. | | ↳ `id` | string | yes | Composed booking reference (opaque passthrough); pass it to GET /v1/hotels/{hotelId}/rates/{rateId}/booking-link. | | ↳ `roomName` | string | yes | Room type name, e.g. Deluxe King. | | ↳ `board` | string | no | Board basis, normalized to lower_snake_case (e.g. room_only, breakfast_included). Absent when the provider states none. | | ↳ `refundable` | boolean | yes | True when this rate carries a refundable or free-cancellation code. Authoritative on this endpoint, unlike the results card's refundable witness. | | ↳ `cancellationPolicy` | string | no | Coarse policy derived from the refundability codes: free_cancellation or non_refundable. | | ↳ `price` | object | yes | A rate's pricing. Every amount covers the whole booking, all rooms in the search, so quote these figures as they stand. Upstream rounds per booking, so total is the exact stay figure and amountPerNight is one night of it. All amounts exclude totalLocalTax, which wego.com adds to the displayed price. | | ↳ `roomsLeft` | number | no | Rooms remaining at this rate, when the provider reports scarcity; absent otherwise. | | ↳ `images` | string[] | no | Room image URLs, when the provider supplies them. | | `metadata` | object | yes | What this read resolved currency and locale to, and how each was decided. | | ↳ `currencyCode` | string | yes | The currency this read ASKED upstream for, and the one every price on it is meant to be in. Read it beside currencyCodeSource before you show a number: a price computed in the wrong currency renders as a perfectly normal price, with no error and no odd shape to notice, so the response states which one rather than leaving it to be inferred. Where the operation also publishes a top-level currencyCode, that field reports the currency the prices actually came back in; the two agree unless upstream declined to reprice. | | ↳ `currencyCodeSource` | `explicit` \| `default` | yes | How the API resolved currencyCode: explicit (the caller sent currency – including a value equal to the default) or default (USD, no currency sent). A default here is the one signal that the request never carried the currency you meant. | | ↳ `locale` | string | yes | The language tag this read asked upstream for – what any localized text on it was resolved in (room and board names, airline and airport names, review prose). | | ↳ `localeSource` | `explicit` \| `default` | yes | How the API resolved locale: explicit (the caller sent locale – including a value equal to the default) or default (en, no locale sent). A default here explains text that came back in a language the caller did not ask for. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/hotels/$HOTEL_ID/rates?searchId=$SEARCH_ID" ``` # Search a hotel's guest reviews > Guest reviews for a hotel, newest first: rating, pros, cons and the provider. Source: https://docs.wego.com/api/hotels/get-hotel-reviews/ · Markdown: https://docs.wego.com/api/hotels/get-hotel-reviews/index.md `GET /v1/hotels/{hotelId}/reviews` Guest reviews for a hotel, newest first: rating, pros, cons and the provider. Filter by topic with ?topics=breakfast,pool and by cohort with ?guest-type=. Quote a review against metadata.totalCandidates, and cite metadata.matchedTerms for the word actually matched. Operation ID: `getHotelReviews` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `hotelId` | path | integer | yes | 1..9007199254740991 | The hotel's numeric id (a positive integer), as carried by hotel search results (results[].hotelId) and embedded in a rate id. | | `page` | query | integer | no | 1..100; default `1` | Page number, 1-based (max 100). Defaults to 1. | | `pageSize` | query | integer | no | 1..50; default `10` | Reviews per page (1-50). Defaults to 10. | | `sort` | query | `posted_at_desc` \| `rating_desc` \| `rating_asc` | no | default `"posted_at_desc"` | Sort order. posted_at_desc (default, newest first) – a review corpus answers 'what is it like now', so recency opens; rating_desc / rating_asc sort by the provider's rating. | | `locale` | query | string | no | length 1..35; default `"en"` | Response language tag (e.g. en, ar). Defaults to en. | | `topics` | query | string[] | no | items 1..* | Optional topic terms to filter reviews by (repeat or comma-separate, OR'd together) – e.g. ?topics=breakfast,pool keeps reviews mentioning either. metadata.matchedTerms reports the variants actually matched (breakfast, Breakfast). | | `guest-type` | query | `couple` \| `family_with_children` \| `solo_traveller` \| `extended_group` | no | - | Optional reviewer-cohort filter: couple, family_with_children, solo_traveller or extended_group. | | `view` | query | `default` \| `detail` | no | default `"default"` | Response projection. default: rating, title, pros, cons, provider. detail: adds the reviewer's country code and neutral prose notes. Defaults to default. | ## Responses | Status | Description | |---|---| | `200` | One page of guest reviews. | | `400` | Invalid request parameters. | | `401` | Missing or invalid bearer token. | | `404` | Unknown hotel. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream hotels service returned an invalid response. | | `503` | The hotels service is unavailable (`upstream_unavailable`) or rate-limited upstream (`upstream_rate_limited`); retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `hotelId` | number | yes | The hotel these reviews are for. | | `metadata` | object | yes | Pagination and the matched-topic accounting for this reviews read. | | ↳ `page` | integer | yes | 1-based page number of this read. | | ↳ `pageSize` | integer | yes | Reviews requested per page. | | ↳ `resultCount` | integer | yes | Reviews on this page. | | ↳ `totalCandidates` | integer | no | Reviews matching this read's filters across ALL pages – the denominator to quote a review against ('15 of 141'). It is the FILTERED total, so an unfiltered read is needed to state the hotel's full review count. EXACT only when hasMore is false: when hasMore is true this can be a LOWER BOUND, because an upstream page that carries no count of its own falls back to the offset plus the rows it sent, so quote it as 'at least N'. ABSENT when this read establishes no total at all – an empty page past the first whose upstream sent no count says nothing about the pages before it. Absent means UNKNOWN, never zero: re-read page 1 before reporting any number. | | ↳ `hasMore` | boolean | yes | Another page may follow. A full page always sets this, count or no count. While it is true, read totalCandidates as a floor rather than a corpus size. | | ↳ `topics` | string[] | yes | The topic terms this read asked for. | | ↳ `matchedTerms` | string[] | yes | The term variants the upstream actually matched (e.g. breakfast, Breakfast). Empty on an unfiltered read. Cite from here rather than from topics, so a quote states the word that was really found. | | `results` | object[] | yes | The requested page of guest reviews. | | ↳ `rating` | number | yes | The provider's own 0–10 rating for this review, passed through. | | ↳ `title` | string | no | Review title, when the guest gave one. | | ↳ `postedAt` | string | yes | Calendar date (YYYY-MM-DD). | | ↳ `providerCode` | string | yes | Which provider collected the review (e.g. booking.com). | | ↳ `guestType` | `couple` \| `family_with_children` \| `solo_traveller` \| `extended_group` | no | The reviewer's cohort, normalized onto the closed set. Omitted when the upstream sent a value outside it. | | ↳ `pros` | string[] | yes | What the guest liked, verbatim. | | ↳ `cons` | string[] | yes | What the guest disliked, verbatim. | | ↳ `notes` | string[] | no | view=detail only – review prose the provider tagged neither positive nor negative. | | ↳ `countryCode` | string | no | view=detail only – the reviewer's country code. The reviewer's name is never returned. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/hotels/$HOTEL_ID/reviews" ``` # Build a durable wego.com hotel search link > Builds a shareable wego.com hotel-search URL from the caller's own city, dates and occupancy. Source: https://docs.wego.com/api/hotels/get-hotel-search-link/ · Markdown: https://docs.wego.com/api/hotels/get-hotel-search-link/index.md `GET /v1/hotels/search-link` Builds a shareable wego.com hotel-search URL from the caller's own city, dates and occupancy. A pure, stateless string build: no search created. It carries no search-scoped id, so it does not expire: whoever opens it runs the search live. Operation ID: `getHotelSearchLink` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `cityCode` | query | string | yes | pattern `^[A-Z]{3}$` | City code the link searches, e.g. BKK. Take it from a places result's code or cityCode, never its numeric id. A lat/lng pair cannot be shared: wego.com serves no coordinate search URL. To link one hotel instead of a search, use the pageUrl that hotel carries. | | `checkIn` | query | string | yes | pattern `^\d{4}-\d{2}-\d{2}$` | Check-in date, YYYY-MM-DD. Not in the past. | | `checkOut` | query | string | yes | pattern `^\d{4}-\d{2}-\d{2}$` | Check-out date, YYYY-MM-DD. Must be after checkIn. | | `adults` | query | integer | no | 1..9; default `2` | Adults across the link's rooms (1-9). Defaults to 2. | | `children` | query | integer | no | 0..8; default `0` | Children across the link's rooms (0-8). Defaults to 0. Sending more than 0 requires childrenAges. | | `childrenAges` | query | string | no | pattern `^\d{1,2}(,\d{1,2})*$` | Per-child ages as a comma-separated list of integers 0-17, e.g. 5,9. The count must equal children, and it is required whenever children is above 0: the create body prices a missing age at 8, and a durable link would show that guess to a recipient who cannot correct it. | | `rooms` | query | integer | no | 1..4; default `1` | Rooms the link asks for (1-4). Defaults to 1, and cannot be more than adults. Guests spread evenly and fill the earlier rooms first, so 3 adults in 2 rooms give 2 then 1, matching the way a search prices the same stay. | | `currency` | query | string | no | pattern `^[A-Z]{3}$` | Optional pricing currency as a 3-letter ISO 4217 code. When omitted the page prices in whatever the recipient's own session uses. | | `locale` | query | string | no | length 1..35; default `"en"` | Page language tag (e.g. en, ar). Defaults to en. | | `siteCode` | query | string | no | pattern `^[A-Z]{2}$` | Wego market (point of sale) as a 2-letter code, e.g. AE; defaults to US. It selects the wego.com host the link points at. | ## Responses | Status | Description | |---|---| | `200` | The durable wego.com hotel-search URL. | | `400` | Invalid query parameters. checkIn must be a real calendar date and not in the past; checkOut must be after it; rooms is 1-4 and cannot be more than adults; childrenAges is required when children is above 0 and must have exactly that many entries. | | `401` | Missing or invalid bearer token. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `searchUrl` | string | yes | A wego.com hotel-search URL for this city, dates and occupancy. Opening it runs the search live. | | `expires` | boolean | yes | Always false: the URL carries no search-scoped id, so it keeps working. The prices behind it are whatever a live search returns when it is opened. | ## Example ```bash CHECK_IN=$(date -u -v+60d +%F 2>/dev/null || date -u -d '+60 days' +%F) CHECK_OUT=$(date -u -v+67d +%F 2>/dev/null || date -u -d '+67 days' +%F) curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/hotels/search-link?cityCode=BKK&checkIn=$CHECK_IN&checkOut=$CHECK_OUT" ``` # Read hotel search results > Lean list cards; default 10, max 50 per page. searchComplete:true terminal, false advisory; poll snapshotCandidateCount to a steady non-zero. totalCandidates===0 = filters only if totalBeforeFilters>0, else none bookable once complete. ?refundable=true = witnessed. Source: https://docs.wego.com/api/hotels/get-hotel-search-results/ · Markdown: https://docs.wego.com/api/hotels/get-hotel-search-results/index.md `GET /v1/hotels/searches/{searchId}/results` Lean list cards; default 10, max 50 per page. searchComplete:true terminal, false advisory; poll snapshotCandidateCount to a steady non-zero. totalCandidates===0 = filters only if totalBeforeFilters>0, else none bookable once complete. ?refundable=true = witnessed. Operation ID: `getHotelSearchResults` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `searchId` | path | string | yes | pattern `^[A-Za-z0-9._:~=-]{1,256}$` | The opaque searchId returned by createHotelSearch. Ids expire; a 404 means the search is unknown or gone – create a new one. | | `page` | query | integer | no | 1..100; default `1` | Page number, 1-based (max 100). Defaults to 1. | | `pageSize` | query | integer | no | 1..50; default `10` | Results per page (1-50). Defaults to 10. | | `sort` | query | `relevance` \| `price_asc` \| `price_desc` \| `star_desc` \| `review_score_desc` \| `guest_rating_desc` \| `distance_asc` | no | default `"relevance"` | Sort order. relevance (default) is the metasearch ranking; price_asc / price_desc by cheapest per-night rate; star_desc by star; review_score_desc by guest score; guest_rating_desc by the ?guest-type= cohort's own score, which requires that param (a hotel upstream did not score for the cohort sorts last, never zero-filled); distance_asc by distance to the city's place-record coordinate (see distanceToCityCentre). With ?refundable=true the price sorts key off the cheapest refundable rate. | | `currency` | query | string | no | pattern `^[A-Z]{3}$`; default `"USD"` | Pricing currency as a 3-letter ISO 4217 code (e.g. AED). Defaults to USD. | | `locale` | query | string | no | length 1..35; default `"en"` | Response language tag (e.g. en, ar). Defaults to en. | | `min-star` | query | integer | no | 1..5 | Keep hotels with at least this star rating (1-5). | | `max-star` | query | integer | no | 1..5 | Keep hotels with at most this star rating (1-5). | | `min-review-score` | query | number | no | 0..10 | Keep hotels whose ALL-GUESTS review score is at least this (0-10). This is the everybody-rated-it-well question; for a named guest cohort use ?guest-type= with ?min-guest-rating=, which is a different number on most hotels. | | `guest-type` | query | `business` \| `couple` \| `family` \| `solo` | no | - | The guest cohort ?min-guest-rating= and sort=guest_rating_desc judge a hotel by: business, couple, family or solo. Read the vocabulary and this snapshot's per-cohort hotel counts from metadata.filterOptions.guestTypes. Must be sent with ?min-guest-rating= or sort=guest_rating_desc, and both of those require it - a cohort with nothing to apply it to is rejected rather than silently ignored. Spelled differently from the /reviews ?guest-type= vocabulary (family here, family_with_children there) because the two upstreams segment guests differently; business exists only here and extended_group only there. | | `min-guest-rating` | query | number | no | 0..10 | Keep hotels the ?guest-type= cohort rates at least this (0-10). Requires ?guest-type=. Judged on that cohort's own score, not the all-guests one: on a settled 420-hotel snapshot, of the 299 hotels scored for both, 58% had a family score at least 3 points from their overall one. A hotel upstream did not score for the cohort is DROPPED, and that absence means too little cohort data rather than a low score - upstream publishes no thin cohort rows, so a cohort score it does publish rests on more reviews than the all-guests figure sometimes does. | | `min-price` | query | number | no | 0..1000000 | Minimum price (inclusive), in the response currency. Bounds the all-in nightly figure: amountPerNight plus every per-night charge the card publishes beside it, localTaxPerNight and taxAmountPerNight where present. That figure covers every room in the search, so multiply a per-room budget by the room count in stay.occupancy.rooms. Read metadata.filterOptions.priceRange for the bounds this snapshot spans. With ?refundable=true it bounds the cheapest refundable rate. | | `max-price` | query | number | no | 0..1000000 | Maximum price (inclusive), in the response currency. Bounds the all-in nightly figure: amountPerNight plus every per-night charge the card publishes beside it, localTaxPerNight and taxAmountPerNight where present. That figure covers every room in the search, so multiply a per-room budget by the room count in stay.occupancy.rooms. Read metadata.filterOptions.priceRange for the bounds this snapshot spans. With ?refundable=true it bounds the cheapest refundable rate. | | `refundable` | query | `0` \| `1` \| `true` \| `false` | no | - | Keep only hotels with a witnessed refundable Book-on-Wego rate, so a 'cheapest refundable' answer needs no per-hotel /rates calls. Exactly equivalent to ?rate-types=free_cancellation, and combines with it: this is the one rate type that has its own param. This is an UNDER-approximation: the results envelope is a rate sample, so a true keeps hotels with a seen refundable rate and a hotel's absence is not authoritative – only GET /v1/hotels/{hotelId}/rates can prove a hotel has no refundable rate. Accepts true or false. | | `rate-types` | query | string[] | no | items 1..* | Keep hotels with a witnessed Book-on-Wego rate carrying ALL listed rate types (AND across terms; repeat or comma-separate), so 'only rooms with breakfast' needs no per-hotel /rates calls. Read the vocabulary from metadata.filterOptions.rateTypes (e.g. breakfast_included, free_cancellation) – unlike every other list filter these are matched EXACTLY (case-insensitively), not as substrings, because they are stable upstream codes rather than localized display names. Several terms mean ONE rate carrying all of them, and while any rate-type filter is active the card price, the price bounds, the price sorts and the cheapest badge all key off that matching rate. Like refundable, an UNDER-approximation: the results envelope is a rate sample, so a hotel's absence is not proof it lacks the type – only GET /v1/hotels/{hotelId}/rates can prove that. | | `deals-only` | query | `0` \| `1` \| `true` \| `false` | no | - | Keep only hotels whose card carries a price.deal, mirroring the 'today's deals' filter on wego.com. Judged on the very price object the card publishes, so the kept hotels and the deals shown always agree – with ?refundable=true that means the refundable rate must be the discounted one. Like refundable, this is an UNDER-approximation: the results envelope is a rate sample that grows while searchComplete is false, so a hotel's absence is not proof it has no discount. Accepts true or false. | | `amenities` | query | string[] | no | items 1..* | Keep hotels offering ALL listed amenities (AND across terms; repeat or comma-separate). Each term is matched case-insensitively as a substring against the name field in metadata.filterOptions.amenities – pick terms from there (e.g. Fitness Centre), not a guessed synonym (gym). One term can span several values (Pool also matches Indoor Pool). | | `property-types` | query | string[] | no | items 1..* | Keep hotels whose property type matches ANY listed term (OR; repeat or comma-separate). Matched case-insensitively as a substring against the name field in metadata.filterOptions.propertyTypes – pick from there rather than guessing. | | `brands` | query | string[] | no | items 1..* | Keep hotels whose brand matches ANY listed term (OR; repeat or comma-separate). Matched case-insensitively as a substring against the name field in metadata.filterOptions.brands – pick from there rather than guessing. Names parent companies as well as individual brands, and a term matches an entry name rather than a corporate relationship, so a group filed under several sibling brands needs each of those names listed. | | `chains` | query | string[] | no | items 1..* | Keep hotels whose chain matches ANY listed term (OR; repeat or comma-separate). Matched case-insensitively as a substring against the name field in metadata.filterOptions.chains – pick from there rather than guessing. Most hotels carry no chain and some entries name a loyalty programme, so a hotel group may be reachable only through brands, or split across both vocabularies. | | `districts` | query | string[] | no | items 1..* | Keep hotels whose district matches ANY listed term (OR; repeat or comma-separate). Matched case-insensitively as a substring against the name field in metadata.filterOptions.districts – pick from there rather than guessing. | | `view` | query | `card` | no | default `"card"` | Response projection. `card` is the only value: the lean results-list projection (price summary, refundability witness, star/review, location names). The former `default` projection was removed in issue #1308 – read GET /v1/hotels/{hotelId} for a hotel's amenities, images and address. | ## Responses | Status | Description | |---|---| | `200` | Ranked hotels, as lean list cards. Amenities, the full image list, the address and brand/chain are not on a card – read the hotel for the one row you picked. | | `400` | Invalid request parameters. | | `401` | Missing or invalid bearer token. | | `404` | Unknown or expired search. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream hotels service returned an invalid response. | | `503` | The hotels service is unavailable (`upstream_unavailable`) or rate-limited upstream (`upstream_rate_limited`); retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `searchId` | string | yes | The id of the search this snapshot belongs to. | | `currencyCode` | string | yes | Currency the prices in this snapshot are quoted in. | | `searchComplete` | boolean | yes | Upstream aggregation flag. true is authoritative/terminal; conclude NO BOOK-ON-WEGO BOOKABLE INVENTORY only when true AND metadata.totalBeforeFilters === 0 – never that no hotel exists, since only Book-on-Wego inventory was requested. A zero totalCandidates on its own means only that this read's filters matched nothing, and an empty page with totalCandidates > 0 is pagination. false is inconclusive, so watch metadata.snapshotCandidateCount convergence to stop sooner. | | `stay` | object | no | What upstream priced: the dates and occupancy every price on this read covers. Present when upstream states them. | | ↳ `checkIn` | string | yes | Check-in date priced upstream, YYYY-MM-DD. | | ↳ `checkOut` | string | yes | Check-out date priced upstream, YYYY-MM-DD. | | ↳ `nights` | integer | yes | Nights between checkIn and checkOut. price.total covers this many nights of price.amountPerNight. | | ↳ `occupancy` | object | yes | The occupancy priced upstream for this search (ages resolved, incl. fallback). | | `metadata` | object | yes | Pagination, the snapshot's filter vocabulary, the settle counters for this read, and what it resolved currency and locale to. | | ↳ `page` | integer | yes | 1-based page number of this snapshot. | | ↳ `pageSize` | integer | yes | Hotels requested per page. | | ↳ `resultCount` | integer | yes | Hotels on this page. The page only – judge a filter on totalCandidates. | | ↳ `totalCandidates` | integer | yes | Hotels matching this read's filters across the snapshot – the count that judges a filter, not the page. A filter that matched nothing is totalCandidates 0 with totalBeforeFilters above 0. | | ↳ `totalBeforeFilters` | integer | yes | Hotels that survived the Book-on-Wego join, before this read's filters ran. A filter that matched nothing is totalCandidates === 0 with totalBeforeFilters > 0. Zero means no Book-on-Wego-bookable inventory surfaced for these dates – it is NOT proof that no hotel exists, since the join runs over a sampled rate list. Equal to totalCandidates on an unfiltered read. | | ↳ `filterOptions` | object | yes | The filterable vocabulary of the hotels in this snapshot, ordered by count, over the same population as totalBeforeFilters. The amenities / property-types / brands / chains / districts query params take an entry's name field VERBATIM (matched case-insensitively as a substring), so pick from here rather than guessing a synonym. A term listed here matches AT LEAST its count on an unfiltered read; the count is a lower bound, since one term can span several values (Pool also matches Indoor Pool). rateTypes and guestTypes are the exceptions: their names are stable codes, matched exactly rather than as substrings, so their counts are precise rather than a lower bound. guestTypes counts a smaller population than the rest for a different reason - it counts only the hotels upstream scored for that cohort, and upstream publishes no thin cohort rows, so a hotel absent from a cohort has too little data for it rather than a poor rating. Still growing while searchComplete is false. priceRange does the same job for the numeric bounds: it states the span min-price / max-price are measured on, so read it before choosing either. | | ↳ `hasMore` | boolean | yes | Another page of hotels follows. | | ↳ `snapshotCandidateCount` | integer | yes | Upstream aggregation counter – the practical early convergence signal. Two spaced (not back-to-back), equal, non-zero reads ≈ settled enough to render; it stabilizes well before searchComplete flips, so use it to stop polling sooner. A heuristic, not proof of completion (searchComplete:true is that). Not the same as totalCandidates (the post-Book-on-Wego-join hotel count). | | ↳ `createdAt` | string | no | When this search was created (ISO 8601) – the freshness anchor for these prices. A search older than about 10 minutes may answer 404 as expired. Absent when the search service omits it. | | ↳ `currencyCode` | string | yes | The currency this read ASKED upstream for, and the one every price on it is meant to be in. Read it beside currencyCodeSource before you show a number: a price computed in the wrong currency renders as a perfectly normal price, with no error and no odd shape to notice, so the response states which one rather than leaving it to be inferred. Where the operation also publishes a top-level currencyCode, that field reports the currency the prices actually came back in; the two agree unless upstream declined to reprice. | | ↳ `currencyCodeSource` | `explicit` \| `default` | yes | How the API resolved currencyCode: explicit (the caller sent currency – including a value equal to the default) or default (USD, no currency sent). A default here is the one signal that the request never carried the currency you meant. | | ↳ `locale` | string | yes | The language tag this read asked upstream for – what any localized text on it was resolved in (room and board names, airline and airport names, review prose). | | ↳ `localeSource` | `explicit` \| `default` | yes | How the API resolved locale: explicit (the caller sent locale – including a value equal to the default) or default (en, no locale sent). A default here explains text that came back in a language the caller did not ask for. | | `results` | object[] | yes | The requested page of ranked hotels, as list cards. | | ↳ `hotelId` | number | yes | The hotel's numeric id; read its detail with GET /v1/hotels/{hotelId}. | | ↳ `name` | string | yes | Hotel display name. | | ↳ `pageUrl` | string | yes | The hotel's page on wego.com. It is not tied to a search, so it keeps working after this search expires. Give it to a traveller who wants to look at the hotel, and use it in anything that is saved or sent on. It opens with no dates set. | | ↳ `star` | number | no | Star rating (1-5), when classified. | | ↳ `review` | object | no | Aggregate guest review score and count. | | ↳ `reviewsByGuestType` | object | no | Guest review score and count per cohort, on the same 0-10 scale as review (which is the all-guests figure). These are the exact values ?guest-type= accepts and the same vocabulary metadata.filterOptions.guestTypes counts. A cohort is present only when upstream scored it, and an ABSENT cohort means too little data for that cohort, never a low score - upstream publishes no thin cohort rows. Read these to explain a pick as well as make one: a hotel rated 8.5 overall and 7.6 by families is a different recommendation than one rated 8.5 by both. Omitted when no cohort was scored. | | ↳ `price` | object | yes | A card's per-night and stay price. Every amount covers the whole booking, all rooms in the search, so quote these figures as they stand. Upstream rounds per booking, so total is the exact stay figure and amountPerNight is one night of it. Any per-night figure published beside amountPerNight is charged ON TOP of it: add localTaxPerNight, and taxAmountPerNight when it appears, to reach what a guest pays and what the price sorts and bounds rank on. | | ↳ `refundable` | `available` \| `unknown` | yes | Refundability WITNESS, not a boolean. 'available' = a free-cancellation Book-on-Wego rate was seen in this snapshot. 'unknown' = none was seen, which is NOT evidence that none exists – this envelope carries only a sample of each hotel's rates, so a negative is not computable here. To answer 'does this hotel have a refundable room', read the hotel's rooms/rates. | | ↳ `lowestRefundablePrice` | object | no | Lowest refundable Book-on-Wego rate. Present exactly when refundable is 'available'. | | ↳ `rateTypes` | string[] | no | Rate types WITNESSED on this hotel's Book-on-Wego rates in this snapshot (e.g. breakfast_included, free_cancellation), sorted. A witness on the same terms as refundable, never a negative: this envelope carries only a sample of each hotel's rates, so an absent type is not evidence the hotel lacks it. These are the exact values ?rate-types= accepts, and the same vocabulary metadata.filterOptions.rateTypes counts. Omitted when no rate type was witnessed. | | ↳ `cityName` | string | no | City the hotel is in. | | ↳ `districtName` | string | no | District or neighbourhood the hotel is in. | | ↳ `lat` | number | no | Hotel latitude in decimal degrees. Use with lng to compute distance to any landmark you choose. | | ↳ `lng` | number | no | Hotel longitude in decimal degrees. Use with lat to compute distance to any landmark you choose. | | ↳ `distanceToCityCentre` | number | no | Kilometres from the city's place-record coordinate (the point GET /v1/places reports for the city), and the ?sort=distance_asc key. Where a city record covers an island, a city-state or a whole administrative area, that point can sit far from the commercial centre. For locality prefer the districts filter, or compute distance from lat/lng to a landmark you choose. Present when upstream reports it. | | ↳ `image` | string | no | Primary image URL, if any. | | ↳ `badges` | string[] | yes | Full-result-set badges this hotel wins (e.g. cheapest). | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/hotels/searches/$SEARCH_ID/results" ``` # Get hotel detail > Returns static hotel detail (name, stars, address, images, amenities, reviews). Source: https://docs.wego.com/api/hotels/get-hotel/ · Markdown: https://docs.wego.com/api/hotels/get-hotel/index.md `GET /v1/hotels/{hotelId}` Returns static hotel detail (name, stars, address, images, amenities, reviews). Use ?view=detail for the richer UI projection. Operation ID: `getHotel` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `hotelId` | path | integer | yes | 1..9007199254740991 | The hotel's numeric id (a positive integer), as carried by hotel search results (results[].hotelId) and embedded in a rate id. | | `locale` | query | string | no | length 1..35; default `"en"` | Response language tag (e.g. en, ar). Defaults to en. | | `view` | query | `default` \| `detail` | no | default `"default"` | Response projection. default: the agent hotel detail. detail: the richer UI projection (categorized images, review highlights, badges). Defaults to default. | ## Responses | Status | Description | |---|---| | `200` | Hotel detail (agent default) or the detail projection. | | `400` | Invalid request parameters. | | `401` | Missing or invalid bearer token. | | `404` | Unknown hotel. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream hotels service returned an invalid response. | | `503` | The hotels service is unavailable (`upstream_unavailable`) or rate-limited upstream (`upstream_rate_limited`); retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `hotelId` | number | yes | The hotel's numeric id. | | `name` | string | yes | Hotel display name. | | `pageUrl` | string | yes | The hotel's page on wego.com. It is not tied to a search, so it keeps working after this search expires. Give it to a traveller who wants to look at the hotel, and use it in anything that is saved or sent on. It opens with no dates set. | | `star` | number | no | Star rating (1-5), when classified. | | `review` | object | no | Aggregate guest review score and count. | | ↳ `score` | number | yes | Aggregate guest review score (0-10). | | ↳ `count` | number | yes | Number of guest reviews behind the score. | | `location` | object | yes | Where the hotel is: coordinates and place names. | | ↳ `lat` | number | no | Latitude in decimal degrees. | | ↳ `lng` | number | no | Longitude in decimal degrees. | | ↳ `address` | string | no | Street address, when available. | | ↳ `cityName` | string | no | City name. | | ↳ `districtName` | string | no | District or neighbourhood name. | | ↳ `countryName` | string | no | Country name. | | `propertyType` | string | no | Property type, e.g. Hotel, Apartment. | | `brandName` | string | no | Brand name, when the hotel belongs to one. | | `chainName` | string | no | Parent chain name, when known. | | `description` | string | no | Editorial description of the hotel. | | `amenities` | string[] | no | Hotel-level amenity names. | | `images` | string[] | no | Hotel image URLs. | | `distanceToCityCentre` | number | no | Kilometres from the city's place-record coordinate (the point GET /v1/places reports for the city). Where a city record covers an island, a city-state or a whole administrative area, that point can sit far from the commercial centre. For a specific landmark, compute distance from location.lat/lng. Present when upstream reports it. | | `distanceToNearestAirport` | number | no | Distance to the nearest airport, as the upstream reports it (unit not normalized here). Absent when upstream omits it. | | `categorizedImages` | object[] | no | view=detail only – hotel images grouped by category (e.g. Rooms, Pool). Absent when the content service supplies none. | | ↳ `category` | string | yes | Image category name, e.g. Rooms, Pool. | | ↳ `images` | object[] | yes | Images in this category. | | `reviewHighlights` | object[] | no | view=detail only – short editorial review snippets with a sentiment tag. Absent when none are published. | | ↳ `sentiment` | string | no | Sentiment tag for the snippet, e.g. positive, negative. | | ↳ `text` | string | yes | The review snippet text. | | `highlights` | object[] | no | Editorial badges from the hotel content service. | | ↳ `text` | string | yes | Badge label. | | ↳ `subtext` | string | no | Badge supporting text, when present. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/hotels/$HOTEL_ID" ``` # How search works > Flight and hotel searches are asynchronous; read results until they settle. Source: https://docs.wego.com/api/how-search-works/ · Markdown: https://docs.wego.com/api/how-search-works/index.md Creating a search returns a `searchId` immediately while travel providers keep answering in the background. Each read of the results endpoint returns the current ranked snapshot, so early reads can be sparse or empty. Re-read, backing off from about 300ms to 3s, until the search settles. The two verticals settle differently. **Hotels** publish a terminal flag. `searchComplete: true` means the search is done; `false` is advisory rather than a promise, so poll `metadata.snapshotCandidateCount` until it holds steady at a non-zero value. Once complete, `totalCandidates: 0` means your filters excluded everything if `metadata.totalBeforeFilters` is above zero, and otherwise that nothing bookable was found. **Flights** have no completion flag. Re-read until `metadata.snapshotFareCount` holds steady across two consecutive reads and `metadata.snapshotTripCount` is above zero. The `wego` CLI does this for you and stamps the result: `wego flights search`, and any `results` read with `--wait`, block until the search settles and report `settled` as `converged` or `budget_exhausted`. A bare `results` read is a single snapshot, stamped `unsettled`. Those stamps are a CLI convenience; the HTTP API does not return them. # IDs expire > Which ids are snapshots, which are permanent, and what a 404 on each one means. Source: https://docs.wego.com/api/ids-expire/ · Markdown: https://docs.wego.com/api/ids-expire/index.md Every search-scoped id in the API (`searchId`, trip id, fare id, rate id) is opaque and expires. A `404` on one that worked before means it expired: create a new search and rethread. Retrying the same id never recovers. These ids are also context bound: a trip id only resolves with the `searchId` it came from. Not every id expires. A `hotelId` is opaque too, but it names a stable entity rather than a snapshot inside one search, so it keeps working: a `404` from `GET /v1/hotels/{hotelId}` or its `/reviews` means the hotel is unknown, not that anything expired, and searching again will not help. ## Links that do not expire Two operations hand someone a link that outlives the search behind it: `getFlightSearchLink` (`/v1/flights/search-link`) and `getHotelSearchLink` (`/v1/hotels/search-link`). They take your own route, dates and occupancy rather than a `searchId`, so there is no snapshot to go stale, and they show live prices whenever the recipient opens them. From the CLI: `wego flights share` and `wego hotels share`. Every link the API returns says which kind it is. A booking or checkout link carries `"expires": true`, a share link `"expires": false`, so a client never has to infer it from the endpoint it called. # Places > Turn free text into typed travel locations. Source: https://docs.wego.com/api/places/ · Markdown: https://docs.wego.com/api/places/index.md Turn free text into typed travel locations. `getPlaces` resolves a city, airport, district or hotel name to results carrying the codes the flight and hotel searches take as input. `getNearbyPlaces` answers the follow-up – which other airports serve the same trip – from a place code or a coordinate pair. ## Operations | Verb | Operation | Carries onward | |---|---|---| | `GET` | [Airports and cities near a point](/api/places/get-nearby-places) · `/v1/places/nearby` | `terminal` | | `GET` | [Resolve travel locations](/api/places/get-places) · `/v1/places` | `terminal` | # Airports and cities near a point > The airports (and optionally cities) closest to a place or a coordinate pair, nearest first – for finding an alternative departure airport serving the same trip. Source: https://docs.wego.com/api/places/get-nearby-places/ · Markdown: https://docs.wego.com/api/places/get-nearby-places/index.md `GET /v1/places/nearby` The airports (and optionally cities) closest to a place or a coordinate pair, nearest first – for finding an alternative departure airport serving the same trip. Pass either place (a code, resolved to coordinates here) or latitude+longitude, never neither. Operation ID: `getNearbyPlaces` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `place` | query | string | no | length 1..100 | Place code to search around (e.g. LON, LHR). Resolved to coordinates before the upstream call. | | `latitude` | query | number | no | -90..90 | Latitude of the point to search around, decimal degrees (-90 to 90). Give latitude and longitude together; use either this pair or place, never both. | | `longitude` | query | number | no | -180..180 | Longitude of the point to search around, decimal degrees (-180 to 180). Give latitude and longitude together; use either this pair or place, never both. | | `types` | query | `city` \| `airport` \| `state` \| `district` \| `hotel`[] | no | items 1..*; each of `city`, `airport`, `state`, `district`, `hotel` | Place types to resolve; repeat or comma-separate to mix. | | `locale` | query | string | no | length 1..35; default `"en"` | Language tag for localized place names (e.g. en, ar). Defaults to en. | | `pageSize` | query | integer | no | 1..50; default `50` | Max results (1-50). Defaults to 50, the maximum, because this read wants the complete set of nearby places, not a page of it. The upstream returns roughly ten rows and ignores paging, so pageSize can only narrow that set, never reach further. | ## Responses | Status | Description | |---|---| | `200` | Nearby places, nearest first, plus the origin they were measured from. | | `400` | Neither a place nor a coordinate pair, an out-of-range coordinate, or a place code that resolves to nothing with coordinates. | | `401` | Missing or invalid bearer token. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream places service returned an invalid response. | | `503` | The places service is temporarily unavailable; retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `results` | object[] | yes | The nearby places found around the origin. | | ↳ `id` | number \| string | no | Opaque identifier, unique per place. Do not send it to other endpoints or build wego.com URLs from it: reference a place by code, or by cityCode for a hotels search. | | ↳ `code` | string | no | IATA-style code (airport/city), when the place has one. | | ↳ `name` | string | yes | Display name of the place. | | ↳ `type` | string | yes | Place kind: city, airport, state, district or hotel. | | ↳ `cityCode` | string | no | Code of the city this place belongs to. | | ↳ `latitude` | number | no | Latitude in decimal degrees, when known. | | ↳ `longitude` | number | no | Longitude in decimal degrees, when known. | | `metadata` | object | yes | Pagination and the resolved origin for this nearby search. | | ↳ `resultCount` | integer | yes | Number of results on the current page (always <= pageSize). | | ↳ `totalCandidates` | integer | yes | Rows the upstream returned before this page was sliced. The upstream ignores per_page and answers with roughly ten rows, so pageSize can only narrow this, never reach further. | | ↳ `hasMore` | boolean | yes | True when pageSize clipped the upstream rows. | | ↳ `origin` | object | yes | The point the nearby search ran from, and how it was derived. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/places/nearby" ``` # Resolve travel locations > Resolves a free-text location query to canonical Wego places (cities, airports, states, districts, hotels) with codes and coordinates, for use in later flight and hotel searches. Source: https://docs.wego.com/api/places/get-places/ · Markdown: https://docs.wego.com/api/places/get-places/index.md `GET /v1/places` Resolves a free-text location query to canonical Wego places (cities, airports, states, districts, hotels) with codes and coordinates, for use in later flight and hotel searches. When metadata.hasAmbiguity is true, clarify with the user before proceeding. Operation ID: `getPlaces` ## Parameters | Name | In | Type | Required | Constraints | Description | |---|---|---|---|---|---| | `query` | query | string | yes | length 1..100 | Free text to resolve to typed places with codes. A place-name search (city, airport, district, hotel). | | `types` | query | `city` \| `airport` \| `state` \| `district` \| `hotel`[] | no | items 1..*; each of `city`, `airport`, `state`, `district`, `hotel` | Place types to resolve; repeat or comma-separate to mix. | | `locale` | query | string | no | length 1..35 | Language tag for localized place names (e.g. en, ar). Omitted, the search is language-neutral: the query matches names in any language (sent upstream as the locale wildcard) and results carry canonical English names. Pass a tag to localize the returned names instead. getNearbyPlaces, by contrast, defaults to en. | | `page` | query | integer | no | 1..100; default `1` | Page number, 1-based (max 100). Defaults to 1. | | `pageSize` | query | integer | no | 1..50; default `10` | Results per page (1-50). Defaults to 10. | ## Responses | Status | Description | |---|---| | `200` | Matching places plus pagination/ambiguity metadata. | | `400` | Invalid query parameters. | | `401` | Missing or invalid bearer token. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | | `502` | The upstream places service returned an invalid response. | | `503` | The places service is temporarily unavailable; retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `results` | object[] | yes | The matched places for this page. | | ↳ `id` | number \| string | no | Opaque identifier, unique per place. Do not send it to other endpoints or build wego.com URLs from it: reference a place by code, or by cityCode for a hotels search. | | ↳ `code` | string | no | IATA-style code (airport/city), when the place has one. | | ↳ `name` | string | yes | Display name of the place. | | ↳ `type` | string | yes | Place kind: city, airport, state, district or hotel. | | ↳ `cityCode` | string | no | Code of the city this place belongs to. | | ↳ `latitude` | number | no | Latitude in decimal degrees, when known. | | ↳ `longitude` | number | no | Longitude in decimal degrees, when known. | | `metadata` | object | yes | Pagination and ambiguity signals for this place search. | | ↳ `resultCount` | integer | yes | Number of results on the current page (always <= pageSize). | | ↳ `totalCandidates` | integer | yes | Total matches held for this query (post-dedup, pre-pagination) – the ceiling pagination can reach. 0 means no matches; an empty deep page with totalCandidates > 0 just means the offset is past the end. | | ↳ `hasMore` | boolean | yes | True when a further page exists. | | ↳ `hasAmbiguity` | boolean | yes | True when several distinct real-world locations share the query; ask the user to disambiguate before searching. | | ↳ `disambiguationHint` | string | no | Short clarification sample, present only when hasAmbiguity. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/places?query=dubai" ``` # Rate limits > Per-IP and per-user quota, surfaced in RateLimit response headers. Source: https://docs.wego.com/api/rate-limits/ · Markdown: https://docs.wego.com/api/rate-limits/index.md Requests are metered per client IP before auth and per user after. Two kinds of per-user quota apply: a general one across every operation, and a tighter one on each of the costliest operations, so one expensive funnel cannot consume your whole allowance. Each costly operation is metered over three windows at once, a minute, an hour and a day, and a request is rejected when any one of them is spent. | Operation | Per minute | Per hour | Per day | |---|---|---|---| | every operation | 300 / 60s | – | – | | `createFlightSearch` | 10 / 60s | 100 / 3600s | 500 / 86400s | | `createHotelSearch` (city or geo) | 10 / 60s | 100 / 3600s | 500 / 86400s | | `createHotelSearchScoped` (a create carrying `hotelId`) | 30 / 60s | 300 / 3600s | 1500 / 86400s | | `getHotelRates` | 60 / 60s | 600 / 3600s | 3000 / 86400s | These are the default quotas, and an account can be placed on a larger one. Your own quota is whatever `RateLimit-Policy` states on the response in front of you, so pace against that rather than against this table. The minute window is there for bursts, the hour and day windows for sustained volume. Running at the full minute quota without pause spends the hour quota in ten minutes, so a long-running workflow has to pace itself against the hour and day quotas, not the minute one. Creating a search is the costly step, so thread a `searchId` through `results` rather than re-creating: filtering, sorting and paging a search you already hold spends only the general quota. `getHotelRates` is metered because it is reached once per hotel rather than once per search, and it is looser than the creates because reading a hotel's rates against a **freshly created** search usually takes more than one call while rates land. A hotel search is split in two because the two cost different amounts. A create carrying `hotelId` asks one property's providers, not a whole city's, and it is the search `getHotelRates` is built for – the rates read rejects a search it can positively identify as city- or geo-scoped – so every fresh room comparison spends one: it gets 30 / 60s. The rates quota is looser (60 / 60s) because one create is usually followed by several reads while rates land, but a workflow that mints a fresh search per hotel can still hit the create quota first. ## Reading the headers `RateLimit` and `RateLimit-Policy` are lists carrying one entry per quota window that applied to the request. An operation with no quota of its own names the general quota alone. A metered operation names it plus its own three windows, except when the general quota rejects the request: that happens before the operation's own quota is counted, so such a `429` names only `user`. Each window has its own name: the minute window carries the quota name from the table above, the hour window adds `-hour` and the day window adds `-day`. The quota name is usually the operationId, but not always: a hotel create carrying `hotelId` is metered as `createHotelSearchScoped`, so match on the name the header actually carries rather than on the operation you called. ```http RateLimit-Policy: "user";q=300;w=60, "createFlightSearch";q=10;w=60, "createFlightSearch-hour";q=100;w=3600, "createFlightSearch-day";q=500;w=86400 RateLimit: "user";r=283;t=41, "createFlightSearch";r=8;t=41, "createFlightSearch-hour";r=97;t=2000, "createFlightSearch-day";r=480;t=80000 X-RateLimit-Limit: 10 X-RateLimit-Remaining: 8 X-RateLimit-Reset: 41 X-RateLimit-Resource: createFlightSearch ``` `q` is the quota, `w` the window in seconds, `r` what you have left, and `t` the seconds until that window resets. The older `X-RateLimit-*` headers describe a single quota: the tightest window of the operation you called, meaning the one with the least left, and `X-RateLimit-Resource` names which one. On a `429` they describe the window that rejected you instead, the same one `Retry-After` is timed to. On an operation with no tighter quota of its own, that is `user`. Open endpoints carry no quota headers, and neither does a `401`: metering happens after your token is verified. ## When you are limited Quota is spent per request attempted, so a rejected or invalid request counts too, and every window of an operation is spent by the same attempt: a request the minute quota rejects also costs you an hour and a day unit. On a `429` honor `Retry-After` before retrying; it takes precedence over the reset in `RateLimit`, and it carries the latest reset among the windows you hit, so a spent day quota can mean a long wait. The `X-RateLimit-Resource` header on a `429` tells you which window you hit. An upstream `503` also carries a `Retry-After`; honor it too, it is a short transient hint, not a quota reset. # User > The authenticated caller. Source: https://docs.wego.com/api/user/ · Markdown: https://docs.wego.com/api/user/index.md The authenticated caller. `getCurrentUser` returns the identity behind the bearer token (the CLI's `wego whoami`). ## Operations | Verb | Operation | Carries onward | |---|---|---| | `GET` | [Get the authenticated user](/api/user/get-current-user) · `/v1/user` | `terminal` | # Get the authenticated user > Returns the caller's own identity, read from the verified access-token claims. Source: https://docs.wego.com/api/user/get-current-user/ · Markdown: https://docs.wego.com/api/user/get-current-user/index.md `GET /v1/user` Returns the caller's own identity, read from the verified access-token claims. Profile fields (email, name, country) appear only when the token carries them. Operation ID: `getCurrentUser` ## Responses | Status | Description | |---|---| | `200` | The caller's identity claims. | | `401` | Missing or invalid bearer token. | | `429` | Rate limit exceeded; retry after the `Retry-After` seconds. | ### `200` body | Field | Type | Always present | Description | |---|---|---|---| | `sub` | string | yes | The token subject (the JWT `sub`): the user's email, which auth.wego.com uses as the stable user identifier. | | `scope` | string | no | Space-separated OAuth scopes granted to the token. | | `email` | string | no | The user's email address, when the token carries it. | | `name` | string | no | The user's full display name, when present. | | `first_name` | string | no | The user's given name, when present. | | `last_name` | string | no | The user's family name, when present. | | `country_code` | string | no | The user's market country code, when present (id_token-sourced; usually absent on the access token). | | `uid` | string \| number | no | The AS's own numeric user id. Published as `string \| number` because that is what it is: it arrives as a number today, and a `string`-only declaration would be a promise the API does not keep. | | `principal_name` | string | no | The auth server's principal name for the user, when present. | ## Example ```bash curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/user" ``` # Authentication > Get an OAuth2 bearer token for the Wego API, two ways. Source: https://docs.wego.com/authentication/ · Markdown: https://docs.wego.com/authentication/index.md All `/v1` operations require an OAuth2 bearer token from the Wego auth server. Two ways in, both against the auth endpoints declared in the spec's `securitySchemes`. ## Wego CLI The easiest path. `wego login` runs the browser flow for you and stores the token locally; every CLI command then calls the API as you. See [Wego CLI](/cli). ## Any OAuth2 client Run the OAuth2 authorization code flow with PKCE against the Wego auth server, then send the access token as `Authorization: Bearer ` (RFC 6750). | Field | Value | |---|---| | Authorize URL | `https://auth.wego.com/user-auth/v2/users/oauth/authorize` | | Token URL | `https://auth.wego.com/user-auth/v2/users/oauth/token` | | Client ID | `251815b9647317f4895122fd4924b7d44541d8fcc27be528435e3ad9bbf7e1ee` (public, PKCE) | | Redirect URI | A loopback address you listen on, e.g. `http://127.0.0.1:/callback` (RFC 8252) | | PKCE | S256 | | Scopes | `openid profile users` | # Wego in ChatGPT > Add the Wego plugin to ChatGPT to search flights and hotels by asking, then book on Wego. Source: https://docs.wego.com/chatgpt-plugin/ · Markdown: https://docs.wego.com/chatgpt-plugin/index.md Ask ChatGPT for a trip and the Wego plugin searches live flights and hotels for you, compares what it finds, and hands back a Wego link where you book. Nothing to install and no API token to paste. Plugins are how ChatGPT and Codex pick up outside capabilities, so once Wego is enabled it is available in both. ## Add the plugin 1. Open Wego in the ChatGPT plugin directory. You can also reach the directory from **Settings > Plugins**, or from **Plugins** in the ChatGPT sidebar. 2. Enable Wego for your account. Plugin availability depends on your ChatGPT plan. ## Ask for a trip However you would say it to a person: - Cheapest nonstop from Jeddah to Dubai next Thursday - Business class Riyadh to London, cheapest fare this month - 4-star in Dubai Marina under $250 a night, refundable - 3 nights in Cairo for two, flights from Doha and a hotel near the museum Follow-ups work the same way: ask it to sort by price, add a return date, filter to nonstop, or widen the dates. ## What happens next The plugin searches, compares, and gives you a Wego link. You review the trip and book on Wego. Flight and hotel results settle over a few seconds. ## Other ways in - [Wego in Claude](/claude-connector): the same thing as a Claude connector. - [Agent setup](/agent-setup): give a coding agent the CLI and the Wego skill. - [API Reference](/api): call the API yourself over HTTP. # Wego in Claude > Add the Wego connector to Claude to search flights and hotels by asking, then book on Wego. Source: https://docs.wego.com/claude-connector/ · Markdown: https://docs.wego.com/claude-connector/index.md Add the Wego connector and Claude can search live flights and hotels while you talk to it, compare what it finds, and give you a Wego link where you book. Nothing to install and no API token to paste. Connectors are MCP servers that Claude connects to, so Wego works the same way everywhere you use Claude: web, desktop, mobile, and Claude Code. ## Add the connector Add it straight from the prompt box: 1. Click **+** in the prompt box, then **Connectors > Add connector**. 2. Click **Browse connectors**. 3. Search for **Wego**, then connect it. You can also open Wego in the Claude connectors directory directly. ## Ask for a trip However you would say it to a person: - Cheapest nonstop from Jeddah to Dubai next Thursday - Business class Riyadh to London, cheapest fare this month - 4-star in Dubai Marina under $250 a night, refundable - 3 nights in Cairo for two, flights from Doha and a hotel near the museum Follow-ups work the same way: ask it to sort by price, add a return date, filter to nonstop, or widen the dates. ## What happens next Claude searches, compares, and gives you a Wego link. You review the trip and book on Wego. Flight and hotel results settle over a few seconds. ## Other ways in - [Wego in ChatGPT](/chatgpt-plugin): the same thing as a ChatGPT plugin. - [Agent setup](/agent-setup): give a coding agent the CLI and the Wego skill, for Claude Code and other terminal agents. - [API Reference](/api): call the API yourself over HTTP. # Wego CLI > The same funnels, one command per step, built for people and agents. Source: https://docs.wego.com/cli/ · Markdown: https://docs.wego.com/cli/index.md ## Install ```bash curl -fsSL https://docs.wego.com/cli/install | bash ``` That installs the `wego` binary and the agent skill together. `wego login` opens your browser and stores the token locally. Every search and lookup command prints JSON on stdout, so each step's output threads into the next step's arguments (pipe through `jq`). Three print plain text instead: `wego version` (a bare version string), `wego feedback` (a confirmation), and `wego skill list` without `--json`. ### Environment | Variable | Does | |---|---| | `WEGO_CLI_TELEMETRY` | `on`, `off` or `log` for one run. Wins over the stored setting | | `WEGO_CREDENTIALS_PATH` | Where the login is stored. Default `~/.config//credentials.json`, so `wego` uses `~/.config/wego/credentials.json` | | `WEGO_CLI_REDIRECT_PORT` | Fixed local port for the login callback (see Log in over SSH) | The installer reads three of its own, all optional: `WEGO_CLI_INSTALL_SKILL=0` skips the agent-skill step, `WEGO_CLI_BIN` renames the command (which is also the install's config directory, so a second install needs nothing else to stay independent), and `WEGO_CLI_INSTALL_DIR` moves it off `~/.local/bin`. ## Commands | Command | Does | |---|---| | `wego login [--browser \| --no-browser]` / `wego logout` | Browser login (PKCE); remove stored credentials | | `wego whoami` | Print the authenticated user | | `wego places ""` | Resolve free text to a city, airport, state, district or hotel. `--types` narrows it; the hotel form is where a `hotelId` comes from | | `wego info holidays\|visa-free\|schedules\|airports-near` | Stateless reference lookups, no search needed | | `wego flights search ` | Create a flight search and block until it settles | | `wego flights results [--wait]` | Read a ranked snapshot; `--wait` settles first | | `wego flights trip --search ` | Full itinerary and fares for one trip | | `wego flights fares ` | Fare options (Saver, Flex and similar) for a Wego fare | | `wego flights booking-link --trip --fare-option --from DXB --to LHR --date ` | Print the wego.com booking URL. All five flags are required; repeat `--fare-option` once per leg when a fare covers one leg | | `wego flights experience ` | Per-leg comfort signals: overnight, long stopover, early departure, late arrival | | `wego flights share ` | A wego.com search link to send to someone. Does not expire | | `wego hotels search ` | Create and settle a hotel search. The `hotelId` form is the one `rooms` needs | | `wego hotels results [--wait]` | Read a page; `--wait` settles first | | `wego hotels details ` | Static hotel detail | | `wego hotels reviews ` | Guest reviews, newest first, pros and cons already split | | `wego hotels rooms ` | Rooms and rates, cheapest first. `--search ` re-reads a search `rooms` minted | | `wego hotels booking-link --rate ` | Print the wego.com checkout URL | | `wego hotels share ` | A wego.com hotel search link. Does not expire | | `wego config list` / `wego config set ` / `wego config unset ` | Show or set the defaults every other command inherits. Keys: `currency`, `site`, `locale` | | `wego skill list\|install\|path\|uninstall` | Manage the agent skill in your coding agent; `path` prints where it landed. `--scope user\|project` and `--agent ` pick where it goes | | `wego feedback [--rating N] [--category VALUE] [--message "..."]` | Send feedback about the CLI to the Wego team | | `wego telemetry status\|enable\|disable` | Report or change whether the CLI sends usage events | | `wego update` / `wego uninstall` | Update in place; remove the binary, the login and the user-scope agent skill | | `wego version` | Print the version | `wego config` sets the currency, market and language a command uses when it names none. A flag on the command always wins, then these settings, then your account market, then the default (USD, `en`, US). `wego uninstall` leaves a project-scope or `--dir` skill install alone. Remove one of those with `wego skill uninstall --scope project`, or `--dir `. A settled search is stamped `settled`: `converged` when the snapshot stopped changing, `budget_exhausted` when the re-read budget ran out first. A bare `results` read with no `--wait` is a single snapshot, stamped `unsettled`. `--fare-option` is required on `flights booking-link`: take it from `flights fares `. The endpoint rejects a request without one (`400 validation_failed`), so there is no link to dead-end with. ## Log in over SSH Login binds a loopback listener on the machine that runs the CLI. On a remote machine your browser is somewhere else, so the redirect cannot come back on its own. The CLI detects the SSH session, opens no browser, and waits for you: 1. Open the printed URL in the browser on your own computer. 2. Approve the login. The browser then goes to a `http://127.0.0.1:/callback?code=…` address that only exists on the remote machine, so the page fails to load. That is expected – the code you need is in the address bar. 3. Copy that whole URL, paste it into the waiting terminal, and press Enter. Force the same flow anywhere with `wego login --no-browser` – useful in a container or any shell where the SSH check cannot see that you have no browser. The opposite override is `wego login --browser`: open a browser on the remote machine anyway, for an SSH session with X11 forwarding. If you prefer the browser to reach the remote listener, pin the port and forward it instead – then the standard flow completes with no paste: ```bash ssh -L 8765:127.0.0.1:8765 you@remote WEGO_CLI_REDIRECT_PORT=8765 wego login ``` `wego --help` lists the commands, `wego flights --help` lists the flights commands, and `wego flights results --help` lists that command's flags. The two chains: ```text flights: search -> results -> trip -> fares -> booking-link hotels: search -> results -> rooms -> booking-link ``` `fares` is not optional in the flights chain: `booking-link` needs the `fareOptionId` it returns. # Wego API > What the Wego API is and how travelers and agents use it to search flights and hotels. Source: https://docs.wego.com/overview/ · Markdown: https://docs.wego.com/overview/index.md Wego is a travel company serving millions of travelers across the Middle East, Asia and beyond. This API exposes the building blocks wego.com itself runs on: resolve travel locations, search flights and hotels, compare fares and room rates, and continue on wego.com to book. All `/v1` operations require an OAuth2 bearer token from the Wego auth server. ## Start here - [Agent setup](/agent-setup): give your AI agent one line and it drives Wego. - [Wego in ChatGPT](/chatgpt-plugin): add the Wego plugin and ask for a trip. - [Wego in Claude](/claude-connector): add the Wego connector and ask for a trip. - [Ways to use this API](/ways-to-use): the plugin and connector, the skill, the CLI, or direct HTTP. - [Authentication](/authentication): get a token, two ways. - [Quickstart](/quickstart): Dubai to London, end to end. - [Wego CLI](/cli): the same funnels, one command per step. - [API Reference](/api): every endpoint, request and response. ## The model Both verticals run the same shape: create a search, read ranked results, open one result, then continue to wego.com, where the traveler books. ```text flights: search -> results -> trip -> fare -> booking-link hotels: search -> results -> search(hotelId) -> rates -> booking-link ``` Hotels take one step flights do not. A city search holds only a sample of any one hotel's rates, so pricing a hotel's full room list needs a **second** search created with that `hotelId`. Reading rates against the city search instead is a `409 rates_require_hotel_search` – see the [Quickstart](/quickstart) for the shape, and [Errors](/api/errors) for the code. The [API Reference](/api) documents the machine contract in full: how async search settles, why ids expire, the fare and rate kinds, error codes, and rate limits. ## Research Preview Everything here is a Research Preview: the API, the `wego` CLI, the agent skill, and the Wego plugin for ChatGPT and the Wego connector for Claude. We are shipping it early to learn what people and agents actually ask for. What that means for you: - **Things change.** Endpoints, response shapes, and CLI output can change while we improve them. Read the [API Reference](/api) rather than caching what you saw last month, and pin nothing you cannot re-check. - **Coverage is uneven.** Results settle asynchronously and some routes, fares, or hotels are missing. An empty result is not proof that nothing exists. - **No availability promise.** There is no SLA yet, and we may pause a surface to fix it. - **Booking stays on Wego.** We return a link where the traveler books. We never take a charge. Tell us what worked and what did not: `wego feedback` from the CLI. That feedback is what decides what we build next. # Quickstart > Dubai to London, end to end, with curl. Source: https://docs.wego.com/quickstart/ · Markdown: https://docs.wego.com/quickstart/index.md Get `$TOKEN` per [Authentication](/authentication) first. This needs `jq` (the same threading idiom the CLI uses). ## Flights ```bash # 0. A departure date ~60 days out, so this block never carries a past date FROM_DATE=$(date -u -v+60d +%F 2>/dev/null || date -u -d '+60 days' +%F) # 1. Create a search (IATA airport or city codes; results settle async) SEARCH_ID=$(curl -s -X POST "https://api.wego.com/v1/flights/searches" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"from":"DXB","to":"LHR","fromDate":"'"$FROM_DATE"'","adults":1}' | jq -r .searchId) # 2. Read ranked results and take a trip id (empty? rerun: results settle async) TRIP_ID=$(sleep 3; curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/flights/searches/$SEARCH_ID/results" | jq -r '.results[0].tripId') # 3. Open the trip (full itinerary, every fare) and take its Wego fare id # (null? that trip has no Wego fare: try another tripId from step 2) FARE_ID=$(curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/flights/trips/$TRIP_ID?searchId=$SEARCH_ID" \ | jq -r '[.fares[] | select(.kind=="wego")][0].fareId') # 4. Pick a fare option (Saver, Flex and similar). The list is ordered by leg # first, then by price within a leg - so options[0] is the cheapest option of # leg 1, not the cheapest of the fare. # REQUIRED: step 5 rejects a request with no fareOptionId (400 validation_failed). # This route is one-way, so one id is the whole trip. Read the fare's own # top-level `price` for the trip total; never add or min() the options. FARE_OPTION_ID=$(curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/flights/fares/$FARE_ID/options" \ | jq -r '.options[0].fareOptionId') # 5. Get the wego.com URL (the booking-link endpoint) curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/flights/fares/$FARE_ID/booking-link?tripId=$TRIP_ID&fareOptionId=$FARE_OPTION_ID&from=DXB&to=LHR&fromDate=$FROM_DATE" # -> { "bookingUrl": "https://www.wego.com/...", "expires": true } ``` **Adding a return date?** Then step 4 has a case to check that a one-way does not. Each option carries `price.covers`: `"trip"` means that one id prices the whole journey and step 5 takes it alone, exactly as above; `"leg"` means it prices only its own leg. Send a single `"leg"` id and the booking page still presents the round trip while pricing half of it, with nothing in the response to say so. So when the options report `"leg"`, group them by `legId`, take one per entry in `legs[]`, and pass them to step 5 as a comma-separated `fareOptionId` (at most 8, none twice). `covers` is a positive witness: absent means the API could not attribute the option, not that it covers the trip. See [Fares, rates and partners](/api/fares-rates-partners). Do not know the code? `GET /v1/places?query=dubai` resolves free text to typed places with codes – cities, airports, states, districts and hotels, so it is also where a `hotelId` for the hotels funnel comes from. When the response sets `metadata.hasAmbiguity`, ask the traveler which place they meant before searching. ## Hotels Nearly the same shape, with one extra step: a city search holds only a sample of any one hotel's rates, so pricing a hotel's full room list takes a **second** search created with that `hotelId`. Skip it and the rates read answers `409` `rates_require_hotel_search`. ```bash CHECK_IN=$(date -u -v+60d +%F 2>/dev/null || date -u -d '+60 days' +%F) CHECK_OUT=$(date -u -v+63d +%F 2>/dev/null || date -u -d '+63 days' +%F) # 1. Create a city search CITY_SEARCH=$(curl -s -X POST "https://api.wego.com/v1/hotels/searches" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"cityCode":"DXB","checkIn":"'"$CHECK_IN"'","checkOut":"'"$CHECK_OUT"'","adults":2}' | jq -r .searchId) # 2. Read ranked results and take a hotelId # (null? rerun: results settle async. Read the top-level searchComplete flag to # know when to stop - see How search works) HOTEL_ID=$(sleep 4; curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/hotels/searches/$CITY_SEARCH/results" | jq -r '.results[0].hotelId') # 3. REQUIRED: a second search, scoped to that hotel HOTEL_SEARCH=$(curl -s -X POST "https://api.wego.com/v1/hotels/searches" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"hotelId":"'"$HOTEL_ID"'","checkIn":"'"$CHECK_IN"'","checkOut":"'"$CHECK_OUT"'","adults":2}' | jq -r .searchId) # 4. Rooms and rates, cheapest first (a rate's id is `id`, not `rateId`) # (null? rerun: rates land over a few seconds too) RATE_ID=$(sleep 4; curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/hotels/$HOTEL_ID/rates?searchId=$HOTEL_SEARCH" | jq -r '.rates[0].id') # 5. Get the wego.com checkout URL curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.wego.com/v1/hotels/$HOTEL_ID/rates/$RATE_ID/booking-link?searchId=$HOTEL_SEARCH" # -> { "bookingUrl": "https://www.wego.com/hotels/booking/checkout?...", "expires": true } ``` For endpoint details and parameters, see the [API Reference](/api). # Ways to use this API > The Wego plugin for ChatGPT, the Wego connector for Claude, the agent skill, the wego CLI, or direct HTTP. Source: https://docs.wego.com/ways-to-use/ · Markdown: https://docs.wego.com/ways-to-use/index.md There are several ways in, and none of them rules out another. Pick whichever fits how you work. ## In ChatGPT or Claude (nothing to install) Already talking to an assistant? Add Wego to it and ask for a trip in your own words. It searches and compares; you review and book on Wego. - [Wego in ChatGPT](/chatgpt-plugin): add the Wego plugin to your ChatGPT account. - [Wego in Claude](/claude-connector): add the Wego connector from the Claude directory. ## The Wego skill (for AI agents) Paste one line into your agent and it sets itself up: see [Agent setup](/agent-setup) for the line and what it does. The installer brings the CLI and the skill together, and the skill drives every funnel from natural language. The same line is on [the setup page](https://api.wego.com/home), which names that host's own installer rather than this one – either resolves to the same installer. ## Wego CLI (for humans at a terminal) One command per step, JSON on stdout so each step threads into the next. See [Wego CLI](/cli). ## Direct HTTP Call the API yourself. The [API Reference](/api) is written to double as an agent tool contract: every `operationId` is a stable tool name, and every error is machine branchable. Whichever path you take, you first need a token: see [Authentication](/authentication).