{
  "openapi": "3.1.0",
  "info": {
    "title": "Flight Search API",
    "version": "1.0.0",
    "description": "Search priced flight itineraries for a route and a date. Two three-letter airport codes and a departure date are all that is required, and every itinerary comes back priced as {amount, currency, formatted} with priceType saying what the amount covers. Each leg carries its segments — operating airline, departure and arrival airport codes and full names, times and dates — plus a stop count, layovers with the connecting airport and how long the wait is, and a total duration in minutes. Read stops rather than the length of the segment list: a one-stop itinerary can carry a single segment. Filters are applied before the fares are read and echoed back on the response: cabin class, nonstop only, adults, children, infants and currency. A round trip prices both directions as round_trip_total while the return leg is not itemized, and inbound comes back null. A search that matches nothing is a 200 with an empty itinerary list and a note saying so, not an error. Every response carries scrapedAt, so a caller always knows how old the fares in front of them are."
  },
  "servers": [
    {
      "url": "https://api.fetchlayer.dev/flights"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Send your FetchLayer key as Authorization: Bearer ss-your-key."
      }
    }
  },
  "paths": {
    "/search": {
      "post": {
        "operationId": "search",
        "tags": [
          "Search"
        ],
        "summary": "Search flights by route and date",
        "description": "Search priced flight itineraries for a route and a date. `origin`, `destination` and `departureDate` are the only required fields — two three-letter airport codes and a YYYY-MM-DD date — and every itinerary comes back with its price, its carriers, its segments with full airport names and times, its stop count and its layovers. `price` is `{amount, currency, formatted}` — a number, a currency code and a display string — and `priceType` says what the number covers: `\"one_way\"` or `\"round_trip_total\"`. Pass `currency` to price results in a specific one. Every itinerary measured was priced: 48 of 48 on JFK→LHR, 18 of 18 on DUB→STN, 18 of 18 on a LAX→NRT round trip, 44 of 44 on a nonstop business-class JFK→LHR search. **A round trip prices the whole trip but does not itemize the return.** Pass `returnDate` and `priceType` comes back `\"round_trip_total\"` — the amount covers both directions — while **`inbound` is `null`**. Each outbound option is priced against its cheapest matching return, and that return flight is not described. The total is real; the return leg's times are not in the response. Measured on a LAX→NRT round trip for two adults: 18 itineraries, every one priced, every `inbound` null. For a one-way search, omit `returnDate` — there is no implicit return window. **Read `stops`, not `segments.length`.** A one-stop itinerary can still carry a single `segments[]` entry — measured on a JFK→LHR option that came back with one segment, `stops: 1` and a 120-minute layover in Dublin. `stops` is the authoritative count and `layovers[]` carries the detail as `{airportCode, airportName, city, durationMinutes}`. Deriving the stop count from the segment list will report that itinerary as nonstop. **No matches is a 200, not an error.** A route and filter combination with nothing available returns `itineraries: []`, `itineraryCount: 0` and a note saying so in words — \"No itineraries were found for these dates and filters.\" There is nothing to retry and nothing to mistake for a failure. Measured on a short-haul route with `nonstopOnly` and `cabinClass: \"first\"`. Every response carries `scrapedAt`, an ISO timestamp for when the fares were read. Fares move constantly, so use the response's own timestamp rather than the time you happened to look at it — that is what tells a caller how old the number in front of them is. Coverage spans full-service and low-cost carriers alike, and you can see it in the results rather than take it on trust: one measured DUB→STN search returned Ryanair, Ryanair UK and Turkish Airlines side by side, with fares from $22, and a JFK→LHR search returned 48 priced itineraries in a single call. Filters narrow the search rather than the response: `cabinClass`, `nonstopOnly`, `adults`/`children`/`infants` and `currency` are all applied before the fares are read, and every one of them is echoed back on the response so a result set is self-describing. Billing: 1 credit per request, reported as `pagesFetched`. There is no paging. Measured on 17 September 2026, three runs each: JFK→LHR answered in 1.34-2.84s with 48 itineraries, DUB→STN in 1.67-1.86s with 18, a LAX→NRT round trip for two adults in 1.68-2.76s with 18, and a nonstop business-class JFK→LHR search in 1.78s with 44.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "description": "Departure airport as its three-letter code, e.g. \"JFK\". Anything else is a 400 naming it: `{\"error\":\"origin: must be a 3-letter airport code\"}`."
                  },
                  "destination": {
                    "type": "string",
                    "description": "Arrival airport as its three-letter code, e.g. \"LHR\". Same rule as `origin`."
                  },
                  "departureDate": {
                    "type": "string",
                    "description": "Departure date, YYYY-MM-DD. **Must not be in the past** — a past date is a 400 that names it, `{\"error\":\"departureDate \\\"2020-01-01\\\" is in the past\"}`, so a hardcoded date in a scheduled job will start failing rather than silently returning nothing."
                  },
                  "returnDate": {
                    "type": "string",
                    "description": "Return date, YYYY-MM-DD. Omit for a one-way search — there is no implicit return window. Given, `tripType` becomes `\"round_trip\"`, the price covers both directions as `\"round_trip_total\"`, and `inbound` comes back null."
                  },
                  "adults": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Adult travelers, 1 to 9. Prices are for the whole party, not per person.",
                    "example": "1"
                  },
                  "children": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Children traveling, up to 9.",
                    "example": "0"
                  },
                  "infants": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Infants traveling, up to 9.",
                    "example": "0"
                  },
                  "cabinClass": {
                    "type": "string",
                    "description": "Cabin to search: economy, premium_economy, business, first.",
                    "example": "economy"
                  },
                  "nonstopOnly": {
                    "type": "boolean",
                    "description": "Only include itineraries with no stops. Combined with a narrow cabin on a short route this can legitimately return nothing — still a 200, with a note.",
                    "example": "false"
                  },
                  "currency": {
                    "type": "string",
                    "description": "Currency to price results in, as its three-letter code, e.g. \"USD\". Echoed back on the response, and on every `price.currency`."
                  },
                  "format": {
                    "type": "string",
                    "description": "Envelope format: json or markdown.",
                    "example": "json"
                  }
                },
                "additionalProperties": false,
                "required": [
                  "origin",
                  "destination",
                  "departureDate"
                ]
              },
              "examples": {
                "default": {
                  "value": {
                    "origin": "JFK",
                    "destination": "LHR",
                    "departureDate": "2026-10-15"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transport": {
                      "type": "string",
                      "description": "The route family. \"flight-search\"."
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Caveats and completeness statements you can act on, in plain words. Empty on a search that found itineraries; on one that found none it carries \"No itineraries were found for these dates and filters.\" — which is why an empty result never has to be guessed at."
                    },
                    "pagesFetched": {
                      "type": "number",
                      "description": "What the request is billed at: 1 credit. There is no paging on this route, so a search is always a single unit of work."
                    },
                    "origin": {
                      "type": "string",
                      "description": "The departure airport code, echoed back."
                    },
                    "destination": {
                      "type": "string",
                      "description": "The arrival airport code, echoed back."
                    },
                    "departureDate": {
                      "type": "string",
                      "description": "The outbound date the fares are for."
                    },
                    "returnDate": {
                      "type": "string",
                      "description": "The return date when you gave one, null on a one-way search."
                    },
                    "tripType": {
                      "type": "string",
                      "description": "`\"one_way\"` or `\"round_trip\"` — decided by whether you passed `returnDate`, and stated here so a result set does not depend on remembering the request."
                    },
                    "adults": {
                      "type": "number",
                      "description": "The adult count the prices cover."
                    },
                    "children": {
                      "type": "number",
                      "description": "As `adults`."
                    },
                    "infants": {
                      "type": "number",
                      "description": "As `adults`."
                    },
                    "cabinClass": {
                      "type": "string",
                      "description": "The cabin searched, echoed back."
                    },
                    "nonstopOnly": {
                      "type": "boolean",
                      "description": "Whether the search was restricted to nonstop itineraries."
                    },
                    "currency": {
                      "type": "string",
                      "description": "The currency the prices are in."
                    },
                    "locale": {
                      "type": "string",
                      "description": "The locale the times and names were read in, e.g. \"en\"."
                    },
                    "scrapedAt": {
                      "type": "string",
                      "description": "**An ISO timestamp for when these fares were read.** Fares move; this is what tells you how old the number in front of you is, rather than when you got round to looking at the response."
                    },
                    "itineraryCount": {
                      "type": "number",
                      "description": "How many itineraries came back. 0 is an ordinary answer with a note, not a failure."
                    },
                    "itineraries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "A short id for the itinerary within this response."
                          },
                          "price": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "`{amount, currency, formatted}` — a number, a code and the display string, so no parsing is needed either way."
                          },
                          "priceType": {
                            "type": "string",
                            "description": "**What the price covers**: `\"one_way\"`, or `\"round_trip_total\"` on a search with `returnDate`, where the amount is for both directions."
                          },
                          "airlines": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Every carrier across the itinerary, e.g. `[\"British Airways\"]` or `[\"Ryanair UK\"]`."
                          },
                          "outbound": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "The outbound leg: `{segments, stops, layovers, durationMinutes, airlines}`.",
                            "properties": {
                              "segments": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "additionalProperties": true
                                },
                                "description": "Each flight in the outbound leg, in order: `{airline, departureAirportCode, departureAirportName, arrivalAirportCode, arrivalAirportName, departureTime, arrivalTime, departureDate, arrivalDate}`. Airports come with both the code and the full name, so \"LHR\" never has to be looked up. `departureDate` and `arrivalDate` are separate fields precisely because they differ on an overnight — a 4:55 PM departure landing at 8:05 AM the next day carries both dates."
                              },
                              "stops": {
                                "type": "number",
                                "description": "**The stop count for the outbound leg, and the field to read.** Not `segments.length - 1`: a one-stop itinerary can carry a single segment entry."
                              },
                              "layovers": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "additionalProperties": true
                                },
                                "description": "`[{airportCode, airportName, city, durationMinutes}]` — where the connection is and how long it lasts, so a two-hour Dublin layover and a five-hour one are distinguishable before anyone books either."
                              },
                              "durationMinutes": {
                                "type": "number",
                                "description": "Total elapsed time for the outbound leg in minutes, layovers included. A number, not \"10 hr 10 min\"."
                              },
                              "airlines": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                },
                                "description": "The carriers operating the outbound leg."
                              }
                            }
                          },
                          "inbound": {
                            "type": "string",
                            "description": "**Null on every search measured, including round trips.** On a one-way search there is no return leg to describe; on a round trip the price covers both directions but the return flight is not itemized. Treat the return leg as unavailable rather than as a field that failed to populate."
                          }
                        },
                        "additionalProperties": false
                      }
                    }
                  },
                  "additionalProperties": false
                },
                "examples": {
                  "default": {
                    "value": {
                      "transport": "flight-search",
                      "notes": [],
                      "pagesFetched": 1,
                      "origin": "JFK",
                      "destination": "LHR",
                      "departureDate": "2026-10-15",
                      "returnDate": null,
                      "tripType": "one_way",
                      "adults": 1,
                      "children": 0,
                      "infants": 0,
                      "cabinClass": "economy",
                      "nonstopOnly": false,
                      "currency": "USD",
                      "locale": "en",
                      "scrapedAt": "2026-09-17T11:59:41.161Z",
                      "itineraryCount": 48,
                      "itineraries": [
                        {
                          "id": "ul84o9",
                          "outbound": {
                            "segments": [
                              {
                                "airline": "British Airways",
                                "departureAirportCode": "JFK",
                                "departureAirportName": "John F. Kennedy International Airport",
                                "arrivalAirportCode": "LHR",
                                "arrivalAirportName": "Heathrow Airport",
                                "departureTime": "7:50 AM",
                                "arrivalTime": "7:45 PM",
                                "departureDate": "2026-10-15",
                                "arrivalDate": "2026-10-15"
                              }
                            ],
                            "stops": 0,
                            "layovers": [],
                            "durationMinutes": 415,
                            "airlines": [
                              "British Airways"
                            ]
                          },
                          "inbound": null,
                          "price": {
                            "amount": 295,
                            "currency": "USD",
                            "formatted": "295"
                          },
                          "priceType": "one_way",
                          "airlines": [
                            "British Airways"
                          ]
                        },
                        {
                          "id": "zmpos7",
                          "outbound": {
                            "segments": [
                              {
                                "airline": "Aer Lingus",
                                "departureAirportCode": "JFK",
                                "departureAirportName": "John F. Kennedy International Airport",
                                "arrivalAirportCode": "LHR",
                                "arrivalAirportName": "Heathrow Airport",
                                "departureTime": "4:55 PM",
                                "arrivalTime": "8:05 AM",
                                "departureDate": "2026-10-15",
                                "arrivalDate": "2026-10-16"
                              }
                            ],
                            "stops": 1,
                            "layovers": [
                              {
                                "airportCode": "DUB",
                                "airportName": "Dublin Airport",
                                "city": "Dublin",
                                "durationMinutes": 120
                              }
                            ],
                            "durationMinutes": 610,
                            "airlines": [
                              "Aer Lingus"
                            ]
                          },
                          "inbound": null,
                          "price": {
                            "amount": 295,
                            "currency": "USD",
                            "formatted": "295"
                          },
                          "priceType": "one_way",
                          "airlines": [
                            "Aer Lingus"
                          ]
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid or missing Bearer token"
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    }
  }
}