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
curl -s -H "Authorization: Bearer $TOKEN" \
"https://api.wego.com/v1/flights/searches/$SEARCH_ID/results"