Me, thinking about bid prices.
I work in Revenue Management at Air Canada, and I get asked fairly often what that actually means. The honest answer is that it's a mix of economics, statistics, and a bit of psychology, all applied to the problem of filling an airplane as profitably as possible. This page is my attempt to explain the key ideas in plain language, without hiding behind equations. I'll cover how the field evolved, where it stands today, and what I find genuinely interesting about it.
It starts with a fact that sounds obvious but has enormous consequences: when a plane takes off with an empty seat, that seat is gone forever. You can't put it back on the shelf, sell it tomorrow, or recover its value in any form. The plane was flying anyway, with fuel burned, crew paid, and gate fees settled, so that empty seat represents pure lost revenue with essentially zero cost saving attached to it.
That single property, perishability, is what makes airline pricing so consequential and so different from most other industries. A clothing retailer with unsold inventory can discount it next week or next season. A manufacturer with excess stock can warehouse it. An airline can do neither. Every seat on every flight is a separate, non-transferable asset with a hard expiry date, and the clock is always running.
The second thing that makes airlines special is that they can't easily adjust supply when demand changes. The plane has a fixed number of seats. You can't add a row when a flight sells out, and you can't remove one when it doesn't. Every decision about one seat is implicitly a decision about every other seat on the same flight: every low-fare booking made today is a high-fare booking you won't be able to accept tomorrow.
The third ingredient is demand segmentation. The person booking a beach holiday six months out and the consultant booking the morning of their client meeting are buying the exact same physical product, a seat from A to B, but their willingness to pay can differ by a factor of four or five. The holiday traveler is price-sensitive but flexible; the consultant is time-sensitive and price-insensitive. Revenue Management exists to serve both without letting one segment steal the other's price point.
Revenue Management was invented by airlines in the late 1970s, and the trigger was political rather than mathematical. The US Airline Deregulation Act of 1978 ended government control over routes and fares, and almost overnight carriers found themselves competing on price for the first time. The established carriers (American, United, Delta) faced a new wave of low-cost entrants offering dramatically cheaper fares on the same routes.
Their response was to fight back not by matching every low fare, but by offering low fares selectively, only to the customers who were genuinely price-sensitive, while protecting high-fare inventory for those who weren't. American Airlines' DINAMO system, launched in the early 1980s, is generally credited as the first large-scale RM implementation. Bob Crandall, American's CEO at the time, later claimed it added nearly $1.4 billion in annual revenue in its first few years. The era of Revenue Management had begun.
The chapters that follow trace how airlines actually do this, starting with the fare class structure that makes segmentation mechanically possible, through the forecasting and optimization algorithms that act on it, all the way to where the field is heading today. It's a story that starts with a few economists and a deregulation act, and ends somewhere in the vicinity of real-time personalized pricing driven by machine learning. I find the whole arc genuinely fascinating, and I hope some of that comes through.
Before any forecasting or optimization can happen, airlines need a structure to work within. That structure is the fare class, a letter (Y, K, Q, V, and so on) that represents not just a price but a bundle of rules: refundability, changeability, advance purchase requirements, minimum stay conditions. These rules are sometimes called price fences, and understanding why they exist is the key to understanding everything that follows.
In 1977, a year before deregulation, American Airlines introduced the SuperSaver fare on transcontinental routes. It was roughly half the regular coach price, but it came with a catch: you had to book 30 days in advance and stay over a Saturday night. The established carriers were skeptical. People's Express and the other new low-cost entrants offered cheap fares with no strings attached. Why would anyone take the restricted version?
The answer was that the restrictions weren't really about penalizing anyone. They were about sorting. The 30-day advance purchase meant no last-minute business traveler would accidentally slip into the cheap seats. The Saturday night stay meant no consultant flying out Monday and back Thursday could qualify. The restrictions acted as a filter, letting price-sensitive leisure travelers in while keeping the fare out of reach for the passengers American most needed to charge full price. The SuperSaver was a commercial success, and the concept spread across the industry within a few years. Price fencing as a discipline was born.
The economic logic is straightforward. Different passengers have radically different willingness to pay for the same seat. If an airline charged a single price, it would either leave money on the table from high-WTP passengers or exclude too many low-WTP passengers who would have generated positive revenue. The ideal solution is to charge each passenger exactly what they're willing to pay, which is impossible in practice, but price fences are the closest practical approximation.
The key insight is that restrictions don't need to be enforced by a human checking each booking. They work through self-selection: customers voluntarily sort themselves into the fare that matches their situation. A business traveler could technically book the cheapest fare, but the non-refundable advance purchase requirement doesn't fit how they travel, so they don't. The fence does its job without any airline employee making a single judgment call.
| Class | Typical rules | Who buys it | Yield |
|---|---|---|---|
| Y / J | Fully refundable, no advance purchase | Business, corporate | High |
| K / M | Change fee, partial refund | Semi-flexible traveler | Mid |
| Q / V / L | Non-refundable, advance purchase required | Leisure, price-sensitive | Low |
Once fare classes exist, the RM system needs a way to think about how many seats to make available at each price. The classic mental model is the bucket stack. Imagine a flight's 200 seats divided into a set of buckets, from cheapest at the bottom to most expensive at the top. Each bucket holds a certain number of seats available at that fare class.
Early in the booking window, when departure is 90 days away and demand is uncertain, you open the cheap buckets to stimulate early bookings. As the flight fills and departure approaches, you close the cheap buckets one by one, leaving only higher-fare inventory available. By the time a last-minute business traveler searches the day before departure, the only open bucket is the full-fare one. They pay accordingly. The scarcest seats go to the customers who value them most, and the price they pay reflects that scarcity.
This is the core mechanic that all the forecasting and optimization in later chapters is trying to get right: which buckets to open, how many seats to put in each one, and exactly when to close them.
One thing worth clarifying before moving on: booking class (the letter) and cabin class (Economy, Business) are different things. Many booking classes can exist within a single cabin. Two passengers sitting next to each other in Economy can hold Q and Y fares and have paid very different prices under very different conditions. The letter is invisible to the passenger but central to everything the RM system does.
Knowing that you want to fill the cheap buckets first and the expensive ones last is the easy part. The hard part is knowing when to close them. Close too early and you leave cheap seats empty that could have generated some revenue. Close too late and you've sold to leisure travelers the seats you needed for business passengers who were about to book. Getting that timing right requires a forecast, and building good forecasts is most of what RM analysts actually spend their time on.
The naive answer is that we forecast how many passengers will fly. The real answer is more granular: we forecast how many passengers will book, in each fare class, on each day between now and departure. A complete forecast is essentially a matrix of fare class by days-to-departure that tells the system where demand will come from and when. Without this level of detail, you cannot make sensible bucket decisions. "This flight will have 180 passengers" tells you nothing useful. "40 passengers will book into low fare classes 60 to 90 days out, and 30 business travelers will book in the last two weeks" is actionable.
The most fundamental concept in class-based forecasting is the booking curve: a picture of how reservations accumulate over time before departure. Every route has its own characteristic shape. Long-haul leisure routes see a burst of early bookings from holiday planners, then a lull in the middle, then a small spike close to departure. Short-haul business routes are flatter, with most bookings arriving in the final two weeks.
The RM system maintains a historical average curve for each flight and continuously compares where actual bookings sit against it. Ahead of pace means demand is stronger than expected; you can close cheap buckets earlier and wait for higher-yield passengers. Behind pace means demand is weaker; you may need to keep cheap buckets open longer to stimulate sales.
Here is a methodological trap that took the industry years to properly recognize. If Q class was closed at day 45, you never observed the demand that existed at that price after day 45. Your historical data only captures what you actually sold, not what was there. A passenger who searched, found Q unavailable, and left shows up in your data as nothing. Feed that censored history into a forecasting model and you will systematically underestimate low-fare demand, which causes you to close Q even earlier next cycle, which censors even more data, compounding the error indefinitely.
Unconstraining is the statistical process of recovering the demand that was censored. The two most common approaches are Expectation-Maximization (EM) and Kaplan-Meier survival estimation, both borrowed from other statistical disciplines. The intuition in both cases is the same: use the shape of the demand you did observe, combined with what you know about when the fare was closed, to infer the tail you never got to see. It is not glamorous work, but a system that skips it will quietly drift toward under-selling low-fare inventory until the revenue gap becomes impossible to ignore.
Class-based forecasting is backward-looking by design. It assumes future demand will resemble past demand, adjusted for seasonality and known events. That works well in stable markets. It works less well when something genuinely new happens: a competitor enters or exits, a major event drives unusual demand, or an external shock resets every historical baseline at once. No algorithm trained on pre-2020 data was going to handle March 2020 gracefully. The best RM systems blend statistical models with human analyst judgment precisely for these moments.
With a forecast in hand, the system needs to act on it: how many seats should be protected for each fare class? This is the inventory control problem, and the dominant algorithm for solving it on a single flight leg is EMSR-b. The intuition behind it is genuinely beautiful once you see how it builds up from a very simple starting point.
Every seat decision reduces to one question: is the expected value of holding this seat for a future higher-paying passenger greater than the certain value of selling it right now to a lower-paying one? If yes, protect it. If no, sell it. This is an opportunity cost calculation. The cost of selling cheap isn't just the low fare; it's also the revenue you give up by not having that seat available for a higher-fare passenger later.
Without any protection, a flight would fill up in fare order from cheapest to most expensive, because low-fare demand tends to arrive earlier in the booking window. By the time the business traveler tries to book two weeks before departure, the flight is full. The airline sold 150 seats at $200 when it could have sold 120 at $200 and 30 at $600. Protection levels prevent this by explicitly reserving a block of seats that cannot be sold to lower fare classes, regardless of how far in advance they book.
The question is how large that block should be. Too small and high-fare passengers get turned away. Too large and cheap seats sit empty at departure. The answer requires reasoning about probability.
Ken Littlewood solved the two-class version of this problem in a 1972 paper, working at BOAC (British Overseas Airways Corporation). His setup is simple: one flight, two fare classes, high fare \(f_H\) and low fare \(f_L\), with uncertain demand for the high fare. Low-fare passengers book first. The question is how many seats to protect for high-fare passengers.
The logic is marginal. Consider the \(y\)-th seat you are thinking of protecting. If you protect it, you forgo a certain \(f_L\) now, but you gain \(f_H\) with probability \(P(D_H \geq y)\), where \(D_H\) is the random high-fare demand. It is worth protecting that seat as long as the expected gain exceeds the certain loss:
Rearranging gives the threshold directly: protect seats up to \(y^*\) where \(P(D_H \geq y^*) = f_L / f_H\). You keep protecting until the probability that a high-fare passenger fills that seat drops below the fare ratio.
Most flights have more than two fare classes. The obvious approach is to apply Littlewood's Rule repeatedly: compute the protection level for Y against M, then for (Y + M) against Q, and so on. The problem is that applying the rule independently to each class and summing the results overcounts. When fares are close together, you end up protecting too many seats overall, leaving too few available for the lowest classes and generating unnecessary spoilage.
The "b" in EMSR-b refers to the solution Belobaba developed at MIT in the late 1980s: rather than treating each higher class separately, aggregate all classes above the one being displaced into a single composite class, then apply Littlewood's Rule once to that composite. This aggregation step is the key insight.
To find the protection level for all classes above class \(j\) (i.e., how many seats to keep away from class \(j\) and below), you first build a composite of all higher classes \(j{+}1, \ldots, n\). Its fare is a demand-weighted average of those class fares, and its demand distribution is the sum of the individual distributions:
The protection level \(y_j^*\) against class \(j\) is then found by applying Littlewood's Rule to this composite:
EMSR-b runs this calculation for every class boundary on every flight, and re-runs it each time new bookings arrive or the forecast is updated. The protection levels shift continuously as the picture of demand evolves. That continuous re-optimization, across thousands of flights simultaneously, is what a modern RM system does all day.
EMSR-b, as we just saw, produces protection levels: a count of seats to reserve for each class boundary. That is the right answer to the right question, but it is an awkward interface for a real-time booking system. Every incoming request requires checking the fare class, looking up the relevant protection level, counting how many seats have already gone to that class and above, and only then making a decision. It works, but it buries the economic logic under accounting.
There is a cleaner way to express the same thing. At the optimal protection level, EMSR-b stops protecting seats at the exact point where the expected revenue of holding one more seat equals the fare being turned away. That threshold fare has a name: the bid price.
The bid price is the opportunity cost of one seat at a given moment: the minimum a booking must pay to be worth accepting. Any request above it gets accepted; any request below gets declined, even if seats are physically available. The entire protection level system collapses into a single number that the system compares against every incoming fare.
The bid price is not static. Early in the booking window with plenty of capacity, it is low: almost any positive revenue beats an empty seat. As the flight fills and departure approaches, it rises. If the flight is nearly full two days out and the forecast expects a wave of late-booking business travelers, it can be very high. The system is continuously asking: what is this seat worth right now, given everything I know?
This reframing matters beyond elegance. In a class-based system, the accept/reject decision depends on knowing which bucket a booking belongs to and tracking how many seats remain in it. A bid price system does not care about buckets at all. It only needs two inputs: the fare being offered and the current bid price.
This becomes important because real bookings do not always arrive neatly labeled by fare class. Connecting itineraries, corporate negotiated fares, and interline bookings often come with a total price that does not map cleanly to any single class. A bid price system evaluates all of them with the same rule: does this fare cover the cost of the capacity being consumed? That simplicity is also why bid prices become essential once we move from a single leg to a network, which Chapter 06 covers.
Whether the system is class-based or bid-price-based, there is one further question: when multiple fare classes are open simultaneously, do they share the same inventory or each draw from their own fixed allocation?
In a nested system, all classes draw from the same physical pool. A high-fare passenger can always book as long as any seat remains. In a partitioned system, each class gets a fixed bucket and cannot touch another class's allocation. Nesting is almost universally preferred in practice, and the reason is asymmetric regret: turning away a $600 passenger because of an inventory accounting technicality is a far costlier mistake than the reverse. A nested system controlled by a bid price can never make that error. If a seat exists and the fare clears the threshold, the booking goes through.
The previous chapters treated fare classes as the basic unit of demand: forecast how many passengers want Q, how many want M, how many want Y, then protect inventory accordingly. This works well enough in practice, but it rests on a fiction worth naming. Fare classes are a structure airlines impose on the market. They do not exist in any passenger's head.
When a passenger searches for a flight, they do not think "I want the Q fare." They think "I want to fly to London and I'm willing to spend up to around $400." Their willingness to pay is a number, not a category. The Q, M, and Y designations are lines the airline draws to slice a continuous range of values into manageable buckets. Class-free forecasting asks: what if we stopped pretending those lines are real, and modelled the underlying distribution directly?
The most immediate problem with treating buckets as real is buy-down. When a cheap fare and an expensive fare are both open, passengers who would have paid the higher price will naturally take the cheaper one. They are not behaving irrationally — they are simply responding to the options in front of them. The class-based model cannot see this. It records a booking at the low fare, infers that this passenger's willingness to pay was low, and updates its forecast of low-fare demand upward. The higher fare goes under-credited.
This creates a self-reinforcing dynamic. Underestimating high-fare demand leads the system to open cheap classes too early and too widely. More cheap availability means more buy-down. More buy-down means the observed demand at low fares keeps looking strong, while high-fare demand keeps looking weak. Each cycle the model digs itself a little deeper: it is not learning about true demand, it is learning about the consequences of its own pricing decisions, and mistaking them for the underlying market. Left unchecked, this spiral quietly erodes yield over time without any single decision ever looking obviously wrong.
Class-free forecasting breaks this loop by abandoning the bucket as the unit of analysis. Instead of asking how many passengers want Q, it estimates the full distribution of willingness to pay across the passenger population: for any price $p, what fraction of arriving passengers would be willing to pay at least that much? This distribution is estimated from historical transactions, search logs, and rejected offers — every signal that reveals something about what passengers were and were not willing to spend.
Once you have that distribution, the revenue optimization problem changes character entirely. In the class-based world, the question is which pre-filed fare to make available. In the class-free world, the question is what price to set. For any given price $p, expected revenue is $p times the probability that an arriving passenger accepts it. That probability decreases as $p rises, so the revenue function peaks somewhere in between. The optimal price is wherever that peak is, and it can be computed for any remaining capacity and any point in the booking window — no fare filing, no buckets, no protection levels.
In practice, many systems use a hybrid approach sometimes called fare transformation. Rather than replacing the entire class structure overnight, the WTP distribution is used to recompute what the optimal fare level should be at each inventory position and re-map it onto the existing class ladder. The distribution handles the demand modelling; the class structure handles the distribution plumbing. It is not fully class-free, but it captures most of the revenue benefit while remaining compatible with legacy systems that still expect a booking class in every message.
Airlines do not operate isolated flights. They operate networks, and the way those networks are structured has a direct bearing on how RM needs to work.
Consider two carriers at opposite ends of the spectrum. EasyJet is a point-to-point airline: it flies from A to B, the passenger gets off, and that is the end of the transaction. A seat on the London Gatwick to Nice flight is part of exactly one itinerary. The RM problem is entirely self-contained within that leg. This is the world everything in the previous chapters was implicitly describing.
Air France operates very differently. Its network is built around Charles de Gaulle as a hub. A passenger flying from Bordeaux to Tokyo does not take a direct flight — they fly Bordeaux–Paris on a regional feeder, connect at CDG, and continue on the long-haul Paris–Tokyo service. A passenger from Nantes might connect through CDG to Nairobi. One from Toulouse to Montréal. The hub acts as a giant mixing point where short-haul feeders from dozens of French and European cities converge and redistribute onto long-haul routes spanning the globe. This structure lets Air France serve an enormous number of city pairs with a manageable fleet, but it creates a problem that EasyJet never has to think about: a single seat on the CDG–NRT leg could belong to passengers originating in Bordeaux, Lyon, Marseille, Amsterdam, or Madrid, each paying a different total fare for a completely different itinerary.
When you think about it that way, a flight leg is not really a product. It is a piece of infrastructure. The revenue from a seat on that leg depends entirely on which itinerary fills it.
Everything in the previous chapters optimized a single leg at a time. That works reasonably well for point-to-point routes where most passengers are flying from A to B and nothing else. It breaks down on hub-and-spoke networks, and the failure mode is easy to illustrate.
The YUL–FRA leg has one seat left. Two requests arrive: a local passenger paying $420 for Frankfurt only, and a connecting passenger paying $850 in total for Montréal–Frankfurt–Istanbul, with $360 attributed to the first leg. A leg-based system compares $420 against $360 and accepts the local passenger. It just left $430 on the table.
This is not a corner case. It is a systematic bias. Leg-based RM always undervalues connecting passengers because it only sees one component of their fare. The connecting passenger's $850 looks like a $360 booking from the perspective of the first leg and a $490 booking from the perspective of the second. Neither leg sees the full picture, so neither makes the right decision.
The deeper issue is what network RM calls displacement cost. The true cost of accepting any booking is not just the seat it occupies on one leg — it is the opportunity cost of all the capacity it consumes across the network. A connecting itinerary using two congested legs displaces two potential high-value bookings elsewhere. An itinerary on two lightly loaded legs displaces almost nothing. Leg-based RM treats both the same. Network RM does not.
The solution is to evaluate bookings at the itinerary level rather than the leg level. Origin-Destination control does this by assigning a network bid price to each leg: the shadow price of one seat on that leg, derived by solving a linear program across all flights in the network simultaneously. The LP balances supply and demand across the entire schedule and produces a bid price for each leg that reflects how scarce and valuable that capacity is in the context of the whole network.
An itinerary is accepted only if its total revenue exceeds the sum of bid prices for every leg it uses. For the connecting passenger above: if YUL–FRA carries a network bid price of $380 and FRA–IST carries $200, the combined threshold is $580. The $850 fare clears it comfortably. The local passenger's $420 beats the $380 leg bid price on its own. Now the system can compare them properly: the connecting itinerary contributes $850 − $580 = $270 to the network; the local booking contributes $420 − $380 = $40. Take the connecting passenger.
Re-solving the full network LP every time a booking request arrives is computationally expensive — airlines receive millions of requests per day. The widely used practical approximation is virtual nesting. The network LP is solved periodically, perhaps several times a day, to generate bid prices. Those bid prices are then used to pre-rank all possible itineraries by their net contribution to the network. Each itinerary is assigned to a virtual booking class based on that ranking: high-contribution itineraries get a high virtual class, low-contribution ones get a low class.
The inventory system then controls availability using these virtual classes, exactly as it would with ordinary fare classes. When a request arrives, the system looks up its virtual class and checks whether that class is open — no LP required at query time. The network intelligence is baked into the class assignments in advance. It is a pragmatic approximation, and it loses some precision compared to solving the full problem in real time, but it captures most of the network benefit at a fraction of the computational cost.
Classical RM, as described in the previous chapters, is fundamentally a system of availability control. The prices themselves are set by a separate pricing team, filed with distribution systems months in advance, and largely fixed. The RM system's job is to decide which of those pre-set prices to make available at any given moment. It controls the menu, but it did not write it.
Dynamic pricing abandons that constraint. Rather than choosing from a fixed ladder of pre-approved fares, the system computes the optimal price for each seat as conditions evolve. The price itself becomes the decision variable.
The intuition is a direct extension of what came before. In classical RM, the bid price is the opportunity cost of a seat — the minimum a fare must clear to be worth accepting. In a dynamic pricing system, that bid price becomes the posted price, or more precisely, the floor from which it is derived. As each seat sells, the remaining capacity shrinks, scarcity increases, and the bid price rises. The price offered to the next customer is automatically higher than the one offered to the previous customer. Seat by seat, the system walks up the demand curve, extracting more from later buyers who are booking into increasingly scarce inventory.
What this means in practice is that the fare a passenger sees is no longer one of a handful of pre-filed price points. It is the output of a continuous calculation that accounts for remaining capacity, time to departure, current booking pace, and estimated demand. The same flight might be priced at $210 when 90 seats remain and $390 when 12 remain, with no discrete class boundary in between.
The advantage over classical RM is precision. A fare ladder forces the system to round to the nearest available price point. If the revenue-maximizing price right now is $317 but the nearest fare levels are $280 and $350, something is left on the table either way. Dynamic pricing eliminates that rounding across every seat on every flight. The gains per seat are often small, but compounded over a large network they are material.
There is also a structural advantage. Classical pricing and RM are two separate functions that have to be coordinated: one team sets the menu, another decides how to use it, and misalignments between them create revenue leakage that is hard to attribute to either side. Dynamic pricing collapses the two into a single optimization. There is one number to set and one system responsible for setting it.
Continuous dynamic pricing is technically feasible today for airlines with modern distribution infrastructure and direct booking channels. What has slowed its full adoption is partly legacy systems, partly the complexity of coordinating prices across GDS intermediaries that were built for a world of pre-filed fares, and partly the commercial reality that passengers respond to fare predictability. The fare class, for all its inefficiencies, is also a commitment: this seat costs $180 today and tomorrow. Giving that up entirely is a trade-off no major carrier has yet been fully willing to make.
Traditional RM optimizes one thing: the seat. But a seat is not the only thing an airline sells. Bags, seat selection, lounge access, meals, Wi-Fi, partner products — ancillary revenue now represents anywhere from 15% to over 50% of total revenue depending on the carrier. The next frontier of RM is optimizing not just a seat price, but an entire offer.
The airline industry's distribution infrastructure was built in the 1960s around the Passenger Name Record, a structure designed for a world where airlines sold exactly one product (a seat) through a small number of channels. It's remarkably rigid. IATA's New Distribution Capability (NDC) standard is designed to replace this with a flexible API-based architecture capable of representing any product, any bundle, any personalized offer, constructed dynamically rather than pulled from a pre-filed fare catalog.
The unbundling of the airline ticket is one of the defining commercial shifts of the past two decades. It started with low-cost carriers. Ryanair and Spirit realized early that passengers are far more price-sensitive to the headline fare they see in a search result than to the add-ons they buy afterward. By stripping the base fare to its minimum and charging separately for everything else — a checked bag, a seat assignment, priority boarding, a snack — they could advertise a dramatically lower entry price while recovering margin through ancillaries. The result was that passengers who valued those extras paid for them, and passengers who didn't could fly more cheaply than before. Both groups were being served more precisely than a single bundled price ever could.
Legacy carriers initially resisted, then followed. Today virtually every airline charges separately for at least some ancillary products, and the range of what counts as an ancillary has expanded well beyond bags. It now includes seat selection (including extra legroom, bulkhead, and exit row seats), lounge access, fast-track security, onboard Wi-Fi, travel insurance, hotel and car rental cross-sells, and increasingly sophisticated loyalty currency products. Carriers like Ryanair and Spirit derive more than half their revenue per passenger from ancillaries. Even full-service carriers like Air Canada or Lufthansa now see ancillaries accounting for a meaningful and growing share of total revenue.
For RM, this creates both an opportunity and a complication. The opportunity is that there is more total revenue to optimize. The complication is that the products being sold are not all perishable in the same way, do not have the same demand patterns, and do not interact with seat inventory in obvious ways. A bag fee is booked at the time of ticket purchase and generates revenue regardless of when the passenger flies. An exit row seat is as perishable as the seat itself. Lounge access ties to passenger status and route characteristics. Each ancillary product has its own demand curve, its own price sensitivity, and its own relationship to the base fare. Optimizing them together is genuinely harder than optimizing the seat alone.
Once the distribution infrastructure can carry rich, dynamic offers, the RM problem expands considerably. The classic seat allocation question — how many seats to protect at each price — becomes only one part of a larger puzzle. Now the system also has to decide what bundle to present to which customer, at what price, and through which channel.
Attribute-based selling takes this the furthest: rather than choosing between Economy and Business, a customer assembles their own product from a menu of attributes — extra legroom, a checked bag, a flexible ticket, priority boarding, a meal. Two passengers sitting next to each other in the same cabin might have paid for completely different combinations of attributes at completely different total prices. RM's role shifts from protecting blocks of seats to pricing a large combinatorial space of product configurations in real time.
This changes the optimization objective fundamentally. The question is no longer "what is the revenue-maximizing seat fare for this passenger" but "what is the revenue-maximizing offer for this passenger" — a bundle of seat, ancillaries, and services priced as a whole. A passenger on a low base fare who reliably buys a bag, an exit row seat, and lounge access may be worth more to the airline than a higher-fare passenger who buys nothing else. The seat fare alone is a poor proxy for passenger value.
Getting this right requires connecting systems that were historically never designed to talk to each other: the RM platform, the pricing engine, the ancillary merchandising system, the loyalty program, and the customer data infrastructure. Each of those systems was built to optimize its own slice of the problem. Total offer optimization asks them to solve it jointly. That integration challenge is, in most airlines today, still a work in progress — but it is clearly where the commercial logic of the industry is pointing.
Revenue Management has come a long way from EMSR-b running overnight batch jobs in the 1980s. The field is still moving, and a few directions stand out as genuinely important.
Classical RM assumes demand follows a statistical distribution and optimizes within that model. Reinforcement learning takes a different approach: rather than fitting a demand model first and optimizing against it, an RL agent learns a pricing policy directly by interacting with the market — setting prices, observing what happened, and updating its behavior accordingly. What I find appealing about RL is that it naturally captures the sequential nature of RM decisions. The price you set today shapes the demand you observe tomorrow. Classical methods treat each decision largely as independent. RL does not, and that difference matters most in volatile or fast-moving markets where historical patterns break down and any pre-fitted model is already out of date.
Most RM systems today still run on batch cycles: forecasts computed overnight, controls updated a few times a day. The direction of travel is continuous optimization that responds to bookings, competitor moves, and external signals as they happen. Alongside this, the data available to feed those models is improving. Booked fares tell you what passengers paid. Search data tells you what they considered and rejected — a much richer signal for estimating true willingness to pay. As airlines gain more control over their distribution through direct channels and NDC, this data becomes available in ways it historically wasn't, and the forecasting models that can use it become correspondingly sharper.
The methods developed in airline RM have already spread to hotels, car rentals, cruises, and rail. The next wave is likely to reach industries that have historically used static pricing: restaurants, parking, sports venues, healthcare. The core intuitions — perishability, fixed capacity, demand segmentation, opportunity cost — are universal. What changes is the domain.
Anyway, that's the overview. If something here was unclear or you want to go deeper on any chapter, feel free to reach out. I find this stuff genuinely fascinating, and I'm always happy to talk about it.