Back to Knowledge Hub
Network

Flight Price Scraping: Hold Four Inputs

Hold currency, locale, point of sale and device constant so a fare difference is a price difference, then record the four inputs beside every quoted fare.

BotBrowser Team

Documentation

Want the structured docs for Network?

This article lives in the editorial library. For step-by-step setup, reference material, and ongoing updates, jump into the docs section.

Flight price scraping only produces a usable series when the environment behind each request is held constant. A fare is quoted for a currency, a locale, a point of sale and a device, and changing any one of them can change the number without anything about the market changing. BotBrowser documents profile-backed locale, timezone and language inputs with geo-aligned egress, so a capture can declare the environment it represents. It does not supply fares, does not book, does not control a carrier's pricing logic and does not guarantee that a quoted price will be returned again. Where a licensed fare feed or agency API exists, that is the route to use.

Four constructed inputs that decide which fare a request receives: currency, declared locale, point of sale and device, each with what holds it constant, plus a delta classification that separates a comparable price change from an environment change.

TL;DR

  • A fare is quoted for a specific currency, locale, point of sale and device. Hold all four, or the difference you measure is partly an environment difference.
  • Currency is a request or account setting, not a browser profile value. Locale and language are profile-backed. Point of sale is a site parameter that has to agree with the egress region and the account.
  • Record the four inputs beside every quoted fare, not once per run.
  • Classify a delta by which inputs changed. A currency change is not a fare movement, however large the number.
  • Prefer a licensed fare feed or agency API where one exists. A browser run does not create permission to monitor, and it cannot book.

Contents

Four inputs that move a fare

A flight search request carries more than an origin, a destination and a date. It carries a currency, a language preference, a market the itinerary is priced for, and a device class that may select a different offer set. Those four are not interchangeable, and a monitoring series that varies them measures its own configuration as much as it measures fares.

InputWhere it is setWhat it can change
CurrencyRequest parameter or account settingThe quoted amount and how it is rounded
LocaleProfile-backed locale and language listPage language, formatting, sometimes the offer set
Point of saleSite parameter, egress region and account, togetherThe market whose inventory and rules apply
DeviceViewport and profile device valuesLayout, and sometimes a mobile-only offer

schema.org's Offer type gives the vocabulary a useful shape here. It defines price and recommends pairing it with priceCurrency using an ISO 4217 code rather than an ambiguous symbol, and it warns against using separators inside the numeric value. That guidance is about publishing an offer, and it maps directly onto the capture problem: a fare recorded as $412 is not a fact, while 412 USD is a comparable observation.

The second and third inputs are the ones that get conflated. A locale is what the browser declares. A point of sale is what market the site prices for, and it is often derived from a combination of a site parameter, the request's apparent region and the account's own setting. Making the browser declare German does not by itself make the fare a German point-of-sale fare, and exiting from a German address does not by itself set the currency.

Currency deserves a paragraph of its own because it is the input most often left implicit. A site may accept an explicit currency parameter, derive one from the point of sale, or read it from an account preference, and the three paths can disagree. When they do, the page usually shows one currency and the checkout shows another, or the same currency with a different conversion rate applied. Record the currency the page displayed, not the one the request asked for, and record the point of sale beside it, because the pair is what makes the amount reproducible.

Rounding is a smaller but persistent source of noise. A fare converted from one currency to another is rounded at a currency-specific precision, and some currencies round to whole units while others keep two decimals. Two captures of the same underlying fare in the same currency should therefore match exactly; if they differ by a fraction of a unit, the difference usually comes from a conversion rather than from a fare change, and the record should say so rather than treating the fraction as movement.

Taxes and fees belong to the same family of problems. A displayed amount may include them, exclude them, or include a subset that depends on the point of sale, and a change in the included set looks exactly like a price change. Record what the displayed amount includes when the page states it, and keep that field with the amount. A series that switches from a tax-inclusive display to a tax-exclusive one will show a step change that has nothing to do with the fare.

Which of the four a profile actually holds

BotBrowser's timezone, locale and language documentation describes automatic derivation from the proxy exit IP when a proxy is configured, plus manual overrides of timezone, locale and languages, with the manual overrides gated behind an ENT Tier1 license. The GeoIP documentation describes translating the exit address into geographic defaults, and states the accuracy boundary plainly: country-level mapping is generally strong while city-level mapping can resolve to a nearby city or region.

That covers the locale half of the table and part of the point of sale. It does not cover currency, and it does not fully cover point of sale either, because a site may accept a currency parameter independently of the language it serves, a site behaviour rather than a documented browser input. A monitoring design that assumes the profile handles everything will therefore hold two of four inputs and believe it holds four.

InputHeld by the profileNeeds an explicit check
CurrencyNoThe request parameter or account setting
LocaleYes, when configuredThe declared list the page actually reports
Point of salePartlyThe site's own indication of the market
DeviceYes, when configuredThe viewport and the device values the page reports

The practical consequence is that the check has to be per capture, not per configuration. A run configured correctly at the start will drift as soon as a request parameter is edited, an override is added, or a proxy is changed for one region. Recording the four inputs at the moment of the quote is the only way to know which of them applied.

Device class is the input that hides best. A desktop viewport and a mobile viewport can return different offer sets, and some carriers expose a mobile-only fare that simply does not exist on the desktop path. That is not a price change between two observations of the same offer; it is a different offer. Record the viewport alongside the device class so a later reader can tell whether the two captures used the same one, and treat a change in the offer set as a device effect rather than a fare movement.

There is also a time dimension inside the environment itself. A capture taken during a sale window and one taken an hour later can differ because the sale ended, which is a source-side change that the four inputs correctly report as unchanged. That is the right outcome: the record is doing its job by ruling out the capture as the explanation. The mistake to avoid is the reverse, blaming the source for a difference that a currency switch explains.

A last habit is worth stating because it is cheap. Capture the four inputs before the fare page loads rather than after, and keep the pre-load values in the record. A page can change a locale or a currency during its own client-side initialisation, so a value read after the quote may describe the final state rather than the state the request carried. Recording both, when they differ, turns a confusing difference into a documented transition.

Record the four inputs per capture

A capture record for fare monitoring is small. It needs the itinerary, the quoted amount with its currency, the four inputs, and the observation time. Everything else is supporting detail.

FieldContentWhy it is in the record
itinerary_keyRoute, dates and cabin class as requestedDefines what is being compared
amountNumeric value with a decimal pointComparable only with a currency
currencyISO 4217 codePrevents a symbol being read as an amount
accept_languageDeclared language list at capture timeShows which locale applied
point_of_saleMarket the site indicated it priced forSeparates the market from the language
device_classDesktop or mobile, with viewportSeparates layout from fare
observed_atCapture time in UTCOrders observations and exposes schedule changes
alignmentaligned or the named input that differedKeeps an incomparable row out of a series

Two entries in that table are easy to record wrongly. amount should be a number with a decimal point rather than a formatted string, because a string carries the locale's separator convention and becomes ambiguous as soon as the series spans two locales. point_of_sale should be what the site indicated, not what the operator intended: an intent the site did not honour is exactly the case worth catching.

The record builder below compares two captures of the same itinerary and reports which inputs held. The fixture is constructed.

// Constructed fixture: two captures of one itinerary, one input changed.
const captureA = {
  itineraryKey: 'YYZ-LHR 2026-12-10 Y',
  amount: 812.4,
  currency: 'CAD',
  acceptLanguage: 'en-CA,en',
  pointOfSale: 'CA',
  deviceClass: 'desktop',
  observedAt: '2026-11-10T09:00:00Z',
};
const captureB = {
  itineraryKey: 'YYZ-LHR 2026-12-10 Y',
  amount: 512.1,
  currency: 'EUR',
  acceptLanguage: 'en-CA,en',
  pointOfSale: 'CA',
  deviceClass: 'desktop',
  observedAt: '2026-11-10T09:00:30Z',
};

const FIELDS = ['currency', 'acceptLanguage', 'pointOfSale', 'deviceClass'];
const changed = FIELDS.filter(f => captureA[f] !== captureB[f]);
const alignment = changed.length === 0 ? 'aligned' : `input_changed:${changed.join(',')}`;

const lines = [
  `itinerary=${captureA.itineraryKey}`,
  `a=${captureA.amount} ${captureA.currency} b=${captureB.amount} ${captureB.currency}`,
  `currency=${captureA.currency}->${captureB.currency} locale=${captureA.acceptLanguage}`,
  `pos=${captureA.pointOfSale} device=${captureA.deviceClass}`,
  `changed=${changed.join(',') || 'none'} alignment=${alignment}`,
  `observed_at=${captureA.observedAt}->${captureB.observedAt}`,
];
console.log(lines.join('\n'));

The constructed output below is the point of the exercise. The amount fell by roughly 37 percent, and the classification says that the drop is not a fare movement at all.

itinerary=YYZ-LHR 2026-12-10 Y
a=812.4 CAD b=512.1 EUR
currency=CAD->EUR locale=en-CA,en
pos=CA device=desktop
changed=currency alignment=input_changed:currency
observed_at=2026-11-10T09:00:00Z->2026-11-10T09:00:30Z

Read a price delta by which input changed

The size of a difference says nothing about its cause. The useful question is which of the four inputs were held, because that determines what the pair of captures can support.

Inputs heldObservationWhat the delta supports
All four identicalAmount differsA comparable price change in that environment
Currency differs, others heldAmount differsA currency conversion or repricing, not a fare move
Point of sale differs, others heldAmount differsTwo markets observed; neither is the other's baseline
Device class differs, others heldAmount differs or offer set changesA device-dependent offer, not a fare change

The first row is the only one that supports a fare series. The second row is the most common error in practice, because a currency switch changes the number by a large factor and the resulting chart looks like a dramatic movement. The third row is a market comparison that has to be built as two series, not one. The fourth row is where a mobile-only offer can masquerade as a price change.

Two further cautions belong to the reading rather than to the record. First, a quote is not a bookable price: taxes, fees and availability can change between the quote and the checkout page. Second, a repeated capture of an unchanged environment can still differ because the carrier's own inventory or fare rules changed, which is a real observation and should be labelled as a source-side change rather than as a capture error.

The price-monitoring architecture covers scheduling and change detection for a general price series. What travel adds is that the four inputs are more independent of each other than they are in retail, where a single product page usually implies one currency and one locale. A profile-to-route consistency record is the right companion for the egress half of point of sale. A calendar of availability needs a date key rather than a product key, and the availability model sets out how to keep that calendar comparable across captures.

When a licensed fare feed is the answer

Most carriers and large agencies offer a licensed route: a fare distribution feed, an agency API, or an agreed data export. Those routes give the same itineraries with a defined schema, a stated update cadence and a licence that says what the data may be used for. When one exists, a browser run is usually the wrong instrument, because it produces a less stable series with more legal exposure for a source that already offered a supported path.

Where no such route exists, the honest question is what authorizes the observation. A working browser session is not that answer. The applicable carrier or agency terms decide, and they frequently restrict automated access, resale of fare data and the display of cached prices. Record the basis for the run alongside the captures so a later reader inherits the answer rather than the assumption.

schema.org's FlightReservation type is a reminder of the same boundary from the other side. It defines a vocabulary for a reservation, with bookingTime, passenger fields, and reservationFor pointing to the flight. It describes how a reservation may be represented; it says nothing about whether a fare may be monitored, and nothing about what a quoted price will be at checkout.

The vocabulary is still useful for naming the things a capture keeps separate. A reservation is not an offer, and an offer is not a fare rule. When a record mixes the three, a later comparison cannot tell whether the carrier changed a price, the itinerary, or the conditions attached to it. Keep the itinerary key describing what was requested, the amount and currency describing what was quoted, and the four environment inputs describing how the quote was obtained.

FAQ

Is a currency change a price change?

No. It changes the unit of the amount, and usually the rounding. A series that switches currency mid-flight measures the switch, so keep one currency per series or convert with a stated rate and record the conversion.

Can a browser profile hold the point of sale?

Only partly. The profile controls locale, language and, through the proxy, the apparent region. A site's point of sale is often a separate parameter or account setting, so verify what the site indicated rather than what the operator intended.

Does the same itinerary always return the same price?

No. Inventory and fare rules change, and the quote is not the checkout total. A repeated capture in an unchanged environment can still differ, and that difference belongs to the source.

Does BotBrowser supply fares or book flights?

No. It provides documented browser inputs so a capture can declare its environment. It does not supply fare data, does not book, and does not guarantee a quoted price.

Sources

Practical conclusion

Decide which currency, locale, point of sale and device the series represents before the first capture. Verify all four per capture, because a profile holds two of them fully and one only partly, and record them beside the quoted amount and the observation time. Classify every delta by which input changed, and treat a currency switch as an environment change rather than a fare movement. Where a licensed fare feed or agency API exists, use it and keep the browser run out of the design.

#Flight Price Scraping#Fare Monitoring#Browser Automation

This article is part of the guide: Proxy Configuration Overview: A Browser Network Decision Map

Run the same check in BotBrowser

Load a profile, keep the signals you just read about consistent inside the browser, and confirm the result on your own machine before a rollout.