Flight Search API — FetchLayer Docs
Documentation menu
FetchLayer FetchLayer API

Flight Search API v1

Flight fares and itineraries as JSON or Markdown, from https://api.fetchlayer.dev/flights. Two airport codes and a date is the whole request. Every itinerary comes back priced, with its segments, stop count, layover airports and durations, and every response carries the timestamp the fares were read at.

OpenAPI JSON

One route, three required fields

There is one endpoint, and it takes an origin, a destination and a departure date. Everything else — cabin, nonstop, passenger counts, currency — narrows the search before the fares are read, and is echoed back on the response so a result set is self-describing.

{ "origin": "JFK", "destination": "LHR", "departureDate": "2026-10-15" }

That returned 48 itineraries in 1.34–2.84s across three runs on 17 September 2026, every one priced. A DUB→STN search returned 18 in 1.67–1.86s, also all priced — Ryanair, Ryanair UK and Turkish Airlines side by side, with fares from $22, so low-cost carriers are in scope and you can see it in the results rather than take it on trust.

Every itinerary is priced, and the price says what it covers

"price":     { "amount": 295, "currency": "USD", "formatted": "295" },
"priceType": "one_way",
"airlines":  ["British Airways"]

amount is a number, currency a code, formatted the display string — you need no parsing either way. priceType is "one_way" or "round_trip_total", so an amount is never ambiguous about how many directions it bought. Measured coverage: 48 of 48 priced on JFK→LHR, 18 of 18 on DUB→STN, 18 of 18 on a LAX→NRT round trip for two adults, 44 of 44 on a nonstop business-class search.

Read stops, not segments.length

This is the one thing on this API that will quietly give you wrong answers. A one-stop itinerary can carry a single segments[] entry. The stop count lives on stops, and the connection is described in layovers[]:

"segments": [ { "airline": "Aer Lingus", "departureAirportCode": "JFK", … } ],
"stops": 1,
"layovers": [
  { "airportCode": "DUB", "airportName": "Dublin Airport",
    "city": "Dublin", "durationMinutes": 120 }
],
"durationMinutes": 610

One segment, one stop, a two-hour wait in Dublin — a real JFK→LHR result from 17 September 2026. Computing segments.length - 1 would file it as a nonstop and sell it as one.

durationMinutes is a number, layovers included, so sorting by journey length is arithmetic rather than parsing “10 hr 10 min”. Each segment carries both the airport code and the full airport name, plus separate departureDate and arrivalDate fields — which is how a 4:55 PM departure landing at 8:05 AM is unambiguously an overnight.

A round trip prices both directions, but does not itemize the return

Pass returnDate and tripType becomes "round_trip", priceType becomes "round_trip_total" — and inbound comes back null. Each outbound option is priced against its cheapest matching return, and that return flight is not described.

You pass priceType What the amount buys
no returnDateone_wayThe outbound flight described in outbound
returnDateround_trip_totalBoth directions — the return leg's times are not in the response

So the total is real and comparable; the return itinerary is not there to display. This is stated rather than left to be discovered, because an absent inbound otherwise reads as missing data. Measured on LAX→NRT with a return date and two adults: 18 itineraries, all priced, every inbound null. For a one-way search simply omit returnDate — there is no implicit return window.

No matches is a 200, not an error

A route and filter combination with nothing available answers itineraries: [], itineraryCount: 0 and a note in plain words: “No itineraries were found for these dates and filters.” Measured on a short-haul route with nonstopOnly and cabinClass: "first". There is nothing to retry and nothing to mistake for a failure — an empty list is an answer about the market, not about the service.

Every response also carries scrapedAt, an ISO timestamp for when the fares were read. Fares move constantly; that field is what tells you how old the number in front of you is, rather than when you got round to looking at it.

Filters

cabinClass takes economy, premium_economy, business, first. nonstopOnly drops every itinerary with a stop. adults (1–9), children and infants price the whole party rather than one seat. currency takes a three-letter code and sets every price.currency in the response. All of them come back on the response, so a stored result never depends on remembering the request that produced it.

Available endpoints

Billing

One credit per request, reported on every response as pagesFetched. There is no paging on this route: a search is always a single unit of work, whether it returns 48 itineraries or none. Measured against production on 17 September 2026, three runs each: JFK→LHR in 1.34–2.84s, DUB→STN in 1.67–1.86s, a LAX→NRT round trip in 1.68–2.76s, and a nonstop business-class JFK→LHR search in 1.78s.

Errors

  • 400 — the request is wrong, and the message names the problem: a malformed code ({"error":"origin: must be a 3-letter airport code"}), a departure date gone by ({"error":"departureDate \"2020-01-01\" is in the past"}), or an unknown field — the body is a strict object, so {"error":"Unrecognized key: \"sortBy\""} rather than a silently ignored typo.
  • 401 — missing or invalid key: {"error":"Unauthorized"}.

Worth noting for anything scheduled: a past departureDate is a hard 400, not an empty result. A hardcoded date in a cron job fails loudly the day it goes stale, which is the behaviour you want — compute the date rather than pinning it.