Checks one route against your price target each day and emails you once, when the fare first drops below it.
Uses
Gmail
Email me when a flight fare drops below my target
Once a day, your fare provider's search endpoint is asked what one route costs on the date you want to fly, and the cheapest offer that comes back is compared with the price you would actually book at.
You are emailed on the change, not the state. The first day the fare sits at or below your target you get one alert with the price, plus the airline and a booking link where the provider supplies them. While it stays there you hear nothing more, and when it climbs back above your target you get a single note saying the watch is armed again. It knows what it has already told you by searching your own mailbox for the newest alert about this route, so nothing is repeated. If that search fails, nothing is sent rather than risk a duplicate, and once your departure date has passed the checks stop on their own.
You need a fare search endpoint and key from a travel data provider, answering with a list of offers that each carry a price.
fareApiUrl to blank.Set fareApiUrl to blank.fareApiToken to blank.Set fareApiToken to blank.originAirport to blank.Set originAirport to blank.destinationAirport to blank.Set destinationAirport to blank.departureDate to blank.Set departureDate to blank.returnDate to blank.Set returnDate to blank.targetPrice to 0.Set targetPrice to 0.currencyCode to USD.Set currencyCode to USD.alertEmailAddress to blank.Set alertEmailAddress to blank.priceTarget to 0.Set priceTarget to 0.targetPriceErrorIf a step below fails, save the error as targetPriceErrortargetPrice to 2 decimal places and save it as priceTarget.Round targetPrice to 2 decimal places and save it as priceTarget.priceTarget is less than or equal to 0If priceTarget is less than or equal to 0daysUntilDeparture to 0.Set daysUntilDeparture to 0.departureDateErrorIf a step below fails, save the error as departureDateErrordepartureDate and save it as daysUntilDeparture.Count the days from today's date to departureDate and save it as daysUntilDeparture.daysUntilDeparture is less than 0If daysUntilDeparture is less than 0routeLabel to originAirport to destinationAirport.Set routeLabel to originAirport to destinationAirport.watchTag to Fare watch originAirport-destinationAirport departureDate.Set watchTag to Fare watch originAirport-destinationAirport departureDate.underTargetMarker to fare is under your target.Set underTargetMarker to fare is under your target.aboveTargetMarker to fare is back above your target.Set aboveTargetMarker to fare is back above your target.fareSearch to a record of origin, destination, departure date, and currency.Set fareSearch to a record of origin, destination, departure date, and currency.returnLine to blank.Set returnLine to blank.returnDate isn't blankIf returnDate isn't blankfareSearch to a record of origin, destination, departure date, and 2 more.Change fareSearch to a record of origin, destination, departure date, and 2 more.returnLine to Returning: returnDateChange returnLine to Returning: returnDate
.fareResponse to an empty record.Set fareResponse to an empty record.fareApiErrorIf a step below fails, save the error as fareApiErrorfareApiUrl and save it as fareResponse.Fetch fareApiUrl and save it as fareResponse.fareApiError as text.Stop and report a problem: The fare search did not answer, so no fare was checked. Check the address in the 'fareApiUrl' step and the key in the 'fareApiToken' step. The endpoint said: fareApiError as text.offers to fareResponse.body.offers.Set offers to fareResponse.body.offers.offerCount to 0.Set offerCount to 0.fareShapeErrorIf a step below fails, save the error as fareShapeErroroffers and save it as offerCount.Count the items in offers and save it as offerCount.offerCount is 0If offerCount is 0routeLabel on departureDate, so there was nothing to compare with your target.Finish the run: No fares came back for routeLabel on departureDate, so there was nothing to compare with your target.bestPrice to 0.Set bestPrice to 0.bestOffer to an empty record.Set bestOffer to an empty record.pricedOffers to 0.Set pricedOffers to 0.skippedOffers to 0.Set skippedOffers to 0.offer in offers:For each offer in offers:offerPrice to 0.Set offerPrice to 0.offerHasPrice to false.Set offerHasPrice to false.offerPriceErrorIf a step below fails, save the error as offerPriceErroroffer.price to 2 decimal places and save it as offerPrice.Round offer.price to 2 decimal places and save it as offerPrice.offerHasPrice to true.Change offerHasPrice to true.skippedOffers.Add 1 to skippedOffers.offerHasPrice is yesIf offerHasPrice is yespricedOffers is 0 or offerPrice is less than bestPriceIf pricedOffers is 0 or offerPrice is less than bestPricebestPrice to offerPrice.Change bestPrice to offerPrice.bestOffer to offer.Change bestOffer to offer.pricedOffers.Add 1 to pricedOffers.skippedOffers is greater than 0If skippedOffers is greater than 0skippedOffers offer(s) that carried no usable price. Each offer needs a number in 'price'.Show in the output: Skipped skippedOffers offer(s) that carried no usable price. Each offer needs a number in 'price'.pricedOffers is 0If pricedOffers is 0offerCount offer(s) but none of them carried a number in 'price', so no fare could be compared with your target.Stop and report a problem: The fare search returned offerCount offer(s) but none of them carried a number in 'price', so no fare could be compared with your target.airlineLine to blank.Set airlineLine to blank.airlineErrorIf a step below fails, save the error as airlineErrorbestOffer.airline and save it as airlineText.Trim bestOffer.airline and save it as airlineText.airlineText isn't emptyIf airlineText isn't emptyairlineLine to Airline: airlineTextChange airlineLine to Airline: airlineText
.airlineLine to blank.Change airlineLine to blank.bookingLine to blank.Set bookingLine to blank.bookingLinkErrorIf a step below fails, save the error as bookingLinkErrorbestOffer.link and save it as linkText.Trim bestOffer.link and save it as linkText.linkText isn't emptyIf linkText isn't emptybookingLine to Book here: linkTextChange bookingLine to Book here: linkText
.bookingLine to blank.Change bookingLine to blank.@watchTag'. Save it as priorAlerts.List emails matching 'subject:@watchTag'. Save it as priorAlerts.priorMessages to priorAlerts.emails.Set priorMessages to priorAlerts.emails.priorAlertCount to 0.Set priorAlertCount to 0.alertHistoryErrorIf a step below fails, save the error as alertHistoryErrorpriorMessages and save it as priorAlertCount.Count the items in priorMessages and save it as priorAlertCount.newestAlertSubject to blank.Set newestAlertSubject to blank.priorAlertCount is greater than 0If priorAlertCount is greater than 0alertSubjectErrorIf a step below fails, save the error as alertSubjectErrorpriorMessages.subject and save it as newestAlertSubject.Trim the first item of priorMessages.subject and save it as newestAlertSubject.newestAlertSubject to blank.Change newestAlertSubject to blank.newestAlertSubject contains underTargetMarker and save it as alreadyReported.Check whether newestAlertSubject contains underTargetMarker and save it as alreadyReported.fareIsUnderTarget to bestPrice is less than or equal to priceTarget.Set fareIsUnderTarget to bestPrice is less than or equal to priceTarget.fareIsUnderTarget is yes and alreadyReported is noIf fareIsUnderTarget is yes and alreadyReported is noalertEmailAddress.Send an email to alertEmailAddress.
Subject: watchTag - underTargetMarkerA fare on this route has dropped below your target.
Route: routeLabel
Leaving: departureDate
returnLineCheapest fare found: currencyCode bestPrice formatted as a number with thousands separators with 2 decimal places
Your target: currencyCode priceTarget formatted as a n...alertEmailAddress.Send an email to alertEmailAddress.
Subject: watchTag - aboveTargetMarkerThe fare you were told about has gone back above your target.
Route: routeLabel
Leaving: departureDate
returnLineCheapest fare now: currencyCode bestPrice formatted as a number with thousands separators with 2 decimal places
Your target: currencyCode priceTarget formatte...routeLabel on departureDate is currencyCode bestPrice formatted as a number with thousands separators with 2 decimal places against a target of currencyCode priceTarget formatted as a number with thousands separators with 2 decimal places. Nothing has changed since the last email, so none was sent.Show in the output: Cheapest fare for routeLabel on departureDate is currencyCode bestPrice formatted as a number with thousands separators with 2 decimal places against a target of currencyCode priceTarget formatted as a number with thousands separators with 2 decimal places. Nothing has changed since the last email, so none was sent.